diff --git a/on_audio_query/CHANGELOG.md b/on_audio_query/CHANGELOG.md
index a4ff38ff..19d50910 100644
--- a/on_audio_query/CHANGELOG.md
+++ b/on_audio_query/CHANGELOG.md
@@ -1,10 +1,47 @@
+## [[2.7.0](https://github.com/LucJosin/on_audio_query/releases/tag/2.7.0)] - [29.03.2023]
+
+### Features
+
+- **Added** `[LogType]`.
+- **Added** `[LogConfig]`.
+- **Added** `[PermissionController]` **(Native)**
+- **Added** `[PluginProvider]` **(Native)**
+- **Added** `[setLogConfig]` method.
+- **Added** `[checkAndRequest]` method.
+- **Added** `[controller]` to `[QueryArtworkWidget]`.
+- **Added** `[retryRequest]` param to `[checkAndRequest]` and `[permissionsRequest]`.
+
+### Fixes
+
+#### Android
+
+- **Fixed** crash after request permission. - [#68](https://github.com/LucJosin/on_audio_query/issues/68)
+- **Fixed** quality always being sent as `200` using `[queryArtwork]`.
+
+### Changes
+
+- **Updated** example.
+- **Renamed** natives files/folders.
+- **Reduced** the default `artwork` resolution (from 100 to 50).
+- **Updated** `[QueryArtworkWidget]` params.
+- **Updated** quality assert on `[QueryArtworkWidget]`.
+
+### ⚠ Important Changes
+
+- **Updated** application permission check.
+ - If application doesn't have permission to access the library, will throw a PlatformException.
+- **Updated** `quality` param from `[QueryArtworkWidget]`.
+ - This param cannot be defined as null anymore and, by default, will be set to `50`.
+- **Updated** minimum supported **Dart** version.
+ - Increase minimum version from `2.12` to `2.17`.
+
## [2.6.2] - [03.03.2023]
### Fixes
#### Android
-- **[Fixed]** incompatibility with `Android 13`. - [#91](https://github.com/LucJosin/on_audio_query/issues/91) - Thanks [@ruchit-7span](https://github.com/ruchit-7span)
+- **Fixed** incompatibility with `Android 13`. - [#91](https://github.com/LucJosin/on_audio_query/issues/91) - Thanks [@ruchit-7span](https://github.com/ruchit-7span)
## [2.6.1] - [05.17.2022]
@@ -12,21 +49,21 @@
#### Android
-- **[Fixed]** incompatibility with `Flutter 3`. - [#78](https://github.com/LucJosin/on_audio_query/issues/78)
+- **Fixed** incompatibility with `Flutter 3`. - [#78](https://github.com/LucJosin/on_audio_query/issues/78)
## [2.6.0] - [02.01.2022]
### Features
-- **[Added]** `[scanMedia]` method that will scan the given path and update the `[Android]` MediaStore.
+- **Added** `[scanMedia]` method that will scan the given path and update the `[Android]` MediaStore.
### Fixes
-- **[Fixed]** media showing when calling `[querySongs]` even after deleting with `[dart:io]`. - [#67](https://github.com/LucJosin/on_audio_query/issues/67)
+- **Fixed** media showing when calling `[querySongs]` even after deleting with `[dart:io]`. - [#67](https://github.com/LucJosin/on_audio_query/issues/67)
### Changes
-- **[Updated]** some required packages.
+- **Updated** some required packages.
### Documentation
@@ -39,7 +76,7 @@
### Changes
-- **[Updated]** all Github links.
+- **Updated** all Github links.
## [2.5.3] - [11.10.2021]
@@ -47,8 +84,8 @@
#### IOS
-- **[Fixed]** song/artist/album from `Apple Music` returning when 'querying' - [#61](https://github.com/LucJosin/on_audio_query/issues/61)
-- **[Fixed]** wrong `artistId` returning from `[AlbumModel]` - [#60](https://github.com/LucJosin/on_audio_query/issues/60)
+- **Fixed** song/artist/album from `Apple Music` returning when 'querying' - [#61](https://github.com/LucJosin/on_audio_query/issues/61)
+- **Fixed** wrong `artistId` returning from `[AlbumModel]` - [#60](https://github.com/LucJosin/on_audio_query/issues/60)
### Documentation
@@ -60,7 +97,7 @@
#### Android
-- **[Fixed]** wrong value returning from: - [#56](https://github.com/LucJosin/on_audio_query/issues/56)
+- **Fixed** wrong value returning from: - [#56](https://github.com/LucJosin/on_audio_query/issues/56)
- `[is_music]`.
- `[is_alarm]`.
- `[is_notification]`.
@@ -78,11 +115,11 @@
#### Dart
-- **[Fixed]** wrong value returning from `[artistId]` when using `[AlbumModel]`. - [#54](https://github.com/LucJosin/on_audio_query/issues/54)
+- **Fixed** wrong value returning from `[artistId]` when using `[AlbumModel]`. - [#54](https://github.com/LucJosin/on_audio_query/issues/54)
#### Android
-- **[Fixed]** missing songs from `[queryAudiosFrom]` when using `GENRE`. - [#46](https://github.com/LucJosin/on_audio_query/issues/46)
+- **Fixed** missing songs from `[queryAudiosFrom]` when using `GENRE`. - [#46](https://github.com/LucJosin/on_audio_query/issues/46)
### Documentation
@@ -104,14 +141,14 @@
#### Dart
-- **[Added]** `errorBuilder` and `frameBuilder` to `[QueryArtworkWidget]`.
+- **Added** `errorBuilder` and `frameBuilder` to `[QueryArtworkWidget]`.
### Fixes
#### Web
-- **[Fixed]** empty result when using `[querySongs]`.
-- **[Fixed]** error when decoding some images.
+- **Fixed** empty result when using `[querySongs]`.
+- **Fixed** error when decoding some images.
See all development [changes](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/CHANGELOG.md):
@@ -123,23 +160,23 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### All platforms
-- **[Added]** `artwork` to genres. - [#41](https://github.com/LucJosin/on_audio_query/issues/41)
-- **[Added]** `sortType`, `orderType` and `ignoreCase` to `[queryAudiosFrom]`.
+- **Added** `artwork` to genres. - [#41](https://github.com/LucJosin/on_audio_query/issues/41)
+- **Added** `sortType`, `orderType` and `ignoreCase` to `[queryAudiosFrom]`.
#### Android
-- Re-**[Added]** `path` parameter to `[querySongs]`. - [#48](https://github.com/LucJosin/on_audio_query/issues/48)
+- Re-**Added** `path` parameter to `[querySongs]`. - [#48](https://github.com/LucJosin/on_audio_query/issues/48)
#### Web
-- **[Added]** `path` parameter to `[querySongs]`.
+- **Added** `path` parameter to `[querySongs]`.
### Fixes
#### Android
-- **[Fixed]** empty `Uint8List` when using `[queryArtwork]` on Android 7. - [#47](https://github.com/LucJosin/on_audio_query/issues/47)
-- **[Fixed]** null `albumId` when using Android 9 or below. - [#53](https://github.com/LucJosin/on_audio_query/issues/53)
+- **Fixed** empty `Uint8List` when using `[queryArtwork]` on Android 7. - [#47](https://github.com/LucJosin/on_audio_query/issues/47)
+- **Fixed** null `albumId` when using Android 9 or below. - [#53](https://github.com/LucJosin/on_audio_query/issues/53)
### Documentation
@@ -163,7 +200,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### IOS
-- **[Fixed]** no artwork returning from `[queryArtwork]` when using `ArtworkType.ALBUM`. - [#45](https://github.com/LucJosin/on_audio_query/issues/45)
+- **Fixed** no artwork returning from `[queryArtwork]` when using `ArtworkType.ALBUM`. - [#45](https://github.com/LucJosin/on_audio_query/issues/45)
### Documentation
@@ -175,13 +212,13 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Dart
-- **[Fixed]** wrong type of `numOfSongs` from `[SongModel]`. - [#39](https://github.com/LucJosin/on_audio_query/issues/39)
+- **Fixed** wrong type of `numOfSongs` from `[SongModel]`. - [#39](https://github.com/LucJosin/on_audio_query/issues/39)
#### IOS
-- **[Fixed]** wrong filter configuration when using `[queryWithFilters]`.
-- **[Fixed]** crash when using any `'query'` method with a null `sortType`. - [#43](https://github.com/LucJosin/on_audio_query/issues/43)
-- **[Fixed]** error with wrong `[MPMediaQuery]` filter. - [#38](https://github.com/LucJosin/on_audio_query/issues/38)
+- **Fixed** wrong filter configuration when using `[queryWithFilters]`.
+- **Fixed** crash when using any `'query'` method with a null `sortType`. - [#43](https://github.com/LucJosin/on_audio_query/issues/43)
+- **Fixed** error with wrong `[MPMediaQuery]` filter. - [#38](https://github.com/LucJosin/on_audio_query/issues/38)
### Documentation
@@ -193,13 +230,13 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Added]** a better 'search' method to `[queryWithFilters]`, now the query uses 'contains' when 'querying'. - [#35](https://github.com/LucJosin/on_audio_query/issues/35)
+- **Added** a better 'search' method to `[queryWithFilters]`, now the query uses 'contains' when 'querying'. - [#35](https://github.com/LucJosin/on_audio_query/issues/35)
### Fixes
#### IOS
-- **[Fixed]** error with wrong `[MPMediaQuery]` type and wrong value from `[jpegData]`. - [#37](https://github.com/LucJosin/on_audio_query/issues/37)
+- **Fixed** error with wrong `[MPMediaQuery]` type and wrong value from `[jpegData]`. - [#37](https://github.com/LucJosin/on_audio_query/issues/37)
#### Documentation
@@ -215,7 +252,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android/Web
-- **[Added]** `[ignoreCase]` to:
+- **Added** `[ignoreCase]` to:
- `[querySongs]`.
- `[queryAlbums]`.
- `[queryArtists]`.
@@ -226,9 +263,9 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** `error` when trying to build using `Android`. - [#32](https://github.com/LucJosin/on_audio_query/issues/32) & [#33](https://github.com/LucJosin/on_audio_query/issues/33)
-- **[Fixed]** `error` related to android song projection. - [#31](https://github.com/LucJosin/on_audio_query/issues/31)
-- **[Fixed]** `'bug'` when using `SongSortType.TITLE`. This is now a `'feature'` and can be controlled using `[ignoreCase]`. - [#29](https://github.com/LucJosin/on_audio_query/issues/29)
+- **Fixed** `error` when trying to build using `Android`. - [#32](https://github.com/LucJosin/on_audio_query/issues/32) & [#33](https://github.com/LucJosin/on_audio_query/issues/33)
+- **Fixed** `error` related to android song projection. - [#31](https://github.com/LucJosin/on_audio_query/issues/31)
+- **Fixed** `'bug'` when using `SongSortType.TITLE`. This is now a `'feature'` and can be controlled using `[ignoreCase]`. - [#29](https://github.com/LucJosin/on_audio_query/issues/29)
### Documentation
@@ -247,17 +284,17 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android/IOS/Web
-- **[Added]** `[numOfSongs]` to `[PlaylistModel]` and `[GenreModel]`.
-- **[Added]** `Playlist` and `Artist` to `ArtworkType`.
+- **Added** `[numOfSongs]` to `[PlaylistModel]` and `[GenreModel]`.
+- **Added** `Playlist` and `Artist` to `ArtworkType`.
#### Android/IOS
-- **[Added]** `quality` to `queryArtwork`.
+- **Added** `quality` to `queryArtwork`.
#### Android
-- **[Added]** `[isAudioBook]`, `[Genre]` and `[GenreId]` to `[SongModel]`.
-- Re-**[Added]** to `[SongModel]`:
+- **Added** `[isAudioBook]`, `[Genre]` and `[GenreId]` to `[SongModel]`.
+- Re-**Added** to `[SongModel]`:
- `[isAlarm]`.
- `[isMusic]`.
- `[isNotification]`.
@@ -268,9 +305,9 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** wrong value returning from `[id]` when using `[ArtistModel]`.
-- **[Fixed]** wrong value returning from `[id]` when using `[GenreModel]`.
-- **[Fixed]** no value returning from `[queryAudiosFrom]` when using `ARTIST_ID`.
+- **Fixed** wrong value returning from `[id]` when using `[ArtistModel]`.
+- **Fixed** wrong value returning from `[id]` when using `[GenreModel]`.
+- **Fixed** no value returning from `[queryAudiosFrom]` when using `ARTIST_ID`.
### Documentation
@@ -316,7 +353,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### IOS
-- **[Fixed]** wrong value returning from `[permissionsStatus]`. - [#24](https://github.com/LucJosin/on_audio_query/issues/24)
+- **Fixed** wrong value returning from `[permissionsStatus]`. - [#24](https://github.com/LucJosin/on_audio_query/issues/24)
### Documentation
@@ -328,9 +365,9 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** duplicate `media` from `[queryWithFilters]`.
-- **[Fixed]** `crash` when calling `[queryWithFilters]`. - [#23](https://github.com/LucJosin/on_audio_query/issues/23)
-- **[Fixed]** `null` artwork returning from `[queryArtwork]` on Android 11/R. - [#21](https://github.com/LucJosin/on_audio_query/issues/21)
+- **Fixed** duplicate `media` from `[queryWithFilters]`.
+- **Fixed** `crash` when calling `[queryWithFilters]`. - [#23](https://github.com/LucJosin/on_audio_query/issues/23)
+- **Fixed** `null` artwork returning from `[queryArtwork]` on Android 11/R. - [#21](https://github.com/LucJosin/on_audio_query/issues/21)
### Documentation
@@ -343,7 +380,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** error when using `[removeFromPlaylist]`. - [#22](https://github.com/LucJosin/on_audio_query/issues/22)
+- **Fixed** error when using `[removeFromPlaylist]`. - [#22](https://github.com/LucJosin/on_audio_query/issues/22)
### Documentation
@@ -407,7 +444,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** no value returning when using `[permissionsRequest]`.
+- **Fixed** no value returning when using `[permissionsRequest]`.
### Documentation
@@ -431,8 +468,8 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### IOS
-- **[Fixed]** crash when using `[queryArtwork]`.
-- **[Fixed]** wrong `[id]` value returning from `[PlaylistModel]`.
+- **Fixed** crash when using `[queryArtwork]`.
+- **Fixed** wrong `[id]` value returning from `[PlaylistModel]`.
### Documentation
@@ -450,11 +487,11 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** `error` when building to `[Android]`.
+- **Fixed** `error` when building to `[Android]`.
#### IOS
-- **[Fixed]** wrong `[duration]`, `[dateAdded]` and `[bookmark]` values returning from `[SongModel]`.
+- **Fixed** wrong `[duration]`, `[dateAdded]` and `[bookmark]` values returning from `[SongModel]`.
### Documentation
@@ -655,7 +692,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** `[Kotlin]` issue when installing the plugin.
+- **Fixed** `[Kotlin]` issue when installing the plugin.
### Documentation
@@ -673,7 +710,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** `[cursor]` problem when using `[AudiosFromType.GENRE_NAME]` or `[AudiosFromType.GENRE_ID]` on `[queryAudiosFrom]`. - [#16](https://github.com/LucJosin/on_audio_query/issues/16) and [#12](https://github.com/LucJosin/on_audio_query/issues/12)
+- **Fixed** `[cursor]` problem when using `[AudiosFromType.GENRE_NAME]` or `[AudiosFromType.GENRE_ID]` on `[queryAudiosFrom]`. - [#16](https://github.com/LucJosin/on_audio_query/issues/16) and [#12](https://github.com/LucJosin/on_audio_query/issues/12)
### Documentation
@@ -691,7 +728,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- ~~**[Fixed]** `[cursor]` problem when using `[AudiosFromType.GENRE_NAME]` or `[AudiosFromType.GENRE_ID]` on `[queryAudiosFrom]`.~~
+- ~~**Fixed** `[cursor]` problem when using `[AudiosFromType.GENRE_NAME]` or `[AudiosFromType.GENRE_ID]` on `[queryAudiosFrom]`.~~
### Documentation
@@ -709,7 +746,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** `java.lang.Integer cannot be cast to java.lang.Long` from `[queryArtworks]`. - [#11](https://github.com/LucJosin/on_audio_query/issues/11)
+- **Fixed** `java.lang.Integer cannot be cast to java.lang.Long` from `[queryArtworks]`. - [#11](https://github.com/LucJosin/on_audio_query/issues/11)
### Documentation
@@ -742,11 +779,11 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** incompatibility with `[permission_handler]`. - [#3](https://github.com/LucJosin/on_audio_query/issues/3) - Thanks [@mvanbeusekom](https://github.com/mvanbeusekom)
+- **Fixed** incompatibility with `[permission_handler]`. - [#3](https://github.com/LucJosin/on_audio_query/issues/3) - Thanks [@mvanbeusekom](https://github.com/mvanbeusekom)
#### Dart
-- **[Fixed]** wrong name. From `[dataAdded]` to `[dateAdded]`.
+- **Fixed** wrong name. From `[dataAdded]` to `[dateAdded]`.
### Documentation
@@ -781,7 +818,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** Now `[queryArtworks]` will return null. - [#6](https://github.com/LucJosin/on_audio_query/issues/6)
+- **Fixed** Now `[queryArtworks]` will return null. - [#6](https://github.com/LucJosin/on_audio_query/issues/6)
### Documentation
@@ -835,7 +872,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Android
-- **[Fixed]** `[queryArtwork]` returning null album image in Android 11. - [#1](https://github.com/LucJosin/on_audio_query/issues/1)
+- **Fixed** `[queryArtwork]` returning null album image in Android 11. - [#1](https://github.com/LucJosin/on_audio_query/issues/1)
### Documentation
@@ -888,12 +925,12 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
#### Dart
-- **[Fixed]** flutter example.
+- **Fixed** flutter example.
#### Android
-- **[Fixed]** `[audiosFromPlaylist]` [**Now this method is part of queryAudiosFrom**]
-- **[Fixed]** `"count(*)"` error from `[addToPlaylist]`. [**Permission bug on Android 10 still happening**]
+- **Fixed** `[audiosFromPlaylist]` [**Now this method is part of queryAudiosFrom**]
+- **Fixed** `"count(*)"` error from `[addToPlaylist]`. [**Permission bug on Android 10 still happening**]
### Documentation
@@ -1063,6 +1100,9 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
### Changes
- TODO
+### Refactor
+- TODO
+
### ⚠ Important Changes
#### @**Deprecated**
- TODO
@@ -1070,7 +1110,7 @@ See all development [changes](https://github.com/LucJosin/on_audio_query/blob/ma
diff --git a/on_audio_query/README.md b/on_audio_query/README.md
index c5163bef..a98e8dee 100644
--- a/on_audio_query/README.md
+++ b/on_audio_query/README.md
@@ -1,3 +1,5 @@
+
+
# on_audio_query
[![Pub.dev](https://img.shields.io/pub/v/on_audio_query?color=9cf&label=Pub.dev&style=flat-square)](https://pub.dev/packages/on_audio_query)
[![Platforms](https://img.shields.io/badge/Platforms-Android%20%7C%20IOS%20%7C%20Web-9cf?&style=flat-square)]()
@@ -5,28 +7,16 @@
`on_audio_query` is a [Flutter](https://flutter.dev/) Plugin used to query audios/songs 🎶 infos [title, artist, album, etc..] from device storage.
-## Help:
-
**Any problem? [Issues](https://github.com/LucJosin/on_audio_query/issues)**
**Any suggestion? [Pull request](https://github.com/LucJosin/on_audio_query/pulls)**
-### Extensions:
-
-* [on_audio_edit](https://github.com/LucJosin/on_audio_edit) - Used to edit audio metadata.
-* [on_audio_room](https://github.com/LucJosin/on_audio_room) - Used to store audio [Favorites, Most Played, etc..].
-
-### Translations:
-
-NOTE: Feel free to help with readme translations
-
-* [English](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/README.md)
-* [Portuguese](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/README.pt-BR.md)
+
### Topics:
-* [How to Install](#how-to-install)
+* [Installation](#installation)
* [Platforms](#platforms)
-* [How to use](#how-to-use)
+* [Overview](#overview)
* [Examples](#examples)
* [Gif Examples](#gif-examples)
* [License](#license)
@@ -60,30 +50,44 @@ NOTE: Feel free to help with readme translations
**[See all platforms methods support](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/PLATFORMS.md)**
-## How to Install:
+## Installation:
+
Add the following code to your `pubspec.yaml`:
```yaml
dependencies:
- on_audio_query: ^2.6.0
+ on_audio_query: ^2.7.0
```
### Request Permission:
+
#### Android:
-To use this plugin add the following code to your `AndroidManifest.xml`
+To use this plugin add the following code to your [AndroidManifest.xml](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/example/android/app/src/main/AndroidManifest.xml)
```xml
- ...
-
-
+
+
+
+
+
+
+
+
+
```
#### IOS:
-To use this plugin add the following code to your `Info.plist`
-```
+To use this plugin add the following code to your [Info.plist](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/example/ios/Runner/Info.plist)
+```plist
+
+ ...
+
NSAppleMusicUsageDescription
- ..Add a reason..
+ $(PROJECT_NAME) requires access to media library
+
+ ...
+
```
#### Web:
@@ -113,63 +117,16 @@ Since Web Browsers **don't** offer direct access to their user's `file system`,
* Add/Remove/Move specific audios to playlists.
* Specific sort types for all query methods.
-## TODO:
+## Overview:
-* Add better performance for all plugin.
-* Add support to Windows/MacOs/Linux.
-* Option to remove songs.
-* Fix bugs.
-
-## How to use:
-
-```dart
-OnAudioQuery() // The main method to start using the plugin.
-```
All types of methods on this plugin:
-### Query methods
-
-| Methods | Parameters | Return |
-|--------------|-----------------|-----------------|
-| [`querySongs`](#querysongs) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryAlbums`](#queryalbums) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryArtists`](#queryartists) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryPlaylists`](#queryplaylists) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryGenres`](#querygenres) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryAudiosFrom`](#queryaudiosfrom) | `(Type, Where, RequestPermission)` | `List` |
-| [`queryWithFilters`](#querywithfilters) | `(ArgsVal, WithFiltersType, Args, RequestPermission)` | `List` |
-| [`queryArtwork`](#queryArtwork) | `(Id, Type, Format, Size, RequestPermission)` | `Uint8List?` |
-
-### Playlist methods
-
-| Methods | Parameters | Return |
-|--------------|-----------------|-----------------|
-| [`createPlaylist`]() | `(PlaylistName, RequestPermission)` | `bool` |
-| [`removePlaylist`]() | `(PlaylistId, RequestPermission)` | `bool` |
-| [`addToPlaylist`]() | **[BG]**`(PlaylistId, AudioId, RequestPermission)` | `bool` |
-| [`removeFromPlaylist`]() | `(PlaylistId, AudioId, RequestPermission)` | `bool` |
-| [`renamePlaylist`]() | `(PlaylistId, NewName, RequestPermission)` | `bool` |
-| [`moveItemTo`]() | **[NT]**`(PlaylistId, From, To, RequestPermission)` | `bool` |
-
-### Permissions/Device methods
-
-| Methods | Parameters | Return |
-|--------------|-----------------|-----------------|
-| [`permissionsRequest`]() | `(retryRequest)` | `bool` |
-| [`permissionsStatus`]() | | `bool` |
-| [`queryDeviceInfo`]() | | `DeviceModel` |
-
-### Others methods
-| Methods | Parameters | Return |
-|--------------|-----------------|-----------------|
-| [`scanMedia`](#scanmedia) | `(Path)` | `bool` |
-
### Artwork Widget
```dart
Widget someOtherName() async {
return QueryArtworkWidget(
- id: SongId,
+ id: ,
type: ArtworkType.AUDIO,
);
}
@@ -177,72 +134,37 @@ All types of methods on this plugin:
**See more: [QueryArtworkWidget](https://pub.dev/documentation/on_audio_query/latest/on_audio_query/QueryArtworkWidget-class.html)**
-### Abbreviations
-
-**[NT]** -> Need Tests
-**[BG]** -> Bug on Android 10/Q
-
## Examples:
#### OnAudioQuery
-```dart
- final OnAudioQuery _audioQuery = OnAudioQuery();
-```
-#### querySongs
```dart
- someName() async {
- // DEFAULT:
- // SongSortType.TITLE,
- // OrderType.ASC_OR_SMALLER,
- // UriType.EXTERNAL,
- List something = await _audioQuery.querySongs();
- }
+final OnAudioQuery _audioQuery = OnAudioQuery();
```
-#### queryAlbums
-```dart
- someName() async {
- // DEFAULT:
- // AlbumSortType.ALBUM,
- // OrderType.ASC_OR_SMALLER
- List something = await _audioQuery.queryAlbums();
- }
-```
+#### Query methods:
-#### queryArtists
-```dart
- someName() async {
- // DEFAULT:
- // ArtistSortType.ARTIST,
- // OrderType.ASC_OR_SMALLER
- List something = await _audioQuery.queryArtists();
- }
-```
+- queryAudios();
+- queryAlbums();
+- queryArtists();
+- queryPlaylists();
+- queryGenres().
-#### queryPlaylists
```dart
someName() async {
- // DEFAULT:
- // PlaylistSortType.NAME,
- // OrderType.ASC_OR_SMALLER
- List something = await _audioQuery.queryPlaylists();
- }
-```
+ // Query Audios
+ List audios = await _audioQuery.queryAudios();
-#### queryGenres
-```dart
- someName() async {
- // DEFAULT:
- // GenreSortType.NAME,
- // OrderType.ASC_OR_SMALLER
- List something = await _audioQuery.queryGenres();
+ // Query Albums
+ List albums = await _audioQuery.queryAlbums();
}
```
#### scanMedia
+
You'll use this method when updating a media from storage. This method will update the media 'state' and
Android `MediaStore` will be able to know this 'state'.
+
```dart
someName() async {
OnAudioQuery _audioQuery = OnAudioQuery();
@@ -259,12 +181,13 @@ Android `MediaStore` will be able to know this 'state'.
```
#### queryArtwork
+
```dart
someName() async {
// DEFAULT: ArtworkFormat.JPEG, 200 and false
Uint8List something = await _audioQuery.queryArtwork(
- SongId,
- ArtworkType.AUDIO,
+ ,
+ ArtworkType.AUDIO,
...,
);
}
@@ -273,71 +196,6 @@ Android `MediaStore` will be able to know this 'state'.
Or you can use a basic and custom Widget.
**See example [QueryArtworkWidget](#artwork-widget)**
-#### queryAudiosFrom
-You can use this method to 'query' the songs from any section(Album, Artist, Playlist or Genre).
-```dart
- someName() async {
- List something = await _audioQuery.queryAudiosFrom(
- AudiosFromType.ALBUM_ID,
- albumId,
- // You can also define a sortType
- sortType: SongSortType.TITLE, // Default
- orderType: OrderType.ASC_OR_SMALLER, // Default
- );
- }
-```
-
-#### queryWithFilters
-```dart
- someName() async {
- // Here we'll search for a [song](WithFiltersType.AUDIOS) using his
- // [artist](AudiosArgs.ARTIST)
- List something = await _audioQuery.queryWithFilters(
- // The [text] to search
- "Sam Smith",
- // The type of search you want.
- // All types:
- // * WithFiltersType.AUDIOS
- // * WithFiltersType.ALBUMS
- // * WithFiltersType.PLAYLISTS
- // * WithFiltersType.ARTISTS
- // * WithFiltersType.GENRES
- WithFiltersType.AUDIOS,
- // This method has [args] as parameter. With this value you can create
- // a more 'advanced' search.
- args: AudiosArgs.ARTIST,
- );
-
- // Other example:
-
- // Here we'll search for a [song](WithFiltersType.AUDIOS) using his
- // [album](AudiosArgs.ALBUM)
- List something = await _audioQuery.queryWithFilters(
- // The [text] to search
- "In the Lonely Hour",
- // The type of search you want.
- // All types:
- // * WithFiltersType.AUDIOS
- // * WithFiltersType.ALBUMS
- // * WithFiltersType.PLAYLISTS
- // * WithFiltersType.ARTISTS
- // * WithFiltersType.GENRES
- WithFiltersType.AUDIOS,
- // This method has [args] as parameter. With this value you can create
- // a more 'advanced' search.
- args: AudiosArgs.ALBUM,
- );
-
- // After getting the result from [queryWithFilters], convert this list using:
- List convertedList = something.toTypeModel();
-
- // Example:
- List convertedSongs = something.toSongModel();
- }
-```
-
-ArgsTypes: [AudiosArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/AudiosArgs-class.html), [AlbumsArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/AlbumsArgs-class.html), [PlaylistsArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/PlaylistsArgs-class.html), [ArtistsArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/ArtistsArgs-class.html) and [GenresArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/GenresArgs-class.html)
-
## Gif Examples:
| | | | |
|:---:|:---:|:---:|:---:|
diff --git a/on_audio_query/README.pt-BR.md b/on_audio_query/README.pt-BR.md
deleted file mode 100644
index 68feb322..00000000
--- a/on_audio_query/README.pt-BR.md
+++ /dev/null
@@ -1,352 +0,0 @@
-# on_audio_query
-[![Pub.dev](https://img.shields.io/pub/v/on_audio_query?color=9cf&label=Pub.dev&style=flat-square)](https://pub.dev/packages/on_audio_query)
-[![Platforms](https://img.shields.io/badge/Platforms-Android%20%7C%20IOS%20%7C%20Web-9cf?&style=flat-square)]()
-[![Languages](https://img.shields.io/badge/Language-Dart%20%7C%20Kotlin%20%7C%20Swift-9cf?&style=flat-square)]()
-
-`on_audio_query` é um [Flutter](https://flutter.dev/) Plugin usado para adquirir informações de áudios/músicas 🎶 [título, artista, album, etc..] do celular.
-
-## Ajuda:
-
-**Algum problema? [Issues](https://github.com/LucJosin/on_audio_query/issues)**
-**Alguma sugestão? [Pull request](https://github.com/LucJosin/on_audio_query/pulls)**
-
-### Extensões:
-
-* [on_audio_edit](https://github.com/LucJosin/on_audio_edit) - Usado para editar audio metadata.
-* [on_audio_room](https://github.com/LucJosin/on_audio_room) - Usado para guardar audio [Favoritos, Mais tocados, etc..].
-
-### Traduções:
-
-NOTE: Fique à vontade para ajudar nas traduções
-
-* [Inglês](README.md)
-* [Português](README.pt-BR.md)
-
-## Tópicos:
-
-* [Como instalar](#como-instalar)
-* [Plataformas](#platformas)
-* [Como usar](#como-usar)
-* [Exemplos](#exemplos)
-* [Exemplos em Gif](#exemplos-em-gif)
-* [Licença](#licença)
-
-## Platformas:
-
-
-| Methods | Android | IOS | Web |
-|-------|:----------:|:----------:|:----------:|
-| `querySongs` | `✔️` | `✔️` | `✔️` |
-| `queryAlbums` | `✔️` | `✔️` | `✔️` |
-| `queryArtists` | `✔️` | `✔️` | `✔️` |
-| `queryPlaylists` | `✔️` | `✔️` | `❌` |
-| `queryGenres` | `✔️` | `✔️` | `✔️` |
-| `queryAudiosFrom` | `✔️` | `✔️` | `✔️` |
-| `queryWithFilters` | `✔️` | `✔️` | `✔️` |
-| `queryArtwork` | `✔️` | `✔️` | `✔️` |
-| `createPlaylist` | `✔️` | `✔️` | `❌` |
-| `removePlaylist` | `✔️` | `❌` | `❌` |
-| `addToPlaylist` | `✔️` | `✔️` | `❌` |
-| `removeFromPlaylist` | `✔️` | `❌` | `❌` |
-| `renamePlaylist` | `✔️` | `❌` | `❌` |
-| `moveItemTo` | `✔️` | `❌` | `❌` |
-| `permissionsRequest` | `✔️` | `✔️` | `❌` |
-| `permissionsStatus` | `✔️` | `✔️` | `❌` |
-| `queryDeviceInfo` | `✔️` | `✔️` | `✔️` |
-| `scanMedia` | `✔️` | `❌` | `❌` |
-
-✔️ -> Tem suporte
-❌ -> Não tem suporte
-
-**[Veja todos os suportes](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/PLATFORMS.md)**
-
-## Como instalar:
-Adicione o seguinte codigo para seu `pubspec.yaml`:
-```yaml
-dependencies:
- on_audio_query: ^2.6.0
-```
-
-#### Solicitar Permissões:
-#### Android:
-Para usar esse plugin adicione o seguinte código no seu `AndroidManifest.xml`
-```xml
- ...
-
-
-
-
-
-```
-
-#### IOS:
-Para usar esse plugin adicione o seguinte código no seu `Info.plist`
-```plist
- NSAppleMusicUsageDescription
- ..Adicione um motivo..
-```
-
-#### Web:
-Já que os navegadores **não** oferecem acesso direto ao `file system` dos usuários, esse plugin irá usar a pasta `assets` para "pegar" os audios. Então, dependerá totalmente do `desenvolvedor`.
-
-```yaml
- # Você não precisa adicionar todos os audios, apenas defina a pasta.
- assets:
- - assets/
- # Se seus arquivos estão em outra pasta dentro de `assets`:
- - assets/audios/
- # - assets/audios/animals/
- # - assets/audios/animals/cat/
- # ...
-```
-
-## Algumas qualidades:
-
-* Opcional e Interna solicitação de permissão para `LER` e `ESCREVER`.
-* Pega todos os áudios.
-* Pega todos os albums e áudios específicos dos albums.
-* Pega todos os artistas e áudios específicos dos artistas.
-* Pega todas as playlists e áudios específicos das playlists.
-* Pega todos os gêneros e áudios específicos dos gêneros.
-* Pega todos os métodos de query com `keys` específicas [Search/Busca].
-* Pega todos as pastas e áudios específicos das pastas.
-* Criar/Deletar/Renomear playlists.
-* Adicionar/Remover/Mover específicos áudios para playlists.
-* Específicos tipos de classificação para todos os métodos.
-
-## Para fazer:
-
-* Adicionar uma melhor performace para todo o plugin.
-* Adicionar suporte para Windows/MacOs/Linux.
-* Opção para remover músicas.
-* Arrumar erros.
-
-## Como usar:
-
-```dart
-OnAudioQuery() // O comando principal para usar o plugin.
-```
-Todos os tipos de métodos nesse plugin:
-
-### Query methods
-
-| Methods | Parameters | Return |
-|--------------|-----------------|-----------------|
-| [`querySongs`](#querysongs) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryAlbums`](#queryalbums) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryArtists`](#queryartists) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryPlaylists`](#queryplaylists) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryGenres`](#querygenres) | `(SortType, OrderType, UriType, RequestPermission)` | `List` |
-| [`queryAudiosFrom`](#queryaudiosfrom) | `(Type, Where, RequestPermission)` | `List` |
-| [`queryWithFilters`](#queryWithFilters) | `(ArgsVal, WithFiltersType, Args, RequestPermission)` | `List` |
-| [`queryArtwork`](#queryArtwork) | `(Id, Type, Format, Size, RequestPermission)` | `Uint8List?` |
-
-### Playlist methods
-
-| Methods | Parameters | Return |
-|--------------|-----------------|-----------------|
-| [`createPlaylist`]() | `(PlaylistName, RequestPermission)` | `bool` |
-| [`removePlaylist`]() | `(PlaylistId, RequestPermission)` | `bool` |
-| [`addToPlaylist`]() | **[BG]**`(PlaylistId, AudioId, RequestPermission)` | `bool` |
-| [`removeFromPlaylist`]() | `(PlaylistId, AudioId, RequestPermission)` | `bool` |
-| [`renamePlaylist`]() | `(PlaylistId, NewName, RequestPermission)` | `bool` |
-| [`moveItemTo`]() | **[NT]**`(PlaylistId, From, To, RequestPermission)` | `bool` |
-
-### Permissions/Device methods
-
-| Methods | Parameters | Return |
-|--------------|-----------------|-----------------|
-| [`permissionsRequest`]() | `(retryRequest)` | `bool` |
-| [`permissionsStatus`]() | | `bool` |
-| [`queryDeviceInfo`]() | | `DeviceModel` |
-
-### Others methods
-| Methods | Parameters | Return |
-|--------------|-----------------|-----------------|
-| [`scanMedia`](#scanmedia) | `(Path)` | `bool` |
-
-### Artwork Widget
-
-```dart
- Widget someOtherName() async {
- return QueryArtworkWidget(
- id: SongId,
- type: ArtworkType.AUDIO,
- );
- }
-```
-
-**Veja mais: QueryArtworkWidget**
-
-### Abreviações
-
-**[NT]** -> Precisa de testes
-**[BG]** -> Bug no Android 10/Q
-
-## Exemplos:
-
-#### OnAudioQuery
-```dart
- final OnAudioQuery _audioQuery = OnAudioQuery();
-```
-
-#### querySongs
-```dart
- someName() async {
- // DEFAULT:
- // SongSortType.TITLE,
- // OrderType.ASC_OR_SMALLER,
- // UriType.EXTERNAL,
- List something = await _audioQuery.querySongs();
- }
-```
-
-#### queryAlbums
-```dart
- someName() async {
- // DEFAULT:
- // AlbumSortType.ALBUM,
- // OrderType.ASC_OR_SMALLER
- List something = await _audioQuery.queryAlbums();
- }
-```
-
-#### queryArtists
-```dart
- someName() async {
- // DEFAULT:
- // ArtistSortType.ARTIST,
- // OrderType.ASC_OR_SMALLER
- List something = await _audioQuery.queryArtists();
- }
-```
-
-#### queryPlaylists
-```dart
- someName() async {
- // DEFAULT:
- // PlaylistSortType.NAME,
- // OrderType.ASC_OR_SMALLER
- List something = await _audioQuery.queryPlaylists();
- }
-```
-
-#### queryGenres
-```dart
- someName() async {
- // DEFAULT:
- // GenreSortType.NAME,
- // OrderType.ASC_OR_SMALLER
- List something = await _audioQuery.queryGenres();
- }
-```
-
-#### scanMedia
-Você irá usar esse método quando atualizar uma media do armazenamento. Esse método irá atualizar o 'estado' da mídia e
-o Android `MediaStore` irá saber esse 'estado'.
-```dart
- someName() async {
- OnAudioQuery _audioQuery = OnAudioQuery();
- File file = File('path');
- try {
- if (file.existsSync()) {
- file.deleteSync();
- _audioQuery.scanMedia(file.path); // Atualiza o 'caminho' da mídia
- }
- } catch (e) {
- debugPrint('$e');
- }
- }
-```
-
-#### queryArtwork
-```dart
- someName() async {
- // DEFAULT: ArtworkFormat.JPEG, 200 and false
- Uint8List something = await _audioQuery.queryArtwork(
- SongId,
- ArtworkType.AUDIO,
- ...,
- );
- }
-```
-
-Ou você pode usar um Widget básico e customizável.
-**Veja o exemplo [QueryArtworkWidget](#artwork-widget)**
-
-#### queryAudiosFrom
-Você pode usar esse método para 'pegar' as músicas de qualquer seção(Album, Artista, Playlist or Gênero).
-```dart
- someName() async {
- List something = await _audioQuery.queryAudiosFrom(
- AudiosFromType.ALBUM_ID,
- albumId,
- // Você pode também definir um tipo de classificação.
- sortType: SongSortType.TITLE, // Default
- orderType: OrderType.ASC_OR_SMALLER, // Default
- );
- }
-```
-
-#### queryWithFilters
-```dart
- someName() async {
- // Aqui nós iremos pesquisar por uma [música](WithFiltersType.AUDIOS) usando o seu
- // [artista](AudiosArgs.ARTIST)
- List something = await _audioQuery.queryWithFilters(
- // O [texto] para pesquisar
- "Sam Smith",
- // O tipo de pesquisa que você quer.
- // Todos os tipos:
- // * WithFiltersType.AUDIOS
- // * WithFiltersType.ALBUMS
- // * WithFiltersType.PLAYLISTS
- // * WithFiltersType.ARTISTS
- // * WithFiltersType.GENRES
- WithFiltersType.AUDIOS,
- // Este método possui [args] como parâmetro. Com este valor você pode criar
- // uma pesquisa mais 'avançada'.
- args: AudiosArgs.ARTIST,
- );
-
- // Outro exemplo:
-
- // Aqui nós iremos pesquisar por uma [música](WithFiltersType.AUDIOS) usando o seu
- // [album](AudiosArgs.ALBUM)
- List something = await _audioQuery.queryWithFilters(
- // O [texto] para pesquisar
- "In the Lonely Hour",
- // O tipo de pesquisa que você quer.
- // Todos os tipos:
- // * WithFiltersType.AUDIOS
- // * WithFiltersType.ALBUMS
- // * WithFiltersType.PLAYLISTS
- // * WithFiltersType.ARTISTS
- // * WithFiltersType.GENRES
- WithFiltersType.AUDIOS,
- // Este método possui [args] como parâmetro. Com este valor você pode criar
- // uma pesquisa mais 'avançada'.
- args: AudiosArgs.ALBUM,
- );
-
- // Depois de adquirir o resultado do [queryWithFilters], converta para uma lista usando:
- List convertedList = something.toTypeModel();
-
- // Exemplo:
- List convertedSongs = something.toSongModel();
- }
-```
-
-ArgsTypes: [AudiosArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/AudiosArgs-class.html), [AlbumsArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/AlbumsArgs-class.html), [PlaylistsArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/PlaylistsArgs-class.html), [ArtistsArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/ArtistsArgs-class.html) and [GenresArgs](https://pub.dev/documentation/on_audio_query_platform_interface/latest/on_audio_query_helper/GenresArgs-class.html)
-
-## Exemplos em Gif:
-| | | | |
-|:---:|:---:|:---:|:---:|
-| | | | |
-| Músicas | Albums | Playlists | Artistas |
-
-## LICENÇA:
-
-* [LICENSE](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/LICENSE)
-
-> * [Voltar ao Topo](#on_audio_query)
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/OnAudioQueryPlugin.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/OnAudioQueryPlugin.kt
index 4b9f69df..93d4d755 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/OnAudioQueryPlugin.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/OnAudioQueryPlugin.kt
@@ -14,19 +14,12 @@ Copyright: © 2021, Lucas Josino. All rights reserved.
package com.lucasjosino.on_audio_query
-import android.Manifest
-import android.app.Activity
-import android.content.Context
-import android.content.pm.PackageManager
import android.media.MediaScannerConnection
import android.os.Build
-import androidx.annotation.NonNull
-import androidx.annotation.RequiresApi
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import com.lucasjosino.on_audio_query.controller.OnAudioController
-import com.lucasjosino.on_audio_query.utils.queryDeviceInfo
-import com.lucasjosino.on_audio_query.interfaces.OnPermissionManagerInterface
+import com.lucasjosino.on_audio_query.consts.Method
+import com.lucasjosino.on_audio_query.controllers.MethodController
+import com.lucasjosino.on_audio_query.controllers.PermissionController
+import io.flutter.Log
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
@@ -34,184 +27,157 @@ import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
-import io.flutter.plugin.common.PluginRegistry
-/** OnAudioQueryPlugin Central */
-class OnAudioQueryPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
- OnPermissionManagerInterface, PluginRegistry.RequestPermissionsResultListener {
+class OnAudioQueryPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
+
+ init {
+ // Set default logging level
+ Log.setLogLevel(Log.WARN)
+ }
+
+ companion object {
+ // Get the current class name.
+ private const val TAG: String = "OnAudioQueryPlugin"
+
+ // Method channel name.
+ private const val CHANNEL_NAME = "com.lucasjosino.on_audio_query"
+ }
+
+ private var permissionController = PermissionController()
+ private var methodController = MethodController()
+
+ private var binding: ActivityPluginBinding? = null
- // Dart <-> Kotlin communication
- private val channelName = "com.lucasjosino.on_audio_query"
private lateinit var channel: MethodChannel
- // Main parameters
- private var retryRequest: Boolean = false
- private lateinit var pContext: Context
- private lateinit var pActivity: Activity
- private lateinit var pResult: Result
- private lateinit var onAudioController: OnAudioController
-
- //
- private val onPermission = arrayOf(
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- )
-
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- private val tiramisuPermission = arrayOf(
- Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.READ_MEDIA_VIDEO
- )
-
- // This is only important for initialization
- override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
- this.pContext = flutterPluginBinding.applicationContext
- channel = MethodChannel(flutterPluginBinding.binaryMessenger, channelName)
+ // Dart <-> Kotlin communication
+ override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
+ Log.i(TAG, "Attached to engine")
+
+ // Setup the method channel communication.
+ channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME)
channel.setMethodCallHandler(this)
}
// Methods will always follow the same route:
// Receive method -> check permission -> controller -> do what's needed -> return to dart
- override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
- pResult = result; onAudioController = OnAudioController(pContext, call, result)
+ override fun onMethodCall(call: MethodCall, result: Result) {
+ Log.d(TAG, "Started method call (${call.method})")
+
+ // Init the plugin provider with current 'call' and 'result'.
+ PluginProvider.setCurrentMethod(call, result)
// If user deny permission request a pop up will immediately show up
// If [retryRequest] is null, the message will only show when call method again
- retryRequest = call.argument("retryRequest") ?: false
+ val retryRequest = call.argument("retryRequest") ?: false
+ permissionController.retryRequest = retryRequest
- //
+ Log.i(TAG, "Method call: ${call.method}")
when (call.method) {
// Permissions
- "permissionsStatus" -> result.success(onPermissionStatus())
- "permissionsRequest" -> onRequestPermission()
+ Method.PERMISSION_STATUS -> {
+ val hasPermission = permissionController.permissionStatus()
+ result.success(hasPermission)
+ }
+ Method.PERMISSION_REQUEST -> {
+ permissionController.requestPermission()
+ }
// Device information
- "queryDeviceInfo" -> queryDeviceInfo(result)
+ Method.QUERY_DEVICE_INFO -> {
+ result.success(
+ hashMapOf(
+ "device_model" to Build.MODEL,
+ "device_sys_version" to Build.VERSION.SDK_INT,
+ "device_sys_type" to "Android"
+ )
+ )
+ }
// This method will scan the given path to update the 'state'.
// When deleting a file using 'dart:io', call this method to update the file 'state'.
- "scan" -> {
+ Method.SCAN -> {
val sPath: String? = call.argument("path")
+ val context = PluginProvider.context()
// Check if the given file is null or empty.
- if (sPath == null || sPath.isEmpty()) result.success(false)
+ if (sPath == null || sPath.isEmpty()) {
+ Log.w(TAG, "Method 'scan' was called with null or empty 'path'")
+ result.success(false)
+ }
// Scan and return
- MediaScannerConnection.scanFile(pContext, arrayOf(sPath), null) { _, _ ->
+ MediaScannerConnection.scanFile(context, arrayOf(sPath), null) { _, _ ->
+ Log.d(TAG, "Scanned file: $sPath")
result.success(true)
}
}
+ // Logging
+ Method.SET_LOG_CONFIG -> {
+ Log.setLogLevel(call.argument("level")!!)
+ result.success(true)
+ }
+
// All others methods
- else -> onAudioController.onAudioController()
+ else -> {
+ Log.d(TAG, "Checking permissions...")
+
+ val hasPermission = permissionController.permissionStatus()
+ Log.d(TAG, "Application has permissions: $hasPermission")
+
+ if (!hasPermission) {
+ Log.w(TAG, "The application doesn't have access to the library")
+ result.error(
+ "MissingPermissions",
+ "Application doesn't have access to the library",
+ "Call the [permissionsRequest] method or install a external plugin to handle the app permission."
+ )
+ }
+
+ methodController.find()
+ }
}
+
+ Log.d(TAG, "Ended method call (${call.method})\n ")
}
- // This is only important for initialization - Start
- override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
+ override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
+ Log.i(TAG, "Detached from engine")
channel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
- this.pActivity = binding.activity
- binding.addRequestPermissionsResultListener(this)
- }
-
- override fun onDetachedFromActivityForConfigChanges() {}
-
- override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {}
+ Log.i(TAG, "Attached to activity")
- override fun onDetachedFromActivity() {}
- // End
+ // Init plugin provider with 'activity' and 'context'.
+ PluginProvider.set(binding.activity)
- // OnPermissionController
- // TODO Find another solution for Permission Request
-
- //
- private val onRequestCode: Int = 88560
-
- override fun onPermissionStatus(context: Context?): Boolean{
- // After "leaving" this class, context will be null so, we need this context argument to
- // call the [checkSelfPermission].
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
- return tiramisuPermission.all {
- ContextCompat.checkSelfPermission(
- context ?: pContext,
- it
- ) == PackageManager.PERMISSION_GRANTED
- }
- }
- else {
- return onPermission.all {
- ContextCompat.checkSelfPermission(
- context ?: pContext,
- it
- ) == PackageManager.PERMISSION_GRANTED
- }
- }
+ // Add to controller the permission to listen to the request result.
+ this.binding = binding
+ binding.addRequestPermissionsResultListener(permissionController)
}
- override fun onRequestPermission() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
- ActivityCompat.requestPermissions(pActivity, tiramisuPermission, onRequestCode)
- }
- else {
- ActivityCompat.requestPermissions(pActivity, onPermission, onRequestCode)
- }
+ override fun onDetachedFromActivityForConfigChanges() {
+ Log.i(TAG, "Detached from engine (config changes)")
+ onDetachedFromActivity()
}
- // Second requestPermission, this one with the option "Never Ask Again".
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- override fun onRetryRequestPermission() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
- if (ActivityCompat.shouldShowRequestPermissionRationale(pActivity, onPermission[0])
- || ActivityCompat.shouldShowRequestPermissionRationale(pActivity, onPermission[1])
- ) {
- retryRequest = false
- onRequestPermission()
- }
- }
- else {
- if (ActivityCompat.shouldShowRequestPermissionRationale(pActivity, tiramisuPermission[0])
- || ActivityCompat.shouldShowRequestPermissionRationale(pActivity, tiramisuPermission[1])
- || ActivityCompat.shouldShowRequestPermissionRationale(pActivity,tiramisuPermission[2])
- ) {
- retryRequest = false
- onRequestPermission()
- }
- }
+ override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
+ Log.i(TAG, "Reattached to activity (config changes)")
+ onAttachedToActivity(binding)
}
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- override fun onRequestPermissionsResult(
- requestCode: Int,
- permissions: Array,
- grantResults: IntArray
- ): Boolean {
- // When [pResult] is not initialized the permission request did not originate from the
- // [on_audio_query] plugin, so return [false] to indicate the [on_audio_query] plugin is not
- // handling the request result and Android should continue executing other registered handlers.
- if (!this::pResult.isInitialized) return false
-
- // When the incoming request code doesn't match the request codes defined by the on_audio_query
- // plugin return [false] to indicate the [on_audio_query] plugin is not handling the request
- // result and Android should continue executing other registered handlers.
- if (requestCode != onRequestCode) return false
-
- // Check permission
- val isPermissionGranted = (grantResults.isNotEmpty()
- && grantResults[0] == PackageManager.PERMISSION_GRANTED)
-
- // After all checks, we can handle the permission request.
- when {
- isPermissionGranted -> pResult.success(true)
- retryRequest -> onRetryRequestPermission()
- else -> pResult.success(false)
+ // Detach all parameters.
+ override fun onDetachedFromActivity() {
+ Log.i(TAG, "Detached from activity")
+
+ // Remove the permission listener
+ if (binding != null) {
+ binding!!.removeRequestPermissionsResultListener(permissionController)
}
- // Return [true] here to indicate that the [on_audio_query] plugin handled the permission request
- // result and Android should not continue executing other registered handlers.
- return true
+ this.binding = null
+ Log.i(TAG, "Removed all declared methods")
}
}
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/PluginProvider.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/PluginProvider.kt
new file mode 100644
index 00000000..f94a3049
--- /dev/null
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/PluginProvider.kt
@@ -0,0 +1,90 @@
+package com.lucasjosino.on_audio_query
+
+import android.app.Activity
+import android.content.Context
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import java.lang.ref.WeakReference
+
+/**
+ * A singleton used to define all variables/methods that will be used on all plugin.
+ *
+ * The singleton will provider the ability to 'request' required variables/methods on any moment.
+ *
+ * All variables/methods should be defined after plugin initialization (activity/context) and
+ * dart request (call/result).
+ */
+object PluginProvider {
+ private const val ERROR_MESSAGE =
+ "Tried to get one of the methods but the 'PluginProvider' has not initialized"
+
+ private lateinit var context: WeakReference
+
+ private lateinit var activity: WeakReference
+
+ private lateinit var call: WeakReference
+
+ private lateinit var result: WeakReference
+
+ /**
+ * Used to define the current [Activity] and [Context].
+ *
+ * Should be defined once.
+ */
+ fun set(activity: Activity) {
+ this.context = WeakReference(activity.applicationContext)
+ this.activity = WeakReference(activity)
+ }
+
+ /**
+ * Used to define the current dart request.
+ *
+ * Should be defined/redefined on every [MethodChannel.MethodCallHandler.onMethodCall] request.
+ */
+ fun setCurrentMethod(call: MethodCall, result: MethodChannel.Result) {
+ this.call = WeakReference(call)
+ this.result = WeakReference(result)
+ }
+
+ /**
+ * The current plugin 'context'. Defined once.
+ *
+ * @throws UninitializedPluginProviderException
+ * @return [Context]
+ */
+ fun context(): Context {
+ return this.context.get() ?: throw UninitializedPluginProviderException(ERROR_MESSAGE)
+ }
+
+ /**
+ * The current plugin 'activity'. Defined once.
+ *
+ * @throws UninitializedPluginProviderException
+ * @return [Activity]
+ */
+ fun activity(): Activity {
+ return this.activity.get() ?: throw UninitializedPluginProviderException(ERROR_MESSAGE)
+ }
+
+ /**
+ * The current plugin 'call'. Will be replace with newest dart request.
+ *
+ * @throws UninitializedPluginProviderException
+ * @return [MethodCall]
+ */
+ fun call(): MethodCall {
+ return this.call.get() ?: throw UninitializedPluginProviderException(ERROR_MESSAGE)
+ }
+
+ /**
+ * The current plugin 'result'. Will be replace with newest dart request.
+ *
+ * @throws UninitializedPluginProviderException
+ * @return [MethodChannel.Result]
+ */
+ fun result(): MethodChannel.Result {
+ return this.result.get() ?: throw UninitializedPluginProviderException(ERROR_MESSAGE)
+ }
+
+ class UninitializedPluginProviderException(msg: String) : Exception(msg)
+}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/consts/Method.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/consts/Method.kt
new file mode 100644
index 00000000..108cdb39
--- /dev/null
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/consts/Method.kt
@@ -0,0 +1,29 @@
+package com.lucasjosino.on_audio_query.consts
+
+object Method {
+ // General methods
+ const val PERMISSION_STATUS = "permissionsStatus"
+ const val PERMISSION_REQUEST = "permissionsRequest"
+ const val QUERY_DEVICE_INFO = "queryDeviceInfo"
+ const val SCAN = "scan"
+ const val SET_LOG_CONFIG = "setLogConfig"
+
+ // Query methods
+ const val QUERY_AUDIOS = "querySongs"
+ const val QUERY_ALBUMS = "queryAlbums"
+ const val QUERY_ARTISTS = "queryArtists"
+ const val QUERY_GENRES = "queryGenres"
+ const val QUERY_PLAYLISTS = "queryPlaylists"
+ const val QUERY_ARTWORK = "queryArtwork"
+ const val QUERY_AUDIOS_FROM = "queryAudiosFrom"
+ const val QUERY_WITH_FILTERS = "queryWithFilters"
+ const val QUERY_ALL_PATHS = "queryAllPath"
+
+ // Playlist methods
+ const val CREATE_PLAYLIST = "createPlaylist"
+ const val REMOVE_PLAYLIST = "removePlaylist"
+ const val ADD_TO_PLAYLIST = "addToPlaylist"
+ const val REMOVE_FROM_PLAYLIST = "removeFromPlaylist"
+ const val RENAME_PLAYLIST = "renamePlaylist"
+ const val MOVE_ITEM_TO = "moveItemTo"
+}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controller/OnAudioController.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controller/OnAudioController.kt
deleted file mode 100644
index 1b3ba22b..00000000
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controller/OnAudioController.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.lucasjosino.on_audio_query.controller
-
-import android.content.Context
-import android.util.Log
-import com.lucasjosino.on_audio_query.query.*
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
-
-class OnAudioController(
- private val context: Context,
- private val call: MethodCall,
- private val result: MethodChannel.Result
-) {
-
- //
- fun onAudioController() {
- when (call.method) {
- //Query methods
- "querySongs" -> OnAudiosQuery().querySongs(context, result, call)
- "queryAlbums" -> OnAlbumsQuery().queryAlbums(context, result, call)
- "queryArtists" -> OnArtistsQuery().queryArtists(context, result, call)
- "queryPlaylists" -> OnPlaylistQuery().queryPlaylists(context, result, call)
- "queryGenres" -> OnGenresQuery().queryGenres(context, result, call)
- "queryArtwork" -> OnArtworksQuery().queryArtwork(context, result, call)
- "queryAudiosFrom" -> OnAudiosFromQuery().querySongsFrom(context, result, call)
- "queryWithFilters" -> OnWithFiltersQuery().queryWithFilters(context, result, call)
- "queryAllPath" -> OnAllPathQuery().queryAllPath(context, result)
- //Playlists methods
- "createPlaylist" -> OnPlaylistsController().createPlaylist(context, result, call)
- "removePlaylist" -> OnPlaylistsController().removePlaylist(context, result, call)
- "addToPlaylist" -> OnPlaylistsController().addToPlaylist(context, result, call)
- "removeFromPlaylist" -> OnPlaylistsController().removeFromPlaylist(
- context,
- result,
- call
- )
- "renamePlaylist" -> OnPlaylistsController().renamePlaylist(context, result, call)
- "moveItemTo" -> OnPlaylistsController().moveItemTo(context, result, call)
- }
- }
-}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controllers/MethodController.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controllers/MethodController.kt
new file mode 100644
index 00000000..608d8629
--- /dev/null
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controllers/MethodController.kt
@@ -0,0 +1,32 @@
+package com.lucasjosino.on_audio_query.controllers
+
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.consts.Method
+import com.lucasjosino.on_audio_query.queries.*
+
+class MethodController() {
+
+ //
+ fun find() {
+ when (PluginProvider.call().method) {
+ //Query methods
+ Method.QUERY_AUDIOS -> AudioQuery().querySongs()
+ Method.QUERY_ALBUMS -> AlbumQuery().queryAlbums()
+ Method.QUERY_ARTISTS -> ArtistQuery().queryArtists()
+ Method.QUERY_PLAYLISTS -> PlaylistQuery().queryPlaylists()
+ Method.QUERY_GENRES -> GenreQuery().queryGenres()
+ Method.QUERY_ARTWORK -> ArtworkQuery().queryArtwork()
+ Method.QUERY_AUDIOS_FROM -> AudioFromQuery().querySongsFrom()
+ Method.QUERY_WITH_FILTERS -> WithFiltersQuery().queryWithFilters()
+ Method.QUERY_ALL_PATHS -> AllPathQuery().queryAllPath()
+ //Playlists methods
+ Method.CREATE_PLAYLIST -> PlaylistController().createPlaylist()
+ Method.REMOVE_PLAYLIST -> PlaylistController().removePlaylist()
+ Method.ADD_TO_PLAYLIST -> PlaylistController().addToPlaylist()
+ Method.REMOVE_FROM_PLAYLIST -> PlaylistController().removeFromPlaylist()
+ Method.RENAME_PLAYLIST -> PlaylistController().renamePlaylist()
+ Method.MOVE_ITEM_TO -> PlaylistController().moveItemTo()
+ else -> PluginProvider.result().notImplemented()
+ }
+ }
+}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controllers/PermissionController.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controllers/PermissionController.kt
new file mode 100644
index 00000000..5080b474
--- /dev/null
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controllers/PermissionController.kt
@@ -0,0 +1,93 @@
+package com.lucasjosino.on_audio_query.controllers
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.os.Build
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.interfaces.PermissionManagerInterface
+import io.flutter.Log
+import io.flutter.plugin.common.PluginRegistry
+
+class PermissionController : PermissionManagerInterface,
+ PluginRegistry.RequestPermissionsResultListener {
+
+ companion object {
+ private const val TAG: String = "PermissionController"
+
+ private const val REQUEST_CODE: Int = 88560
+ }
+
+ var retryRequest: Boolean = false
+
+ private var permissions: Array =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ arrayOf(
+ Manifest.permission.READ_MEDIA_AUDIO,
+ Manifest.permission.READ_MEDIA_IMAGES
+ )
+ } else {
+ arrayOf(
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE
+ )
+ }
+
+ override fun permissionStatus(): Boolean = permissions.all {
+ // After "leaving" this class, context will be null so, we need this context argument to
+ // call the [checkSelfPermission].
+ return ContextCompat.checkSelfPermission(
+ PluginProvider.context(),
+ it
+ ) == PackageManager.PERMISSION_GRANTED
+ }
+
+ override fun requestPermission() {
+ Log.d(TAG, "Requesting permissions.")
+ Log.d(TAG, "SDK: ${Build.VERSION.SDK_INT}, Should retry request: $retryRequest")
+ val activity = PluginProvider.activity()
+ ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE)
+ }
+
+ // Second requestPermission, this one with the option "Never Ask Again".
+ override fun retryRequestPermission() {
+ val activity = PluginProvider.activity()
+ if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permissions[0])
+ || ActivityCompat.shouldShowRequestPermissionRationale(activity, permissions[1])
+ ) {
+ Log.d(TAG, "Retrying permission request")
+ retryRequest = false
+ requestPermission()
+ }
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ): Boolean {
+ // When the incoming request code doesn't match the request codes defined by the on_audio_query
+ // plugin return [false] to indicate the [on_audio_query] plugin is not handling the request
+ // result and Android should continue executing other registered handlers.
+ if (REQUEST_CODE != requestCode) return false
+
+ // Check permission
+ val isPermissionGranted = (grantResults.isNotEmpty()
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED)
+
+ Log.d(TAG, "Permission accepted: $isPermissionGranted")
+
+ // After all checks, we can handle the permission request.
+ val result = PluginProvider.result()
+ when {
+ isPermissionGranted -> result.success(true)
+ retryRequest -> retryRequestPermission()
+ else -> result.success(false)
+ }
+
+ // Return [true] here to indicate that the [on_audio_query] plugin handled the permission request
+ // result and Android should not continue executing other registered handlers.
+ return true
+ }
+}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controller/OnPlaylistsController.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controllers/PlaylistController.kt
similarity index 88%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controller/OnPlaylistsController.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controllers/PlaylistController.kt
index ba105892..1bf99581 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controller/OnPlaylistsController.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/controllers/PlaylistController.kt
@@ -1,17 +1,15 @@
-package com.lucasjosino.on_audio_query.controller
+package com.lucasjosino.on_audio_query.controllers
import android.content.ContentResolver
import android.content.ContentUris
import android.content.ContentValues
-import android.content.Context
import android.os.Build
import android.provider.MediaStore
import android.util.Log
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
+import com.lucasjosino.on_audio_query.PluginProvider
/** OnPlaylistsController */
-class OnPlaylistsController {
+class PlaylistController {
//Main parameters
private val uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
@@ -24,8 +22,12 @@ class OnPlaylistsController {
"count(*)"
)
+ private val context = PluginProvider.context()
+ private val result = PluginProvider.result()
+ private val call = PluginProvider.call()
+
//
- fun createPlaylist(context: Context, result: MethodChannel.Result, call: MethodCall) {
+ fun createPlaylist() {
this.resolver = context.contentResolver
val playlistName = call.argument("playlistName")!!
@@ -37,7 +39,7 @@ class OnPlaylistsController {
}
//
- fun removePlaylist(context: Context, result: MethodChannel.Result, call: MethodCall) {
+ fun removePlaylist() {
this.resolver = context.contentResolver
val playlistId = call.argument("playlistId")!!
@@ -52,7 +54,7 @@ class OnPlaylistsController {
//TODO Add option to use a list
//TODO Fix error on Android 10
- fun addToPlaylist(context: Context, result: MethodChannel.Result, call: MethodCall) {
+ fun addToPlaylist() {
this.resolver = context.contentResolver
val playlistId = call.argument("playlistId")!!
val audioId = call.argument("audioId")!!
@@ -84,7 +86,7 @@ class OnPlaylistsController {
}
//TODO Add option to use a list
- fun removeFromPlaylist(context: Context, result: MethodChannel.Result, call: MethodCall) {
+ fun removeFromPlaylist() {
this.resolver = context.contentResolver
val playlistId = call.argument("playlistId")!!
val audioId = call.argument("audioId")!!
@@ -108,7 +110,7 @@ class OnPlaylistsController {
}
//TODO("Need tests")
- fun moveItemTo(context: Context, result: MethodChannel.Result, call: MethodCall) {
+ fun moveItemTo() {
this.resolver = context.contentResolver
val playlistId = call.argument("playlistId")!!
val from = call.argument("from")!!
@@ -123,7 +125,7 @@ class OnPlaylistsController {
}
//
- fun renamePlaylist(context: Context, result: MethodChannel.Result, call: MethodCall) {
+ fun renamePlaylist() {
this.resolver = context.contentResolver
val playlistId = call.argument("playlistId")!!
val newPlaylistName = call.argument("newPlName")!!
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/interfaces/OnPermissionManagerInterface.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/interfaces/OnPermissionManagerInterface.kt
deleted file mode 100644
index e581f7d0..00000000
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/interfaces/OnPermissionManagerInterface.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.lucasjosino.on_audio_query.interfaces
-
-import android.content.Context
-
-/** OnPermissionManagerInterface */
-interface OnPermissionManagerInterface {
- fun onPermissionStatus(context: Context? = null) : Boolean
- fun onRequestPermission()
- fun onRetryRequestPermission()
-}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/interfaces/PermissionManagerInterface.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/interfaces/PermissionManagerInterface.kt
new file mode 100644
index 00000000..b0bdab9a
--- /dev/null
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/interfaces/PermissionManagerInterface.kt
@@ -0,0 +1,7 @@
+package com.lucasjosino.on_audio_query.interfaces
+
+interface PermissionManagerInterface {
+ fun permissionStatus() : Boolean
+ fun requestPermission()
+ fun retryRequestPermission()
+}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAlbumsQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AlbumQuery.kt
similarity index 52%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAlbumsQuery.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AlbumQuery.kt
index 689ead37..dea02450 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAlbumsQuery.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AlbumQuery.kt
@@ -1,44 +1,39 @@
-package com.lucasjosino.on_audio_query.query
+package com.lucasjosino.on_audio_query.queries
import android.content.ContentResolver
-import android.content.Context
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.lucasjosino.on_audio_query.OnAudioQueryPlugin
-import com.lucasjosino.on_audio_query.query.helper.OnAudioHelper
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.queries.helper.QueryHelper
import com.lucasjosino.on_audio_query.types.checkAlbumsUriType
import com.lucasjosino.on_audio_query.types.sorttypes.checkAlbumSortType
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
+import io.flutter.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/** OnAlbumsQuery */
-class OnAlbumsQuery : ViewModel() {
+class AlbumQuery : ViewModel() {
+
+ companion object {
+ private const val TAG = "OnAlbumsQuery"
+ }
// Main parameters.
- private val helper = OnAudioHelper()
+ private val helper = QueryHelper()
- // None of this methods can be null.
private lateinit var uri: Uri
private lateinit var sortType: String
private lateinit var resolver: ContentResolver
/**
* Method to "query" all albums.
- *
- * Parameters:
- * * [context]
- * * [result]
- * * [call]
*/
- fun queryAlbums(
- context: Context,
- result: MethodChannel.Result,
- call: MethodCall
- ) {
+ fun queryAlbums() {
+ val call = PluginProvider.call()
+ val result = PluginProvider.result()
+ val context = PluginProvider.context()
this.resolver = context.contentResolver
// Sort: Type and Order.
@@ -47,69 +42,51 @@ class OnAlbumsQuery : ViewModel() {
call.argument("orderType")!!,
call.argument("ignoreCase")!!
)
+
// Check uri:
- // * [0]: External.
- // * [1]: Internal.
+ // * 0 -> External
+ // * 1 -> Internal
uri = checkAlbumsUriType(call.argument("uri")!!)
+ Log.d(TAG, "Query config: ")
+ Log.d(TAG, "\tsortType: $sortType")
+ Log.d(TAG, "\turi: $uri")
+
// Query everything in background for a better performance.
viewModelScope.launch {
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty list.
- var resultAlbumList = ArrayList>()
-
- // We cannot "query" without permission so, just return a empty list.
- if (hasPermission) {
- // Start querying
- resultAlbumList = loadAlbums()
- }
-
- // Flutter UI will start, but, information still loading.
- result.success(resultAlbumList)
+ val queryResult = loadAlbums()
+ result.success(queryResult)
}
}
// Loading in Background
private suspend fun loadAlbums(): ArrayList> =
withContext(Dispatchers.IO) {
- // Setup the cursor with [uri], [projection](null == all items) and [sortType].
+ // Setup the cursor with 'uri', 'projection'(null == all items) and 'sortType'.
val cursor = resolver.query(uri, null, null, null, sortType)
- // Empty list.
+
val albumList: ArrayList> = ArrayList()
+ Log.d(TAG, "Cursor count: ${cursor?.count}")
+
// For each item(album) inside this "cursor", take one and "format"
- // into a [Map].
+ // into a 'Map'.
while (cursor != null && cursor.moveToNext()) {
val tempData: MutableMap = HashMap()
+
for (albumMedia in cursor.columnNames) {
tempData[albumMedia] = helper.loadAlbumItem(albumMedia, cursor)
}
- // In Android 10 and above [album_art] will return null, to avoid problem,
- // we remove it. Use [queryArtwork] instead.
+
+ // Android 10 and above 'album_art' will return null. Use 'queryArtwork' instead.
val art = tempData["album_art"].toString()
if (art.isEmpty()) tempData.remove("album_art")
+
albumList.add(tempData)
}
// Close cursor to avoid memory leaks.
cursor?.close()
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
return@withContext albumList
}
}
-
-//I/AlbumCursor: [
-// numsongs,
-// artist,
-// numsongs_by_artist,
-// _id,
-// album,
-// album_art,
-// album_key,
-// artist_id,
-// maxyear,
-// minyear,
-// album_id,
-// ]
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AllPathQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AllPathQuery.kt
new file mode 100644
index 00000000..aa8c8e0f
--- /dev/null
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AllPathQuery.kt
@@ -0,0 +1,60 @@
+package com.lucasjosino.on_audio_query.queries
+
+import android.annotation.SuppressLint
+import android.content.ContentResolver
+import android.net.Uri
+import android.provider.MediaStore
+import com.lucasjosino.on_audio_query.PluginProvider
+import io.flutter.Log
+import java.io.File
+
+/** OnAllPathQuery */
+class AllPathQuery {
+
+ companion object {
+ private const val TAG = "OnAllPathQuery"
+
+ private val URI: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
+ }
+
+ private lateinit var resolver: ContentResolver
+
+ /**
+ * Method to "query" all paths.
+ */
+ fun queryAllPath() {
+ val result = PluginProvider.result()
+ val context = PluginProvider.context()
+ this.resolver = context.contentResolver
+
+ val resultAllPath = loadAllPath()
+ result.success(resultAllPath)
+ }
+
+ // Ignore the '_data' deprecation because this plugin support older versions.
+ @SuppressLint("Range")
+ @Suppress("DEPRECATION")
+ private fun loadAllPath(): ArrayList {
+ val cursor = resolver.query(URI, null, null, null, null)
+
+ val songPathList: ArrayList = ArrayList()
+
+ Log.d(TAG, "Cursor count: ${cursor?.count}")
+
+ // For each item(path) inside this "cursor", take one and add to the list.
+ while (cursor != null && cursor.moveToNext()) {
+ val content = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))
+
+ val path = File(content).parent
+
+ // Check if path is null or if already exist inside list.
+ if (path != null && !songPathList.contains(path)) {
+ songPathList.add(path)
+ }
+ }
+
+ // Close cursor to avoid memory leaks.
+ cursor?.close()
+ return songPathList
+ }
+}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnArtistsQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/ArtistQuery.kt
similarity index 55%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnArtistsQuery.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/ArtistQuery.kt
index 91ded344..d4e0abbd 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnArtistsQuery.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/ArtistQuery.kt
@@ -1,26 +1,29 @@
-package com.lucasjosino.on_audio_query.query
+package com.lucasjosino.on_audio_query.queries
import android.content.ContentResolver
-import android.content.Context
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.lucasjosino.on_audio_query.OnAudioQueryPlugin
-import com.lucasjosino.on_audio_query.query.helper.OnAudioHelper
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.controllers.PermissionController
+import com.lucasjosino.on_audio_query.queries.helper.QueryHelper
import com.lucasjosino.on_audio_query.types.checkArtistsUriType
import com.lucasjosino.on_audio_query.types.sorttypes.checkArtistSortType
import com.lucasjosino.on_audio_query.utils.artistProjection
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
+import io.flutter.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/** OnArtistsQuery */
-class OnArtistsQuery : ViewModel() {
+class ArtistQuery : ViewModel() {
+
+ companion object {
+ private const val TAG = "OnArtistsQuery"
+ }
//Main parameters
- private val helper = OnAudioHelper()
+ private val helper = QueryHelper()
// None of this methods can be null.
private lateinit var uri: Uri
@@ -29,14 +32,12 @@ class OnArtistsQuery : ViewModel() {
/**
* Method to "query" all artists.
- *
- * Parameters:
- * * [context]
- * * [result]
- * * [call]
*/
- fun queryArtists(context: Context, result: MethodChannel.Result, call: MethodCall) {
- resolver = context.contentResolver
+ fun queryArtists() {
+ val call = PluginProvider.call()
+ val result = PluginProvider.result()
+ val context = PluginProvider.context()
+ this.resolver = context.contentResolver
// Sort: Type and Order
sortType = checkArtistSortType(
@@ -44,41 +45,38 @@ class OnArtistsQuery : ViewModel() {
call.argument("orderType")!!,
call.argument("ignoreCase")!!
)
+
// Check uri:
- // * [0]: External.
- // * [1]: Internal.
+ // * 0 -> External.
+ // * 1 -> Internal.
uri = checkArtistsUriType(call.argument("uri")!!)
+ Log.d(TAG, "Query config: ")
+ Log.d(TAG, "\tsortType: $sortType")
+ Log.d(TAG, "\turi: $uri")
+
// Query everything in background for a better performance.
viewModelScope.launch {
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty list.
- var resultArtistList = ArrayList>()
-
- // We cannot "query" without permission so, just return a empty list.
- if (hasPermission) {
- // Start querying
- resultArtistList = loadArtists()
- }
-
- //Flutter UI will start, but, information still loading
- result.success(resultArtistList)
+ val queryResult = loadArtists()
+ result.success(queryResult)
}
}
// Loading in Background
private suspend fun loadArtists(): ArrayList> =
withContext(Dispatchers.IO) {
- // Setup the cursor with [uri], [projection] and [sortType].
+ // Setup the cursor with 'uri', 'projection' and 'sortType'.
val cursor = resolver.query(uri, artistProjection, null, null, sortType)
- // Empty list.
+
val artistList: ArrayList> = ArrayList()
+ Log.d(TAG, "Cursor count: ${cursor?.count}")
+
// For each item(artist) inside this "cursor", take one and "format"
- // into a [Map].
+ // into a 'Map'.
while (cursor != null && cursor.moveToNext()) {
val tempData: MutableMap = HashMap()
+
for (artistMedia in cursor.columnNames) {
tempData[artistMedia] = helper.loadArtistItem(artistMedia, cursor)
}
@@ -88,18 +86,6 @@ class OnArtistsQuery : ViewModel() {
// Close cursor to avoid memory leaks.
cursor?.close()
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
return@withContext artistList
}
}
-
-//Extras:
-
-//I/OnArtistCursor[All/Audio]: [
-// _id
-// artist
-// artist_key
-// number_of_albums
-// number_of_tracks
-// ]
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnArtworksQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/ArtworkQuery.kt
similarity index 52%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnArtworksQuery.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/ArtworkQuery.kt
index 3788c7d6..dc157f16 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnArtworksQuery.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/ArtworkQuery.kt
@@ -1,8 +1,7 @@
-package com.lucasjosino.on_audio_query.query
+package com.lucasjosino.on_audio_query.queries
import android.content.ContentResolver
import android.content.ContentUris
-import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever
@@ -11,12 +10,11 @@ import android.os.Build
import android.util.Size
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.lucasjosino.on_audio_query.OnAudioQueryPlugin
-import com.lucasjosino.on_audio_query.query.helper.OnAudioHelper
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.queries.helper.QueryHelper
import com.lucasjosino.on_audio_query.types.checkArtworkFormat
import com.lucasjosino.on_audio_query.types.checkArtworkType
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
+import io.flutter.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -24,70 +22,73 @@ import java.io.ByteArrayOutputStream
import java.io.FileInputStream
/** OnArtworksQuery */
-class OnArtworksQuery : ViewModel() {
+class ArtworkQuery : ViewModel() {
+
+ companion object {
+ private const val TAG = "OnArtworksQuery"
+ }
//Main parameters
- private val helper = OnAudioHelper()
+ private val helper = QueryHelper()
private var type: Int = -1
private var id: Number = 0
private var quality: Int = 100
private var size: Int = 200
- // None of this methods can be null.
private lateinit var uri: Uri
private lateinit var resolver: ContentResolver
private lateinit var format: Bitmap.CompressFormat
/**
* Method to "query" all albums.
- *
- * Parameters:
- * * [context]
- * * [result]
- * * [call]
*/
- fun queryArtwork(context: Context, result: MethodChannel.Result, call: MethodCall) {
- resolver = context.contentResolver
+ fun queryArtwork() {
+ val call = PluginProvider.call()
+ val result = PluginProvider.result()
+ val context = PluginProvider.context()
+ this.resolver = context.contentResolver
+
+ id = call.argument("id")!!
+
+ // If the 'size' is null, will be '200'.
+ size = call.argument("size")!!
- // The [id] of the song/album. If the [size] is null, will be [200].
- id = call.argument("id")!!; size = call.argument("size")!!
- // Define the quality of image.
- // The [quality] value cannot be greater than 100 so, we check and if is, set to [100].
+ // The 'quality' value cannot be greater than 100 so, we check and if is, set to '50'.
quality = call.argument("quality")!!
- if (quality > 100) quality = 100
+ if (quality > 100) quality = 50
+
// Check format:
- // * [0]: JPEG
- // * [1]: PNG
+ // * 0 -> JPEG
+ // * 1 -> PNG
format = checkArtworkFormat(call.argument("format")!!)
+
// Check uri:
- // * [0]: Song.
- // * [1]: Album.
- // * [2]: Playlist.
- // * [3]: Artist.
- // * [4]: Genre.
+ // * 0 -> Song.
+ // * 1 -> Album.
+ // * 2 -> Playlist.
+ // * 3 -> Artist.
+ // * 4 -> Genre.
uri = checkArtworkType(call.argument("type")!!)
- // Define the [type]:
+
type = call.argument("type")!!
+ Log.d(TAG, "Query config: ")
+ Log.d(TAG, "\tid: $id")
+ Log.d(TAG, "\tquality: $quality")
+ Log.d(TAG, "\tformat: $format")
+ Log.d(TAG, "\turi: $uri")
+ Log.d(TAG, "\ttype: $type")
+
// Query everything in background for a better performance.
viewModelScope.launch {
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty array.
- var resultArtList: ByteArray? = null
-
- // We cannot "query" without permission so, just return null.
- if (hasPermission) {
- // Start querying
- resultArtList = loadArt()
- }
+ var resultArtList = loadArt()
// Sometimes android will extract a 'wrong' or 'empty' artwork. Just set as null.
if (resultArtList != null && resultArtList.isEmpty()) {
+ Log.i(TAG, "Artwork for '$id' is empty. Returning null")
resultArtList = null
}
- // Flutter UI will start, but, information still loading
result.success(resultArtList)
}
}
@@ -95,28 +96,23 @@ class OnArtworksQuery : ViewModel() {
//Loading in Background
@Suppress("BlockingMethodInNonBlockingContext")
private suspend fun loadArt(): ByteArray? = withContext(Dispatchers.IO) {
- // Empty array.
var artData: ByteArray? = null
- // In this case we need check the [Android] version and query [type].
- //
- // If [Android] >= 29/Q:
- // * We have a limited access to files/folders and we use [loadThumbnail].
- // If [Android] < 29/Q:
- // * We use the [embeddedPicture] from [MediaMetadataRetriever] to get the image.
+ // If 'Android' >= 29/Q:
+ // * Limited access to files/folders. Use 'loadThumbnail'.
+ // If 'Android' < 29/Q:
+ // * Use the 'embeddedPicture' from 'MediaMetadataRetriever' to get the image.
if (Build.VERSION.SDK_INT >= 29) {
- // Try / Catch to avoid problems.
try {
- // If [type] is 2, 3 or 4, we need to 'get' the first item from playlist or artist.
- // We'll use the first artist song to 'simulate' the artwork.
+ // If 'type' is 2, 3 or 4, Get the first item from playlist or artist.
+ // Use the first artist song to 'simulate' the artwork.
//
// Type:
- // * [2]: Playlist.
- // * [3]: Artist.
- // * [4]: Genre.
+ // * 2 -> Playlist.
+ // * 3 -> Artist.
+ // * 4 -> Genre.
//
- // Due old problems with [MethodChannel] the [id] is defined as [Number].
- // Here we convert to [Long]
+ // OBS: The 'id' is defined as 'Number'. Convert to 'Long'
val query = if (type == 2 || type == 3 || type == 4) {
val item = helper.loadFirstItem(type, id, resolver) ?: return@withContext null
ContentUris.withAppendedId(uri, item.toLong())
@@ -127,40 +123,34 @@ class OnArtworksQuery : ViewModel() {
val bitmap = resolver.loadThumbnail(query, Size(size, size), null)
artData = convertOrResize(bitmap = bitmap)!!
} catch (e: Exception) {
- // Some problem can occur, we hide to not "flood" the terminal.
-// Log.i("on_audio_error: ", e.toString())
+ Log.w(TAG, "($id) Message: $e")
}
} else {
- // If [uri == Audio]:
- // * Load the first [item] from cursor using the [id] as filter.
+ // If 'uri == Audio':
+ // * Load the first 'item' from cursor using the 'id' as filter.
// else:
- // * Load the first [item] from [album] using the [id] as filter.
+ // * Load the first 'item' from 'album' using the 'id' as filter.
//
- // If [item] return null, no song/album has found, just return null.
+ // If 'item' return null, no song/album has found, return null.
val item = helper.loadFirstItem(type, id, resolver) ?: return@withContext null
+
try {
- // I tried both [_data] and [_uri], none of them work.
- // So we use the [_data] inside the [FileInputStream] and take the
- // [fd(FileDescriptor)].
val file = FileInputStream(item)
val metadata = MediaMetadataRetriever()
- // Most of the cases the error occurred here.
metadata.setDataSource(file.fd)
val image = metadata.embeddedPicture
- // Check if [image] null.
+ // Convert image. If null, return
artData = convertOrResize(byteArray = image) ?: return@withContext null
- // [close] can only be called using [Android] >= 29/Q.
+ // 'close' can only be called using 'Android' >= 29/Q.
if (Build.VERSION.SDK_INT >= 29) metadata.close()
} catch (e: Exception) {
- // Some problem can occur, we hide to not "flood" the terminal.
-// Log.i("on_audio_error: ", e.toString())
+ Log.w(TAG, "($id) Message: $e")
}
}
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
+
return@withContext artData
}
@@ -168,8 +158,9 @@ class OnArtworksQuery : ViewModel() {
private fun convertOrResize(bitmap: Bitmap? = null, byteArray: ByteArray? = null): ByteArray? {
val convertedBytes: ByteArray?
val byteArrayBase = ByteArrayOutputStream()
+
try {
- // If [bitmap] isn't null:
+ // If 'bitmap' isn't null:
// * The image(bitmap) is from first method. (Android >= 29/Q).
// else:
// * The image(bytearray) is from second method. (Android < 29/Q).
@@ -180,8 +171,9 @@ class OnArtworksQuery : ViewModel() {
convertedBitmap.compress(format, quality, byteArrayBase)
}
} catch (e: Exception) {
- //Log.i("Error", e.toString())
+ Log.w(TAG, "($id) Message: $e")
}
+
convertedBytes = byteArrayBase.toByteArray()
byteArrayBase.close()
return convertedBytes
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAudiosFromQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AudioFromQuery.kt
similarity index 59%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAudiosFromQuery.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AudioFromQuery.kt
index 672fb123..00c0933c 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAudiosFromQuery.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AudioFromQuery.kt
@@ -1,18 +1,18 @@
-package com.lucasjosino.on_audio_query.query
+package com.lucasjosino.on_audio_query.queries
-import android.annotation.SuppressLint
import android.content.ContentResolver
-import android.content.Context
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.lucasjosino.on_audio_query.OnAudioQueryPlugin
-import com.lucasjosino.on_audio_query.query.helper.OnAudioHelper
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.controllers.PermissionController
+import com.lucasjosino.on_audio_query.queries.helper.QueryHelper
import com.lucasjosino.on_audio_query.types.checkAudiosFromType
import com.lucasjosino.on_audio_query.types.sorttypes.checkSongSortType
import com.lucasjosino.on_audio_query.utils.songProjection
+import io.flutter.Log
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.Dispatchers
@@ -20,17 +20,20 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/** OnAudiosFromQuery */
-class OnAudiosFromQuery : ViewModel() {
+class AudioFromQuery : ViewModel() {
+
+ companion object {
+ private const val TAG = "OnAudiosFromQuery"
+
+ private val URI: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
+ }
//Main parameters
- private val helper = OnAudioHelper()
- private val uri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
+ private val helper = QueryHelper()
private var pId = 0
private var pUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
// None of this methods can be null.
- @SuppressLint("StaticFieldLeak")
- private lateinit var context: Context
private lateinit var where: String
private lateinit var whereVal: String
private lateinit var sortType: String
@@ -38,67 +41,59 @@ class OnAudiosFromQuery : ViewModel() {
/**
* Method to "query" all songs from a specific item.
- *
- * Parameters:
- * * [context]
- * * [result]
- * * [call]
*/
- fun querySongsFrom(context: Context, result: MethodChannel.Result, call: MethodCall) {
- this.context = context; resolver = context.contentResolver
-
- // The type of [item]:
- // * [0]: Album
- // * [1]: Album Id
- // * [2]: Artist
- // * [3]: Artist Id
- // * [4]: Genre
- // * [5]: Genre Id
- // * [6]: Playlist
+ fun querySongsFrom() {
+ val call = PluginProvider.call()
+ val result = PluginProvider.result()
+ val context = PluginProvider.context()
+ this.resolver = context.contentResolver
+
+ // The type of 'item':
+ // * 0 -> Album
+ // * 1 -> Album Id
+ // * 2 -> Artist
+ // * 3 -> Artist Id
+ // * 4 -> Genre
+ // * 5 -> Genre Id
+ // * 6 -> Playlist
val type = call.argument("type")!!
+
// Sort: Type and Order.
- // TODO
sortType = checkSongSortType(
call.argument("sortType"),
call.argument("orderType")!!,
call.argument("ignoreCase")!!
)
+ Log.d(TAG, "Query config: ")
+ Log.d(TAG, "\tsortType: $sortType")
+ Log.d(TAG, "\ttype: $type")
+ Log.d(TAG, "\turi: $URI")
+
// TODO: Add a better way to handle this query
// This will fix (for now) the problem between Android < 30 && Android > 30
// The method used to query genres on Android >= 30 don't work properly on Android < 30 so,
// we need separate.
//
- // If helper == 6 (Playlist) send to [querySongsFromPlaylistOrGenre] in any version.
+ // If helper == 6 (Playlist) send to 'querySongsFromPlaylistOrGenre' in any version.
// If helper == 4 (Genre) || helper == 5 (GenreId) and Android < 30 send to
- // [querySongsFromPlaylistOrGenre] else, follow the rest of the "normal" code.
+ // 'querySongsFromPlaylistOrGenre' else, follow the rest of the "normal" code.
//
- // Why? Android 10 and below don't has "genre" category and we need use a "workaround".
- // [MediaStore](https://developer.android.com/reference/android/provider/MediaStore.Audio.AudioColumns#GENRE)
+ // Why? Android 10 and below doesn't have "genre" category and we need use a "workaround".
+ // 'MediaStore'(https://developer.android.com/reference/android/provider/MediaStore.Audio.AudioColumns#GENRE)
if (type == 6 || ((type == 4 || type == 5) && Build.VERSION.SDK_INT < 30)) {
- // Works on [Android] 10.
+ // Works on Android 10.
querySongsFromPlaylistOrGenre(result, call, type)
} else {
- // Works on [Android] 11.
- // [whereVal] -> Album/Artist/Genre(Sometimes)
- // [where] -> uri
+ // Works on Android 11.
+ // 'whereVal' -> Album/Artist/Genre
+ // 'where' -> uri
whereVal = call.argument("where")!!.toString()
where = checkAudiosFromType(type)
// Query everything in background for a better performance.
viewModelScope.launch {
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty list.
- var resultSongList = ArrayList>()
-
- // We cannot "query" without permission so, just return a empty list.
- if (hasPermission) {
- // Start querying
- resultSongList = loadSongsFrom()
- }
-
- //Flutter UI will start, but, information still loading
+ val resultSongList = loadSongsFrom()
result.success(resultSongList)
}
}
@@ -107,13 +102,15 @@ class OnAudiosFromQuery : ViewModel() {
//Loading in Background
private suspend fun loadSongsFrom(): ArrayList> =
withContext(Dispatchers.IO) {
- // Setup the cursor with [uri], [projection], [selection](where) and [values](whereVal).
- val cursor = resolver.query(uri, songProjection(), where, arrayOf(whereVal), sortType)
- // Empty list.
+ // Setup the cursor with 'uri', 'projection', 'selection'(where) and 'values'(whereVal).
+ val cursor = resolver.query(URI, songProjection(), where, arrayOf(whereVal), sortType)
+
val songsFromList: ArrayList> = ArrayList()
+ Log.d(TAG, "Cursor count: ${cursor?.count}")
+
// For each item(song) inside this "cursor", take one and "format"
- // into a [Map].
+ // into a 'Map'.
while (cursor != null && cursor.moveToNext()) {
val tempData: MutableMap = HashMap()
for (audioMedia in cursor.columnNames) {
@@ -121,17 +118,14 @@ class OnAudiosFromQuery : ViewModel() {
}
//Get a extra information from audio, e.g: extension, uri, etc..
- val tempExtraData = helper.loadSongExtraInfo(uri, tempData)
+ val tempExtraData = helper.loadSongExtraInfo(URI, tempData)
tempData.putAll(tempExtraData)
- //
songsFromList.add(tempData)
}
// Close cursor to avoid memory leaks.
cursor?.close()
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
return@withContext songsFromList
}
@@ -143,63 +137,59 @@ class OnAudiosFromQuery : ViewModel() {
) {
val info = call.argument("where")!!
- //Check if Playlist exists based in Id
+ // Check if playlist exists using the id.
val checkedName = if (type == 4 || type == 5) {
checkName(genreName = info.toString())
- } else checkName(plName = info.toString())
+ } else {
+ checkName(plName = info.toString())
+ }
if (!checkedName) pId = info.toString().toInt()
- //
pUri = if (type == 4 || type == 5) {
MediaStore.Audio.Genres.Members.getContentUri("external", pId.toLong())
- } else MediaStore.Audio.Playlists.Members.getContentUri("external", pId.toLong())
+ } else {
+ MediaStore.Audio.Playlists.Members.getContentUri("external", pId.toLong())
+ }
// Query everything in background for a better performance.
viewModelScope.launch {
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty list.
- var resultSongsFrom = ArrayList>()
-
- // We cannot "query" without permission so, just return a empty list.
- if (hasPermission) {
- // Start querying
- resultSongsFrom = loadSongsFromPlaylistOrGenre()
- }
-
- //Flutter UI will start, but, information still loading
+ val resultSongsFrom = loadSongsFromPlaylistOrGenre()
result.success(resultSongsFrom)
}
}
private suspend fun loadSongsFromPlaylistOrGenre(): ArrayList> =
withContext(Dispatchers.IO) {
-
val songsFrom: ArrayList> = ArrayList()
+
val cursor = resolver.query(pUri, songProjection(), null, null, sortType)
+
+ Log.d(TAG, "Cursor count: ${cursor?.count}")
+
while (cursor != null && cursor.moveToNext()) {
val tempData: MutableMap = HashMap()
+
for (media in cursor.columnNames) {
tempData[media] = helper.loadSongItem(media, cursor)
}
//Get a extra information from audio, e.g: extension, uri, etc..
- val tempExtraData = helper.loadSongExtraInfo(uri, tempData)
+ val tempExtraData = helper.loadSongExtraInfo(URI, tempData)
tempData.putAll(tempExtraData)
songsFrom.add(tempData)
}
+
cursor?.close()
return@withContext songsFrom
}
- //Return true if playlist or genre exists, false, if don't.
+ // Return true if playlist or genre exists and false, if don't.
private fun checkName(plName: String? = null, genreName: String? = null): Boolean {
val uri: Uri
val projection: Array
- //
if (plName != null) {
uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
projection = arrayOf(MediaStore.Audio.Playlists.NAME, MediaStore.Audio.Playlists._ID)
@@ -208,46 +198,17 @@ class OnAudiosFromQuery : ViewModel() {
projection = arrayOf(MediaStore.Audio.Genres.NAME, MediaStore.Audio.Genres._ID)
}
- //
val cursor = resolver.query(uri, projection, null, null, null)
while (cursor != null && cursor.moveToNext()) {
- val name = cursor.getString(0) //Name
+ val name = cursor.getString(0)
if (name != null && name == plName || name == genreName) {
pId = cursor.getInt(1)
return true
}
}
+
cursor?.close()
return false
}
}
-
-//Extras:
-
-// * All projection used for query audio in this Plugin
-//I/OnAudioCursor[Audio]: [
-// _data,
-// _display_name,
-// _id,
-// _size,
-// album,
-// album_artist,
-// album_id
-// album_key,
-// artist,
-// artist_id,
-// artist_key,
-// bookmark,
-// composer,
-// date_added,
-// duration,
-// title,
-// track,
-// year,
-// is_alarm
-// is_music,
-// is_notification,
-// is_podcast,
-// is_ringtone
-// ]
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AudioQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AudioQuery.kt
new file mode 100644
index 00000000..ef218947
--- /dev/null
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/AudioQuery.kt
@@ -0,0 +1,102 @@
+package com.lucasjosino.on_audio_query.queries
+
+import android.content.ContentResolver
+import android.net.Uri
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.queries.helper.QueryHelper
+import com.lucasjosino.on_audio_query.types.checkAudiosUriType
+import com.lucasjosino.on_audio_query.types.sorttypes.checkSongSortType
+import com.lucasjosino.on_audio_query.utils.songProjection
+import io.flutter.Log
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/** OnAudiosQuery */
+class AudioQuery : ViewModel() {
+
+ companion object {
+ private const val TAG = "OnAudiosQuery"
+ }
+
+ // Main parameters
+ private val helper = QueryHelper()
+ private var selection: String? = null
+
+ private lateinit var uri: Uri
+ private lateinit var sortType: String
+ private lateinit var resolver: ContentResolver
+
+ /**
+ * Method to "query" all songs.
+ */
+ @Suppress("DEPRECATION")
+ fun querySongs() {
+ val call = PluginProvider.call()
+ val result = PluginProvider.result()
+ val context = PluginProvider.context()
+ this.resolver = context.contentResolver
+
+ // Sort: Type and Order.
+ sortType = checkSongSortType(
+ call.argument("sortType"),
+ call.argument("orderType")!!,
+ call.argument("ignoreCase")!!
+ )
+
+ // Check uri:
+ // * 0 -> External.
+ // * 1 -> Internal.
+ uri = checkAudiosUriType(call.argument("uri")!!)
+
+ // Here we provide a custom 'path'.
+ if (call.argument("path") != null) {
+ val projection = songProjection()
+ selection = projection[0] + " like " + "'%" + call.argument("path") + "/%'"
+ }
+
+ Log.d(TAG, "Query config: ")
+ Log.d(TAG, "\tsortType: $sortType")
+ Log.d(TAG, "\tselection: $selection")
+ Log.d(TAG, "\turi: $uri")
+
+ // Query everything in background for a better performance.
+ viewModelScope.launch {
+ val queryResult = loadSongs()
+ result.success(queryResult)
+ }
+ }
+
+ //Loading in Background
+ private suspend fun loadSongs(): ArrayList> =
+ withContext(Dispatchers.IO) {
+ // Setup the cursor with 'uri', 'projection' and 'sortType'.
+ val cursor = resolver.query(uri, songProjection(), selection, null, sortType)
+
+ val songList: ArrayList> = ArrayList()
+
+ Log.d(TAG, "Cursor count: ${cursor?.count}")
+
+ // For each item(song) inside this "cursor", take one and "format"
+ // into a 'Map'.
+ while (cursor != null && cursor.moveToNext()) {
+ val tempData: MutableMap = HashMap()
+
+ for (audioMedia in cursor.columnNames) {
+ tempData[audioMedia] = helper.loadSongItem(audioMedia, cursor)
+ }
+
+ //Get a extra information from audio, e.g: extension, uri, etc..
+ val tempExtraData = helper.loadSongExtraInfo(uri, tempData)
+ tempData.putAll(tempExtraData)
+
+ songList.add(tempData)
+ }
+
+ // Close cursor to avoid memory leaks.
+ cursor?.close()
+ return@withContext songList
+ }
+}
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnGenresQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/GenreQuery.kt
similarity index 58%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnGenresQuery.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/GenreQuery.kt
index dabab8c0..536e991f 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnGenresQuery.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/GenreQuery.kt
@@ -1,42 +1,41 @@
-package com.lucasjosino.on_audio_query.query
+package com.lucasjosino.on_audio_query.queries
import android.content.ContentResolver
-import android.content.Context
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.lucasjosino.on_audio_query.OnAudioQueryPlugin
-import com.lucasjosino.on_audio_query.query.helper.OnAudioHelper
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.queries.helper.QueryHelper
import com.lucasjosino.on_audio_query.types.checkGenresUriType
import com.lucasjosino.on_audio_query.types.sorttypes.checkGenreSortType
import com.lucasjosino.on_audio_query.utils.genreProjection
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
+import io.flutter.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/** OnGenresQuery */
-class OnGenresQuery : ViewModel() {
+class GenreQuery : ViewModel() {
+
+ companion object {
+ private const val TAG = "OnGenresQuery"
+ }
// Main parameters.
- private val helper = OnAudioHelper()
+ private val helper = QueryHelper()
- // None of this methods can be null.
private lateinit var uri: Uri
private lateinit var sortType: String
private lateinit var resolver: ContentResolver
/**
* Method to "query" all genres.
- *
- * Parameters:
- * * [context]
- * * [result]
- * * [call]
*/
- fun queryGenres(context: Context, result: MethodChannel.Result, call: MethodCall) {
- resolver = context.contentResolver
+ fun queryGenres() {
+ val call = PluginProvider.call()
+ val result = PluginProvider.result()
+ val context = PluginProvider.context()
+ this.resolver = context.contentResolver
// Sort: Type and Order.
sortType = checkGenreSortType(
@@ -44,41 +43,38 @@ class OnGenresQuery : ViewModel() {
call.argument("orderType")!!,
call.argument("ignoreCase")!!
)
+
// Check uri:
- // * [0]: External.
- // * [1]: Internal.
+ // * 0 -> External
+ // * 1 -> Internal
uri = checkGenresUriType(call.argument("uri")!!)
+ Log.d(TAG, "Query config: ")
+ Log.d(TAG, "\tsortType: $sortType")
+ Log.d(TAG, "\turi: $uri")
+
// Query everything in background for a better performance.
viewModelScope.launch {
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty list.
- var resultGenreList = ArrayList>()
-
- // We cannot "query" without permission so, just return a empty list.
- if (hasPermission) {
- // Start querying
- resultGenreList = loadGenres()
- }
-
- //Flutter UI will start, but, information still loading
- result.success(resultGenreList)
+ val queryResult = loadGenres()
+ result.success(queryResult)
}
}
- //Loading in Background
+ // Loading in Background
private suspend fun loadGenres(): ArrayList> =
withContext(Dispatchers.IO) {
- // Setup the cursor with [uri], [projection] and [sortType].
+ // Setup the cursor with 'uri', 'projection' and 'sortType'.
val cursor = resolver.query(uri, genreProjection, null, null, sortType)
- // Empty list.
+
val genreList: ArrayList> = ArrayList()
+ Log.d(TAG, "Cursor count: ${cursor?.count}")
+
// For each item(genre) inside this "cursor", take one and "format"
- // into a [Map].
+ // into a 'Map'.
while (cursor != null && cursor.moveToNext()) {
val genreData: MutableMap = HashMap()
+
for (genreMedia in cursor.columnNames) {
genreData[genreMedia] = helper.loadGenreItem(genreMedia, cursor)
}
@@ -94,15 +90,6 @@ class OnGenresQuery : ViewModel() {
// Close cursor to avoid memory leaks.
cursor?.close()
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
return@withContext genreList
}
}
-
-//Extras:
-
-//I/OnGenreCursor[All/Audio]: [
-// _id
-// name
-// ]
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnPlaylistQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/PlaylistQuery.kt
similarity index 57%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnPlaylistQuery.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/PlaylistQuery.kt
index 86bc9f87..e7501f67 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnPlaylistQuery.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/PlaylistQuery.kt
@@ -1,42 +1,42 @@
-package com.lucasjosino.on_audio_query.query
+package com.lucasjosino.on_audio_query.queries
import android.content.ContentResolver
-import android.content.Context
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.lucasjosino.on_audio_query.OnAudioQueryPlugin
-import com.lucasjosino.on_audio_query.query.helper.OnAudioHelper
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.controllers.PermissionController
+import com.lucasjosino.on_audio_query.queries.helper.QueryHelper
import com.lucasjosino.on_audio_query.types.checkPlaylistsUriType
import com.lucasjosino.on_audio_query.types.sorttypes.checkGenreSortType
import com.lucasjosino.on_audio_query.utils.playlistProjection
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
+import io.flutter.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/** OnPlaylistQuery */
-class OnPlaylistQuery : ViewModel() {
+class PlaylistQuery : ViewModel() {
+
+ companion object {
+ private const val TAG = "OnPlaylistQuery"
+ }
//Main parameters
- private val helper = OnAudioHelper()
+ private val helper = QueryHelper()
- // None of this methods can be null.
private lateinit var uri: Uri
private lateinit var resolver: ContentResolver
private lateinit var sortType: String
/**
* Method to "query" all playlists.
- *
- * Parameters:
- * * [context]
- * * [result]
- * * [call]
*/
- fun queryPlaylists(context: Context, result: MethodChannel.Result, call: MethodCall) {
- resolver = context.contentResolver
+ fun queryPlaylists() {
+ val call = PluginProvider.call()
+ val result = PluginProvider.result()
+ val context = PluginProvider.context()
+ this.resolver = context.contentResolver
// Sort: Type and Order.
sortType = checkGenreSortType(
@@ -45,40 +45,36 @@ class OnPlaylistQuery : ViewModel() {
call.argument("ignoreCase")!!
)
// Check uri:
- // * [0]: External.
- // * [1]: Internal.
+ // * 0 -> External.
+ // * 1 -> Internal.
uri = checkPlaylistsUriType(call.argument("uri")!!)
+ Log.d(TAG, "Query config: ")
+ Log.d(TAG, "\tsortType: $sortType")
+ Log.d(TAG, "\turi: $uri")
+
// Query everything in background for a better performance.
viewModelScope.launch {
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty list.
- var resultPlaylistList = ArrayList>()
-
- // We cannot "query" without permission so, just return a empty list.
- if (hasPermission) {
- // Start querying
- resultPlaylistList = loadPlaylists()
- }
-
- //Flutter UI will start, but, information still loading
- result.success(resultPlaylistList)
+ val queryResult = loadPlaylists()
+ result.success(queryResult)
}
}
//Loading in Background
private suspend fun loadPlaylists(): ArrayList> =
withContext(Dispatchers.IO) {
- // Setup the cursor with [uri] and [projection].
+ // Setup the cursor with 'uri' and 'projection'.
val cursor = resolver.query(uri, playlistProjection, null, null, null)
- // Empty list.
+
val playlistList: ArrayList> = ArrayList()
+ Log.d(TAG, "Cursor count: ${cursor?.count}")
+
// For each item(playlist) inside this "cursor", take one and "format"
- // into a [Map].
+ // into a 'Map'.
while (cursor != null && cursor.moveToNext()) {
val playlistData: MutableMap = HashMap()
+
for (playlistMedia in cursor.columnNames) {
playlistData[playlistMedia] = helper.loadPlaylistItem(playlistMedia, cursor)
}
@@ -92,18 +88,6 @@ class OnPlaylistQuery : ViewModel() {
// Close cursor to avoid memory leaks.
cursor?.close()
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
return@withContext playlistList
}
}
-
-//Extras:
-
-//I/OnPlaylistCursor[All/Audio]: [
-// _data
-// _id
-// date_added
-// date_modified
-// name
-// ]
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnWithFiltersQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/WithFiltersQuery.kt
similarity index 60%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnWithFiltersQuery.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/WithFiltersQuery.kt
index a67cca42..ed80ce35 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnWithFiltersQuery.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/WithFiltersQuery.kt
@@ -1,55 +1,57 @@
-package com.lucasjosino.on_audio_query.query
+package com.lucasjosino.on_audio_query.queries
-import android.annotation.SuppressLint
import android.content.ContentResolver
-import android.content.Context
import android.net.Uri
import android.provider.MediaStore
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.lucasjosino.on_audio_query.OnAudioQueryPlugin
-import com.lucasjosino.on_audio_query.query.helper.OnAudioHelper
+import com.lucasjosino.on_audio_query.PluginProvider
+import com.lucasjosino.on_audio_query.queries.helper.QueryHelper
import com.lucasjosino.on_audio_query.types.*
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
+import io.flutter.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-class OnWithFiltersQuery : ViewModel() {
+class WithFiltersQuery : ViewModel() {
+
+ companion object {
+ private const val TAG = "OnWithFiltersQuery"
+
+ private val URI = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
+ }
//Main parameters
- private val helper = OnAudioHelper()
- private val uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
+ private val helper = QueryHelper()
private var projection: Array? = arrayOf()
- @SuppressLint("StaticFieldLeak")
- // None of this methods can be null.
- private lateinit var context: Context
private lateinit var resolver: ContentResolver
private lateinit var withType: Uri
private lateinit var argsVal: String
private lateinit var argsKey: String
//
- fun queryWithFilters(context: Context, result: MethodChannel.Result, call: MethodCall) {
- this.context = context; resolver = context.contentResolver
+ fun queryWithFilters() {
+ val call = PluginProvider.call()
+ val result = PluginProvider.result()
+ val context = PluginProvider.context()
+ this.resolver = context.contentResolver
// Choose the type.
- // * [0]: Audios
- // * [1]: Albums
- // * [2]: Playlists
- // * [3]: Artists
- // * [4]: Genres
+ // * 0 -> Audios
+ // * 1 -> Albums
+ // * 2 -> Playlists
+ // * 3 -> Artists
+ // * 4 -> Genres
withType = checkWithFiltersType(call.argument("withType")!!)
- // The [args] are converted to [String] before send to [MethodChannel].
+ // The 'args' are converted to 'String' before send to 'MethodChannel'.
argsVal = "%" + call.argument("argsVal")!! + "%"
- // A dynamic [projection] to every type of "query".
+ // A dynamic 'projection' to every type of "query".
projection = checkProjection(withType)
- // Choose the [arg].
+ // Choose the 'arg'.
argsKey = when (withType) {
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI -> checkSongsArgs(call.argument("args")!!)
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI -> checkAlbumsArgs(call.argument("args")!!)
@@ -63,36 +65,33 @@ class OnWithFiltersQuery : ViewModel() {
else -> throw Exception("[argsKey] returned null. Report this issue on [on_audio_query] GitHub.")
}
+ Log.d(TAG, "Query config: ")
+ Log.d(TAG, "\twithType: $withType")
+ Log.d(TAG, "\targsVal: $argsVal")
+ Log.d(TAG, "\targsKey: $argsKey")
+
// Query everything in background for a better performance.
viewModelScope.launch {
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty list.
- var resultWithFilter = ArrayList>()
-
- // We cannot "query" without permission so, just return a empty list.
- if (hasPermission) {
- // Start querying
- resultWithFilter = loadWithFilters()
- }
-
- //Flutter UI will start, but, information still loading
- result.success(resultWithFilter)
+ val queryResult = loadWithFilters()
+ result.success(queryResult)
}
}
//Loading in Background
private suspend fun loadWithFilters(): ArrayList> =
withContext(Dispatchers.IO) {
- // Setup the cursor with [uri], [projection], [argsKey] and [argsVal].
+ // Setup the cursor with 'uri', 'projection', 'argsKey' and 'argsVal'.
val cursor = resolver.query(withType, projection, argsKey, arrayOf(argsVal), null)
- // Empty list.
+
val withFiltersList: ArrayList> = ArrayList()
+ Log.d(TAG, "Cursor count: ${cursor?.count}")
+
// For each item inside this "cursor", take one and "format"
- // into a [Map].
+ // into a 'Map'.
while (cursor != null && cursor.moveToNext()) {
val tempData: MutableMap = HashMap()
+
for (media in cursor.columnNames) {
tempData[media] = helper.chooseWithFilterType(
withType,
@@ -101,10 +100,10 @@ class OnWithFiltersQuery : ViewModel() {
)
}
- // If [withType] is a song media, add the extra information.
+ // If 'withType' is a song media, add the extra information.
if (withType == MediaStore.Audio.Media.EXTERNAL_CONTENT_URI) {
//Get a extra information from audio, e.g: extension, uri, etc..
- val tempExtraData = helper.loadSongExtraInfo(uri, tempData)
+ val tempExtraData = helper.loadSongExtraInfo(URI, tempData)
tempData.putAll(tempExtraData)
}
@@ -113,8 +112,6 @@ class OnWithFiltersQuery : ViewModel() {
// Close cursor to avoid memory leaks.
cursor?.close()
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
return@withContext withFiltersList
}
}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/helper/OnAudioHelper.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/helper/QueryHelper.kt
similarity index 99%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/helper/OnAudioHelper.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/helper/QueryHelper.kt
index 0584b93b..083c7df3 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/helper/OnAudioHelper.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/queries/helper/QueryHelper.kt
@@ -1,4 +1,4 @@
-package com.lucasjosino.on_audio_query.query.helper
+package com.lucasjosino.on_audio_query.queries.helper
import android.content.ContentResolver
import android.content.ContentUris
@@ -9,7 +9,7 @@ import android.provider.MediaStore
import android.util.Log
import java.io.File
-class OnAudioHelper {
+class QueryHelper {
//This method will load some extra information about audio/song
fun loadSongExtraInfo(
uri: Uri,
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAllPathQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAllPathQuery.kt
deleted file mode 100644
index 3b905b69..00000000
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAllPathQuery.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.lucasjosino.on_audio_query.query
-
-import android.content.ContentResolver
-import android.content.Context
-import android.net.Uri
-import android.provider.MediaStore
-import com.lucasjosino.on_audio_query.OnAudioQueryPlugin
-import io.flutter.plugin.common.MethodChannel
-import java.io.File
-
-/** OnAllPathQuery */
-class OnAllPathQuery {
-
- // Main parameters, none of this methods can be null.
- private val uri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
- private lateinit var resolver: ContentResolver
-
- /**
- * Method to "query" all paths.
- *
- * Parameters:
- * * [context]
- * * [result]
- */
- fun queryAllPath(context: Context, result: MethodChannel.Result) {
- this.resolver = context.contentResolver
-
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty list.
- var resultAllPath = ArrayList()
-
- // We cannot "query" without permission so, just return a empty list.
- if (hasPermission) {
- // Start querying
- resultAllPath = loadAllPath()
- }
-
- // Send to Dart.
- result.success(resultAllPath)
- }
-
- // Ignore the [Data] deprecation because this plugin support older versions.
- @Suppress("DEPRECATION")
- private fun loadAllPath(): ArrayList {
- // Setup the cursor with [uri].
- val cursor = resolver.query(uri, null, null, null, null)
- // Empty list.
- val songPathList: ArrayList = ArrayList()
-
- // For each item(path) inside this "cursor", take one and add to the list.
- while (cursor != null && cursor.moveToNext()) {
- val content = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))
- val path = File(content).parent
- // Check if path is null or if already exist inside list.
- if (path != null && !songPathList.contains(path)) songPathList.add(path)
- }
-
- // Close cursor to avoid memory leaks.
- cursor?.close()
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
- return songPathList
- }
-}
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAudiosQuery.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAudiosQuery.kt
deleted file mode 100644
index f41f21b0..00000000
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/query/OnAudiosQuery.kt
+++ /dev/null
@@ -1,179 +0,0 @@
-package com.lucasjosino.on_audio_query.query
-
-import android.annotation.SuppressLint
-import android.content.ContentResolver
-import android.content.Context
-import android.net.Uri
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.lucasjosino.on_audio_query.OnAudioQueryPlugin
-import com.lucasjosino.on_audio_query.query.helper.OnAudioHelper
-import com.lucasjosino.on_audio_query.types.checkAudiosUriType
-import com.lucasjosino.on_audio_query.types.sorttypes.checkSongSortType
-import com.lucasjosino.on_audio_query.utils.songProjection
-import io.flutter.plugin.common.MethodCall
-import io.flutter.plugin.common.MethodChannel
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-/** OnAudiosQuery */
-class OnAudiosQuery : ViewModel() {
-
- // Main parameters
- private val helper = OnAudioHelper()
- private var selection: String? = null
-
- // None of this methods can be null.
- private lateinit var uri: Uri
- private lateinit var resolver: ContentResolver
- private lateinit var sortType: String
-
- @SuppressLint("StaticFieldLeak")
- private lateinit var context: Context
-
-
- /**
- * Method to "query" all songs.
- *
- * Parameters:
- * * [context]
- * * [result]
- * * [call]
- */
- // Ignore the [Data] deprecation because this plugin support older versions.
- @Suppress("DEPRECATION")
- fun querySongs(
- context: Context,
- result: MethodChannel.Result,
- call: MethodCall
- ) {
- this.context = context; resolver = context.contentResolver
-
- // Sort: Type and Order.
- sortType = checkSongSortType(
- call.argument("sortType"),
- call.argument("orderType")!!,
- call.argument("ignoreCase")!!
- )
- // Check uri:
- // * [0]: External.
- // * [1]: Internal.
- uri = checkAudiosUriType(call.argument("uri")!!)
- // Here we provide a custom 'path'.
- if (call.argument("path") != null) {
- val projection = songProjection()
- selection = projection[0] + " like " + "'%" + call.argument("path") + "/%'"
- }
-
- // Query everything in background for a better performance.
- viewModelScope.launch {
- // Request permission status from the main method.
- val hasPermission = OnAudioQueryPlugin().onPermissionStatus(context)
- // Empty list.
- var resultSongList = ArrayList>()
-
- // We cannot "query" without permission so, just return a empty list.
- if (hasPermission) {
- // Start querying
- resultSongList = loadSongs()
- }
-
- //Flutter UI will start, but, information still loading
- result.success(resultSongList)
- }
- }
-
- //Loading in Background
- private suspend fun loadSongs(): ArrayList> =
- withContext(Dispatchers.IO) {
-
- // Setup the cursor with [uri], [projection] and [sortType].
- val cursor = resolver.query(uri, songProjection(), selection, null, sortType)
- // Empty list.
- val songList: ArrayList> = ArrayList()
-
- // For each item(song) inside this "cursor", take one and "format"
- // into a [Map].
- while (cursor != null && cursor.moveToNext()) {
- val tempData: MutableMap = HashMap()
- for (audioMedia in cursor.columnNames) {
- tempData[audioMedia] = helper.loadSongItem(audioMedia, cursor)
- }
-
- //Get a extra information from audio, e.g: extension, uri, etc..
- val tempExtraData = helper.loadSongExtraInfo(uri, tempData)
- tempData.putAll(tempExtraData)
-
- songList.add(tempData)
- }
-
- // Close cursor to avoid memory leaks.
- cursor?.close()
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
- return@withContext songList
- }
-}
-
-//Extras:
-
-// * Query only audio > 60000 ms [1 minute]
-// Obs: I don't think is a good idea, some audio "Non music" have more than 1 minute
-//query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, MediaStore.Audio.Media.DURATION +
-// ">= 60000", null, checkSongSortType(sortType!!))
-
-// * Query audio with limit, used for better performance in tests
-//MediaStore.Audio.Media.TITLE + " LIMIT 4"
-
-// * All projection types in android [Audio]
-//I/AudioCursor[All]: [
-// title_key,
-// instance_id,
-// duration,
-// is_ringtone,
-// album_artist,
-// orientation,
-// artist,
-// height,
-// is_drm,
-// bucket_display_name,
-// is_audiobook,
-// owner_package_name,
-// volume_name,
-// title_resource_uri,
-// date_modified,
-// date_expires,
-// composer,
-// _display_name,
-// datetaken,
-// mime_type,
-// is_notification,
-// _id,
-// year,
-// _data,
-// _hash,
-// _size,
-// album,
-// is_alarm,
-// title,
-// track,
-// width,
-// is_music,
-// album_key,
-// is_trashed,
-// group_id,
-// document_id,
-// artist_id,
-// artist_key,
-// is_pending,
-// date_added,
-// is_podcast,
-// album_id,
-// primary_directory,
-// secondary_directory,
-// original_document_id,
-// bucket_id,
-// bookmark,
-// relative_path
-// ]
\ No newline at end of file
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/types/ArtworkType.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/types/ArtworkType.kt
index e898834e..024ba082 100644
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/types/ArtworkType.kt
+++ b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/types/ArtworkType.kt
@@ -6,7 +6,7 @@ import android.provider.MediaStore
fun checkArtworkType(sortType: Int): Uri {
return when (sortType) {
- 0,2,3, 4 -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
+ 0, 2, 3, 4 -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
1 -> MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI
else -> throw Exception("[checkArtworkType] value don't exist!")
}
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/utils/OnCursorProjections.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/utils/CursorProjection.kt
similarity index 100%
rename from on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/utils/OnCursorProjections.kt
rename to on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/utils/CursorProjection.kt
diff --git a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/utils/OnDeviceInfo.kt b/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/utils/OnDeviceInfo.kt
deleted file mode 100644
index 72372ee1..00000000
--- a/on_audio_query/android/src/main/kotlin/com/lucasjosino/on_audio_query/utils/OnDeviceInfo.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.lucasjosino.on_audio_query.utils
-
-import android.os.Build
-import io.flutter.plugin.common.MethodChannel
-
-fun queryDeviceInfo(result: MethodChannel.Result) {
- val deviceData: MutableMap = HashMap()
- deviceData["device_model"] = Build.MODEL
- deviceData["device_sys_version"] = Build.VERSION.SDK_INT
- deviceData["device_sys_type"] = "Android"
- result.success(deviceData)
-}
\ No newline at end of file
diff --git a/on_audio_query/example/android/app/src/main/AndroidManifest.xml b/on_audio_query/example/android/app/src/main/AndroidManifest.xml
index 215d9df3..183e75cd 100644
--- a/on_audio_query/example/android/app/src/main/AndroidManifest.xml
+++ b/on_audio_query/example/android/app/src/main/AndroidManifest.xml
@@ -3,13 +3,14 @@
+
-
-
-
-
+
+
+
+
/dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
- 308D7FDF12FC5F39971BB53B /* [CP] Embed Pods Frameworks */ = {
+ 9099D3364DFEDF1A637FD9DD /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -236,20 +250,6 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "Thin Binary";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
- };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -363,7 +363,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.lucasjosino.onAudioQueryExample;
+ PRODUCT_BUNDLE_IDENTIFIER = com.lucasjosino.onAudioQueryExampleIOS;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -493,7 +493,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.lucasjosino.onAudioQueryExample;
+ PRODUCT_BUNDLE_IDENTIFIER = com.lucasjosino.onAudioQueryExampleIOS;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -517,7 +517,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.lucasjosino.onAudioQueryExample;
+ PRODUCT_BUNDLE_IDENTIFIER = com.lucasjosino.onAudioQueryExampleIOS;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
diff --git a/on_audio_query/example/ios/Runner/Info.plist b/on_audio_query/example/ios/Runner/Info.plist
index 05be961e..c9fdbedb 100644
--- a/on_audio_query/example/ios/Runner/Info.plist
+++ b/on_audio_query/example/ios/Runner/Info.plist
@@ -22,6 +22,8 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+ NSAppleMusicUsageDescription
+ Please let this application access your music library
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -41,7 +43,5 @@
UIViewControllerBasedStatusBarAppearance
- NSAppleMusicUsageDescription
- Please let this application access your music library
diff --git a/on_audio_query/example/lib/main.dart b/on_audio_query/example/lib/main.dart
index 43387f49..22247ae3 100644
--- a/on_audio_query/example/lib/main.dart
+++ b/on_audio_query/example/lib/main.dart
@@ -14,7 +14,6 @@ Copyright: © 2021, Lucas Josino. All rights reserved.
import 'package:flutter/material.dart';
import 'package:on_audio_query/on_audio_query.dart';
-import 'package:flutter/foundation.dart' show kIsWeb;
void main() {
runApp(
@@ -32,23 +31,36 @@ class Songs extends StatefulWidget {
}
class _SongsState extends State {
+ // Main method.
final OnAudioQuery _audioQuery = OnAudioQuery();
+ // Indicate if application has permission to the library.
+ bool _hasPermission = false;
+
@override
void initState() {
super.initState();
- requestPermission();
+ // (Optinal) Set logging level. By default will be set to 'WARN'.
+ //
+ // Log will appear on:
+ // * XCode: Debug Console
+ // * VsCode: Debug Console
+ // * Android Studio: Debug and Logcat Console
+ LogConfig logConfig = LogConfig(logType: LogType.DEBUG);
+ _audioQuery.setLogConfig(logConfig);
+
+ // Check and request for permission.
+ checkAndRequestPermissions();
}
- requestPermission() async {
- // Web platform don't support permissions methods.
- if (!kIsWeb) {
- bool permissionStatus = await _audioQuery.permissionsStatus();
- if (!permissionStatus) {
- await _audioQuery.permissionsRequest();
- }
- setState(() {});
- }
+ checkAndRequestPermissions({bool retry = false}) async {
+ // The param 'retryRequest' is false, by default.
+ _hasPermission = await _audioQuery.checkAndRequest(
+ retryRequest: retry,
+ );
+
+ // Only call update the UI if application has all required permissions.
+ _hasPermission ? setState(() {}) : null;
}
@override
@@ -58,41 +70,73 @@ class _SongsState extends State {
title: const Text("OnAudioQueryExample"),
elevation: 2,
),
- body: FutureBuilder>(
- // Default values:
- future: _audioQuery.querySongs(
- sortType: null,
- orderType: OrderType.ASC_OR_SMALLER,
- uriType: UriType.EXTERNAL,
- ignoreCase: true,
- ),
- builder: (context, item) {
- // Loading content
- if (item.data == null) return const CircularProgressIndicator();
+ body: Center(
+ child: !_hasPermission
+ ? noAccessToLibraryWidget()
+ : FutureBuilder>(
+ // Default values:
+ future: _audioQuery.querySongs(
+ sortType: null,
+ orderType: OrderType.ASC_OR_SMALLER,
+ uriType: UriType.EXTERNAL,
+ ignoreCase: true,
+ ),
+ builder: (context, item) {
+ // Display error, if any.
+ if (item.hasError) {
+ return Text(item.error.toString());
+ }
- // When you try "query" without asking for [READ] or [Library] permission
- // the plugin will return a [Empty] list.
- if (item.data!.isEmpty) return const Text("Nothing found!");
+ // Waiting content.
+ if (item.data == null) {
+ return const CircularProgressIndicator();
+ }
- // You can use [item.data!] direct or you can create a:
- // List songs = item.data!;
- return ListView.builder(
- itemCount: item.data!.length,
- itemBuilder: (context, index) {
- return ListTile(
- title: Text(item.data![index].title),
- subtitle: Text(item.data![index].artist ?? "No Artist"),
- trailing: const Icon(Icons.arrow_forward_rounded),
- // This Widget will query/load image. Just add the id and type.
- // You can use/create your own widget/method using [queryArtwork].
- leading: QueryArtworkWidget(
- id: item.data![index].id,
- type: ArtworkType.AUDIO,
- ),
- );
- },
- );
- },
+ // 'Library' is empty.
+ if (item.data!.isEmpty) return const Text("Nothing found!");
+
+ // You can use [item.data!] direct or you can create a:
+ // List songs = item.data!;
+ return ListView.builder(
+ itemCount: item.data!.length,
+ itemBuilder: (context, index) {
+ return ListTile(
+ title: Text(item.data![index].title),
+ subtitle: Text(item.data![index].artist ?? "No Artist"),
+ trailing: const Icon(Icons.arrow_forward_rounded),
+ // This Widget will query/load image.
+ // You can use/create your own widget/method using [queryArtwork].
+ leading: QueryArtworkWidget(
+ controller: _audioQuery,
+ id: item.data![index].id,
+ type: ArtworkType.AUDIO,
+ ),
+ );
+ },
+ );
+ },
+ ),
+ ),
+ );
+ }
+
+ Widget noAccessToLibraryWidget() {
+ return Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: Colors.redAccent.withOpacity(0.5),
+ ),
+ padding: const EdgeInsets.all(20),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Text("Application doesn't have access to the library"),
+ const SizedBox(height: 10),
+ ElevatedButton(
+ onPressed: () => checkAndRequestPermissions(retry: true),
+ child: const Text("Allow"),
+ ),
+ ],
),
);
}
diff --git a/on_audio_query/example/pubspec.yaml b/on_audio_query/example/pubspec.yaml
index 8b2f107c..9576001a 100644
--- a/on_audio_query/example/pubspec.yaml
+++ b/on_audio_query/example/pubspec.yaml
@@ -43,8 +43,8 @@ flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
- assets:
- - assets/
+ # assets:
+ # - assets/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
diff --git a/on_audio_query/ios/Classes/PluginProvider.swift b/on_audio_query/ios/Classes/PluginProvider.swift
new file mode 100644
index 00000000..9afa6033
--- /dev/null
+++ b/on_audio_query/ios/Classes/PluginProvider.swift
@@ -0,0 +1,46 @@
+/**
+ * A singleton used to define all variables/methods that will be used on all plugin.
+ *
+ * The singleton will provider the ability to 'request' required variables/methods on any moment.
+ *
+ * All variables/methods should be defined after dart request (call/result).
+ */
+enum PluginProvider {
+ private static let ERROR_MESSAGE = "Tried to get one of the methods but the 'PluginProvider' has not been initialized"
+
+ private static var _call: FlutterMethodCall?
+
+ private static var _result: FlutterResult?
+
+ /**
+ * Used to define the current dart request.
+ *
+ * Should be defined/redefined on every [handle] request.
+ */
+ static func set(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
+ PluginProvider._call = call
+ PluginProvider._result = result
+ }
+
+ static func call() throws -> FlutterMethodCall {
+ Log.type.debug(_call == nil)
+ guard _call != nil else {
+ throw PluginProviderException.unitialized(ERROR_MESSAGE)
+ }
+
+ return _call!
+ }
+
+ static func result() throws -> FlutterResult {
+ Log.type.debug(_call == nil)
+ guard _result != nil else {
+ throw PluginProviderException.unitialized(ERROR_MESSAGE)
+ }
+
+ return _result!
+ }
+
+ enum PluginProviderException: Error {
+ case unitialized(String)
+ }
+}
diff --git a/on_audio_query/ios/Classes/SwiftOnAudioQueryPlugin.swift b/on_audio_query/ios/Classes/SwiftOnAudioQueryPlugin.swift
index 449b4bed..f19ee95d 100644
--- a/on_audio_query/ios/Classes/SwiftOnAudioQueryPlugin.swift
+++ b/on_audio_query/ios/Classes/SwiftOnAudioQueryPlugin.swift
@@ -1,55 +1,85 @@
import Flutter
import UIKit
-import MediaPlayer
public class SwiftOnAudioQueryPlugin: NSObject, FlutterPlugin {
+ private static let CHANNEL_NAME: String = "com.lucasjosino.on_audio_query"
+
+ override public init() {
+ Log.setLogLevel(level: .warning)
+ }
// Dart <-> Swift communication.
public static func register(with registrar: FlutterPluginRegistrar) {
- let channel = FlutterMethodChannel(name: "com.lucasjosino.on_audio_query", binaryMessenger: registrar.messenger())
+ Log.type.info("Called register")
+
+ let channel = FlutterMethodChannel(
+ name: "\(CHANNEL_NAME)",
+ binaryMessenger: registrar.messenger()
+ )
let instance = SwiftOnAudioQueryPlugin()
+
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
+ Log.type.debug("Started method call (\(call.method))")
+
+ // Init the plugin provider with current 'call' and 'result'.
+ PluginProvider.set(call, result)
+
+ Log.type.info("Method call: \(call.method)")
switch call.method {
// This is a basic permission handler, will return [true] if has permission and
// [false] if don't.
//
// The others status will be ignored and replaced with [false].
- case "permissionsStatus":
- result(checkPermission())
+ case Method.PERMISSION_STATUS:
+ result(PermissionController.checkPermission())
// The same as [permissionStatus], this is a basic permission handler and will only
// return [true] or [false].
//
// When adding the necessary [permissions] inside [Info.plist], [IOS] will automatically
// request but, in any case, you can call this method.
- case "permissionsRequest":
- MPMediaLibrary.requestAuthorization { status in
- if (status == .authorized) {
- result(true)
- } else {
- result(false)
- }
- }
+ case Method.PERMISSION_REQUEST:
+ result(PermissionController.requestPermission())
// Some basic information about the platform, in this case, [IOS]
// * Model (Only return the "type", like: IPhone, MacOs, IPod..)
// * Version (IOS version)
// * Type (IOS)
- case "queryDeviceInfo":
- queryDeviceInfo(result: result)
+ case Method.QUERY_DEVICE_INFO:
+ let device = UIDevice.current
+ result([
+ "device_model": device.model,
+ "device_sys_type": device.systemName,
+ "device_sys_version": device.systemVersion,
+ ])
+ // Set logging level.
+ case Method.SET_LOG_CONFIG:
+ let args = call.arguments as! [String: Any]
+ let level = args["level"] as! Int
+ Log.setLogLevel(dartLevel: level)
+ result(true)
default:
- //
- OnAudioController(call: call, result: result).chooseMethod()
- }
- }
-
- public func checkPermission() -> Bool {
- let permissionStatus = MPMediaLibrary.authorizationStatus()
- if permissionStatus == MPMediaLibraryAuthorizationStatus.authorized {
- return true
- } else {
- return false
+ Log.type.debug("Checking permissions...")
+
+ let hasPermission = PermissionController.checkPermission()
+ Log.type.debug("Application has permissions: \(hasPermission)")
+
+ if !hasPermission {
+ Log.type.error("The application doesn't have access to the library")
+ result(
+ FlutterError(
+ code: "MissingPermissions",
+ message: "Application doesn't have access to the library",
+ details: "Call the [permissionsRequest] method or install a external plugin to handle the app permission."
+ )
+ )
+ break
+ }
+
+ MethodController().find()
}
+
+ Log.type.debug("Ended method call (\(call.method))")
}
}
diff --git a/on_audio_query/ios/Classes/consts/Method.swift b/on_audio_query/ios/Classes/consts/Method.swift
new file mode 100644
index 00000000..b78a1fb8
--- /dev/null
+++ b/on_audio_query/ios/Classes/consts/Method.swift
@@ -0,0 +1,27 @@
+struct Method {
+ // General methods
+ static let PERMISSION_STATUS = "permissionsStatus"
+ static let PERMISSION_REQUEST = "permissionsRequest"
+ static let QUERY_DEVICE_INFO = "queryDeviceInfo"
+ static let SCAN = "scan"
+ static let SET_LOG_CONFIG = "setLogConfig"
+
+ // Query methods
+ static let QUERY_AUDIOS = "querySongs"
+ static let QUERY_ALBUMS = "queryAlbums"
+ static let QUERY_ARTISTS = "queryArtists"
+ static let QUERY_GENRES = "queryGenres"
+ static let QUERY_PLAYLISTS = "queryPlaylists"
+ static let QUERY_ARTWORK = "queryArtwork"
+ static let QUERY_AUDIOS_FROM = "queryAudiosFrom"
+ static let QUERY_WITH_FILTERS = "queryWithFilters"
+ static let QUERY_ALL_PATHS = "queryAllPath"
+
+ // Playlist methods
+ static let CREATE_PLAYLIST = "createPlaylist"
+ static let REMOVE_PLAYLIST = "removePlaylist"
+ static let ADD_TO_PLAYLIST = "addToPlaylist"
+ static let REMOVE_FROM_PLAYLIST = "removeFromPlaylist"
+ static let RENAME_PLAYLIST = "renamePlaylist"
+ static let MOVE_ITEM_TO = "moveItemTo"
+}
diff --git a/on_audio_query/ios/Classes/controller/OnAudioController.swift b/on_audio_query/ios/Classes/controller/OnAudioController.swift
deleted file mode 100644
index 0c728e3a..00000000
--- a/on_audio_query/ios/Classes/controller/OnAudioController.swift
+++ /dev/null
@@ -1,52 +0,0 @@
-import Flutter
-
-public class OnAudioController {
- var call: FlutterMethodCall
- var result: FlutterResult
-
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- self.call = call
- self.result = result
- }
-
-
- //This method will sort call according to request.
- public func chooseMethod() {
- // All necessary method to this plugin support both platforms, only playlists
- // are limited when using [IOS].
- switch call.method {
- case "querySongs":
- OnAudioQuery(call: call, result: result).querySongs()
- case "queryAlbums":
- OnAlbumsQuery(call: call, result: result).queryAlbums()
- case "queryArtists":
- OnArtistsQuery(call: call, result: result).queryArtists()
- case "queryGenres":
- OnGenresQuery(call: call, result: result).queryGenres()
- case "queryPlaylists":
- OnPlaylistsQuery(call: call, result: result).queryPlaylists()
- case "queryAudiosFrom":
- OnAudiosFromQuery(call: call, result: result).queryAudiosFrom()
- case "queryWithFilters":
- OnWithFiltersQuery(call: call, result: result).queryWithFilters()
- case "queryArtwork":
- OnArtworkQuery(call: call, result: result).queryArtwork()
- // The playlist for [IOS] is completely limited, the developer can only:
- // * Create playlist
- // * Add item to playlist (Unsuported, for now)
- //
- // Missing methods:
- // * Rename playlist
- // * Remove playlist
- // * Remove item from playlist
- // * Move item inside playlist
- case "createPlaylist":
- OnPlaylistsController(call: call, result: result).createPlaylist()
- case "addToPlaylist":
- OnPlaylistsController(call: call, result: result).addToPlaylist()
- default:
- // All non suported methods will throw this error.
- result(FlutterMethodNotImplemented)
- }
- }
-}
diff --git a/on_audio_query/ios/Classes/controller/OnPlaylistsController.swift b/on_audio_query/ios/Classes/controller/OnPlaylistsController.swift
deleted file mode 100644
index d99ea104..00000000
--- a/on_audio_query/ios/Classes/controller/OnPlaylistsController.swift
+++ /dev/null
@@ -1,86 +0,0 @@
-import Flutter
-import MediaPlayer
-
-class OnPlaylistsController {
- var args: [String: Any]
- var result: FlutterResult
-
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- self.args = call.arguments as! [String: Any]
- self.result = result
- }
-
- // Due the [IOS] limitation, for now we can only create/add to playlists.
- func createPlaylist() {
- // The name, author and description for playlist, playlistName cannot be null.
- let playlistName = args["playlistName"] as! String
- let playlistAuthor = args["playlistAuthor"] as? String
- let playlistDesc = args["playlistDesc"] as? String
-
- //
- let playlistMetadata = MPMediaPlaylistCreationMetadata.init(name: playlistName)
- playlistMetadata.authorDisplayName = playlistAuthor
- playlistMetadata.descriptionText = playlistDesc ?? ""
-
- //
- MPMediaLibrary().getPlaylist(with: UUID.init(), creationMetadata: playlistMetadata, completionHandler: { playlist, error in
- //A little second to create the playlist and Flutter UI update
- sleep(1)
- if playlist != nil {
- self.result(true)
- } else {
- print(error ?? "Something wrong happend")
- self.result(false)
- }
- }
- )
- }
-
- func addToPlaylist() {
- //
- let playlistId = args["playlistId"] as! Int
- let audioId = args["audioId"] as! Int
-
- // TODO: Use another method to get UUID from playlist
- //
- // Link: https://github.com/HumApp/MusicKit/blob/master/AppleMusicSample/Controllers/MediaLibraryManager.swift
- let playlist: MPMediaPlaylist? = loadPlaylist(id: playlistId)
-
- // [addItem] won't work in the main thread.
- DispatchQueue.global(qos: .userInitiated).async {
- var hasAdded: Bool = false
-
- // If playlist is null, just return [false].
- if playlist != nil {
- playlist!.addItem(withProductID: String(audioId), completionHandler: { error in
- if error == nil {
- hasAdded = true
- } else {
- hasAdded = false
- // TODO: Fix "NSLocalizedDescription=The requested operation is not enabled for this device."
- print("on_audio_error: " + error.debugDescription)
- }
- })
- } else {
- hasAdded = false
- }
-
- DispatchQueue.main.async {
- self.result(hasAdded)
- }
- }
- }
-
- private func loadPlaylist(id: Int) -> MPMediaPlaylist? {
- let cursor = MPMediaQuery.playlists()
-
- //
- let playlistFilter = MPMediaPropertyPredicate.init(value: id, forProperty: MPMediaPlaylistPropertyPersistentID)
- let noCloudItemFilter = MPMediaPropertyPredicate.init(value: false, forProperty: MPMediaItemPropertyIsCloudItem)
- cursor.addFilterPredicate(playlistFilter)
- cursor.addFilterPredicate(noCloudItemFilter)
-
- let firstPlaylist = cursor.collections?.first as? MPMediaPlaylist
- return firstPlaylist
- }
-}
diff --git a/on_audio_query/ios/Classes/controllers/MethodController.swift b/on_audio_query/ios/Classes/controllers/MethodController.swift
new file mode 100644
index 00000000..fce2543d
--- /dev/null
+++ b/on_audio_query/ios/Classes/controllers/MethodController.swift
@@ -0,0 +1,45 @@
+import Flutter
+
+public class MethodController {
+ public func find() {
+ let call = try! PluginProvider.call()
+ let result = try! PluginProvider.result()
+
+ // All necessary method to this plugin support both platforms, only playlists
+ // are limited when using [IOS].
+ switch call.method {
+ case Method.QUERY_AUDIOS:
+ AudioQuery().querySongs()
+ case Method.QUERY_ALBUMS:
+ AlbumQuery().queryAlbums()
+ case Method.QUERY_ARTISTS:
+ ArtistQuery().queryArtists()
+ case Method.QUERY_GENRES:
+ GenreQuery().queryGenres()
+ case Method.QUERY_PLAYLISTS:
+ PlaylistQuery().queryPlaylists()
+ case Method.QUERY_AUDIOS_FROM:
+ AudioFromQuery().queryAudiosFrom()
+ case Method.QUERY_WITH_FILTERS:
+ WithFiltersQuery().queryWithFilters()
+ case Method.QUERY_ARTWORK:
+ ArtworkQuery().queryArtwork()
+ // The playlist for [IOS] is completely limited, the developer can only:
+ // * Create playlist
+ // * Add item to playlist (Unsuported, for now)
+ //
+ // Missing methods:
+ // * Rename playlist
+ // * Remove playlist
+ // * Remove item from playlist
+ // * Move item inside playlist
+ case Method.CREATE_PLAYLIST:
+ PlaylistController().createPlaylist()
+ case Method.ADD_TO_PLAYLIST:
+ PlaylistController().addToPlaylist()
+ default:
+ // All non suported methods will throw this error.
+ result(FlutterMethodNotImplemented)
+ }
+ }
+}
diff --git a/on_audio_query/ios/Classes/controllers/PermissionController.swift b/on_audio_query/ios/Classes/controllers/PermissionController.swift
new file mode 100644
index 00000000..37b145a2
--- /dev/null
+++ b/on_audio_query/ios/Classes/controllers/PermissionController.swift
@@ -0,0 +1,25 @@
+import MediaPlayer
+
+class PermissionController {
+ public static func checkPermission() -> Bool {
+ let permissionStatus = MPMediaLibrary.authorizationStatus()
+ if permissionStatus == MPMediaLibraryAuthorizationStatus.authorized {
+ return true
+ } else {
+ return false
+ }
+ }
+
+ public static func requestPermission() -> Bool {
+ Log.type.debug("Requesting permissions.")
+ Log.type.debug("iOS Version: \(ProcessInfo().operatingSystemVersion.majorVersion)")
+
+ var isPermissionGranted: Bool = false
+ MPMediaLibrary.requestAuthorization { status in
+ isPermissionGranted = status == .authorized
+ Log.type.debug("Permission accepted: \(isPermissionGranted)")
+ }
+
+ return isPermissionGranted
+ }
+}
diff --git a/on_audio_query/ios/Classes/controllers/PlaylistController.swift b/on_audio_query/ios/Classes/controllers/PlaylistController.swift
new file mode 100644
index 00000000..5fbd4269
--- /dev/null
+++ b/on_audio_query/ios/Classes/controllers/PlaylistController.swift
@@ -0,0 +1,89 @@
+import Flutter
+import MediaPlayer
+
+class PlaylistController {
+ var args: [String: Any]
+ var result: FlutterResult
+
+ init() {
+ self.args = try! PluginProvider.call().arguments as! [String: Any]
+ self.result = try! PluginProvider.result()
+ }
+
+ func createPlaylist() {
+ let playlistName = args["playlistName"] as! String
+ let playlistAuthor = args["playlistAuthor"] as? String
+ let playlistDesc = args["playlistDesc"] as? String
+
+ Log.type.debug("Playlist info: ")
+ Log.type.debug("\tname: \(playlistName)")
+ Log.type.debug("\tauthor: \(String(describing: playlistAuthor))")
+ Log.type.debug("\tdescription: \(String(describing: playlistDesc))")
+
+ let playlistMetadata = MPMediaPlaylistCreationMetadata(name: playlistName)
+ playlistMetadata.authorDisplayName = playlistAuthor
+ playlistMetadata.descriptionText = playlistDesc ?? ""
+
+ MPMediaLibrary().getPlaylist(with: UUID(), creationMetadata: playlistMetadata, completionHandler: { playlist, _ in
+ sleep(1)
+
+ let playlistHasBeenCreated = playlist != nil
+ Log.type.debug("Playlist has been created: \(playlistHasBeenCreated)")
+
+ self.result(playlistHasBeenCreated)
+ })
+ }
+
+ func addToPlaylist() {
+ let playlistId = args["playlistId"] as! Int
+ let audioId = args["audioId"] as! Int
+
+ Log.type.debug("Playlist info: ")
+ Log.type.debug("\tid: \(playlistId)")
+ Log.type.debug("\taudioId: \(audioId)")
+
+ // TODO: Use another method to get UUID from playlist
+ //
+ // Link: https://github.com/HumApp/MusicKit/blob/master/AppleMusicSample/Controllers/MediaLibraryManager.swift
+ let playlist: MPMediaPlaylist? = loadPlaylist(id: playlistId)
+
+ // [addItem] won't work in the main thread.
+ DispatchQueue.global(qos: .userInitiated).async {
+ var hasBeenAdded = false
+
+ if playlist != nil {
+ Log.type.debug("Found playlist! Name: \(playlist?.name ?? "Unknown")")
+
+ playlist!.addItem(withProductID: String(audioId), completionHandler: { error in
+ let hasError = error != nil
+
+ if hasError {
+ Log.type.error(error.debugDescription)
+ }
+
+ hasBeenAdded = !hasError
+ })
+ }
+
+ DispatchQueue.main.async {
+ Log.type.debug("Item (\(audioId)) has been added: \(hasBeenAdded)")
+ self.result(hasBeenAdded)
+ }
+ }
+ }
+
+ private func loadPlaylist(id: Int) -> MPMediaPlaylist? {
+ let cursor = MPMediaQuery.playlists()
+
+ // Create a filter using the playlist id.
+ let playlistFilter = MPMediaPropertyPredicate(value: id, forProperty: MPMediaPlaylistPropertyPersistentID)
+
+ // Remove any cloud playlist.
+ let noCloudItemFilter = MPMediaPropertyPredicate(value: false, forProperty: MPMediaItemPropertyIsCloudItem)
+
+ cursor.addFilterPredicate(playlistFilter)
+ cursor.addFilterPredicate(noCloudItemFilter)
+
+ return cursor.collections?.first as? MPMediaPlaylist
+ }
+}
diff --git a/on_audio_query/ios/Classes/queries/AlbumQuery.swift b/on_audio_query/ios/Classes/queries/AlbumQuery.swift
new file mode 100644
index 00000000..82c45b55
--- /dev/null
+++ b/on_audio_query/ios/Classes/queries/AlbumQuery.swift
@@ -0,0 +1,58 @@
+import Flutter
+import MediaPlayer
+
+class AlbumQuery {
+ var args: [String: Any]
+ var result: FlutterResult
+
+ init() {
+ self.args = try! PluginProvider.call().arguments as! [String: Any]
+ self.result = try! PluginProvider.result()
+ }
+
+ func queryAlbums() {
+ let sortType = args["sortType"] as? Int ?? 0
+
+ let cursor = MPMediaQuery.albums()
+
+ // Using native sort from [IOS] you can only use the [Album] and [Artist].
+ // The others will be sorted "manually" using [formatAlbumList] before
+ // send to Dart.
+ cursor.groupingType = checkSongSortType(sortType: sortType)
+
+ // Filter to avoid audios/songs from cloud library.
+ let cloudFilter = MPMediaPropertyPredicate(
+ value: false,
+ forProperty: MPMediaItemPropertyIsCloudItem
+ )
+ cursor.addFilterPredicate(cloudFilter)
+
+ Log.type.debug("Query config: ")
+ Log.type.debug("\tsortType: \(sortType)")
+
+ // Query everything in background for a better performance.
+ loadAlbums(cursor: cursor.collections)
+ }
+
+ private func loadAlbums(cursor: [MPMediaItemCollection]!) {
+ DispatchQueue.global(qos: .userInitiated).async {
+ var listOfAlbums: [[String: Any?]] = Array()
+
+ // For each item(album) inside this "cursor", take one and "format"
+ // into a [Map], all keys are based on [Android]
+ // platforms.
+ for album in cursor {
+ if !album.items[0].isCloudItem, album.items[0].assetURL != nil {
+ let albumData = loadAlbumItem(album: album)
+ listOfAlbums.append(albumData)
+ }
+ }
+
+ DispatchQueue.main.async {
+ // Custom sort/order.
+ let finalList = formatAlbumList(args: self.args, allAlbums: listOfAlbums)
+ self.result(finalList)
+ }
+ }
+ }
+}
diff --git a/on_audio_query/ios/Classes/queries/ArtistQuery.swift b/on_audio_query/ios/Classes/queries/ArtistQuery.swift
new file mode 100644
index 00000000..b1f43d5b
--- /dev/null
+++ b/on_audio_query/ios/Classes/queries/ArtistQuery.swift
@@ -0,0 +1,51 @@
+import Flutter
+import MediaPlayer
+
+class ArtistQuery {
+ var args: [String: Any]
+ var result: FlutterResult
+
+ init() {
+ self.args = try! PluginProvider.call().arguments as! [String: Any]
+ self.result = try! PluginProvider.result()
+ }
+
+ func queryArtists() {
+ let cursor = MPMediaQuery.artists()
+
+ // We don't need to define a sortType here. [IOS] only support
+ // the [Artist]. The others will be sorted "manually" using
+ // [formatSongList] before send to Dart.
+
+ // Filter to avoid audios/songs from cloud library.
+ let cloudFilter = MPMediaPropertyPredicate(
+ value: false,
+ forProperty: MPMediaItemPropertyIsCloudItem
+ )
+ cursor.addFilterPredicate(cloudFilter)
+
+ loadArtists(cursor: cursor.collections)
+ }
+
+ private func loadArtists(cursor: [MPMediaItemCollection]!) {
+ DispatchQueue.global(qos: .userInitiated).async {
+ var listOfArtists: [[String: Any?]] = Array()
+
+ // For each item(artist) inside this "cursor", take one and "format"
+ // into a [Map], all keys are based on [Android]
+ // platforms.
+ for artist in cursor {
+ if !artist.items[0].isCloudItem, artist.items[0].assetURL != nil {
+ let artistData = loadArtistItem(artist: artist)
+ listOfArtists.append(artistData)
+ }
+ }
+
+ DispatchQueue.main.async {
+ // Custom sort/order.
+ let finalList = formatArtistList(args: self.args, allArtists: listOfArtists)
+ self.result(finalList)
+ }
+ }
+ }
+}
diff --git a/on_audio_query/ios/Classes/queries/ArtworkQuery.swift b/on_audio_query/ios/Classes/queries/ArtworkQuery.swift
new file mode 100644
index 00000000..be56f6a8
--- /dev/null
+++ b/on_audio_query/ios/Classes/queries/ArtworkQuery.swift
@@ -0,0 +1,127 @@
+import Flutter
+import MediaPlayer
+
+class ArtworkQuery {
+ var args: [String: Any]
+ var result: FlutterResult
+
+ init() {
+ self.args = try! PluginProvider.call().arguments as! [String: Any]
+ self.result = try! PluginProvider.result()
+ }
+
+ // [IOS] has a different artwork system and you can "query" using normal "querySongs, .."
+ // [Android] can't "query" artwork at the same time as "querySongs", so we need to "query"
+ // using a different method(queryArtwork).
+ //
+ // To match both [IOS] and [Android], [queryArtwork] is the only way to get artwork.
+ //
+ // Not the best solution but, at least here we can select differents formats and size.
+ func queryArtwork() {
+ // The 'id' of the [Song] or [Album].
+ let id = args["id"] as! Int
+
+ // The 'size' of the image.
+ let size = args["size"] as! Int
+
+ // The 'size' of the image.
+ var quality = args["quality"] as! Int
+ if quality > 100 {
+ quality = 50
+ }
+
+ // The format
+ // * 0 -> JPEG
+ // * 1 -> PNG
+ let format = args["format"] as! Int
+
+ var cursor: MPMediaQuery?
+ var filter: MPMediaPropertyPredicate?
+
+ let uri = args["type"] as! Int
+ switch uri {
+ case 0:
+ filter = MPMediaPropertyPredicate(value: id, forProperty: MPMediaItemPropertyPersistentID)
+ cursor = MPMediaQuery.songs()
+ case 1:
+ filter = MPMediaPropertyPredicate(value: id, forProperty: MPMediaItemPropertyAlbumPersistentID)
+ cursor = MPMediaQuery.albums()
+ case 2:
+ filter = MPMediaPropertyPredicate(value: id, forProperty: MPMediaPlaylistPropertyPersistentID)
+ cursor = MPMediaQuery.playlists()
+ case 3:
+ filter = MPMediaPropertyPredicate(value: id, forProperty: MPMediaItemPropertyArtistPersistentID)
+ cursor = MPMediaQuery.artists()
+ case 4:
+ filter = MPMediaPropertyPredicate(value: id, forProperty: MPMediaItemPropertyGenrePersistentID)
+ cursor = MPMediaQuery.genres()
+ default:
+ filter = nil
+ cursor = nil
+ }
+
+ if cursor == nil || filter == nil {
+ Log.type.warning("Cursor or filter has null value!")
+ result(nil)
+ return
+ }
+
+ Log.type.debug("Query config: ")
+ Log.type.debug("\tid: \(id)")
+ Log.type.debug("\tsize: \(size)")
+ Log.type.debug("\tquality: \(quality)")
+ Log.type.debug("\tformat: \(format)")
+ Log.type.debug("\turi: \(uri)")
+ Log.type.debug("\tfilter: \(String(describing: filter))")
+
+ cursor!.addFilterPredicate(filter!)
+
+ // Filter to avoid audios/songs from cloud library.
+ let cloudFilter = MPMediaPropertyPredicate(
+ value: false,
+ forProperty: MPMediaItemPropertyIsCloudItem
+ )
+ cursor?.addFilterPredicate(cloudFilter)
+
+ // Query everything in background for a better performance.
+ loadArtwork(cursor: cursor, size: size, format: format, uri: uri, quality: quality)
+ }
+
+ private func loadArtwork(cursor: MPMediaQuery!, size: Int, format: Int, uri: Int, quality: Int) {
+ DispatchQueue.global(qos: .userInitiated).async {
+ var artwork: Data?
+ var item: MPMediaItem?
+ let convertedQuality = CGFloat(Double(quality) / 100.0)
+
+ // 'uri' == 0 -> artwork is from [Song]
+ // 'uri' == 1, 2 or 3 -> artwork is from [Album], [Playlist] or [Artist]
+ if uri == 0 {
+ item = cursor!.items?.first
+ } else {
+ item = cursor!.collections?.first?.items[0]
+ }
+
+ let cgSize = CGSize(width: size, height: size)
+ let image: UIImage? = item?.artwork?.image(at: cgSize)
+
+ // 'format' == 0 -> JPEG
+ // 'format' == 1 -> PNG
+ if format == 0 {
+ artwork = image?.jpegData(compressionQuality: convertedQuality)
+ } else {
+ // [PNG] format will return a high quality image.
+ artwork = image?.pngData()
+ }
+
+ DispatchQueue.main.async {
+ // Avoid "empty" or broken image.
+ if artwork != nil, artwork!.isEmpty {
+ Log.type.info("Item (\(item?.persistentID ?? 0)) has a null or empty artwork")
+ artwork = nil
+ }
+
+ self.result(artwork)
+ }
+ }
+ }
+}
diff --git a/on_audio_query/ios/Classes/query/OnAudiosFromQuery.swift b/on_audio_query/ios/Classes/queries/AudioFromQuery.swift
similarity index 63%
rename from on_audio_query/ios/Classes/query/OnAudiosFromQuery.swift
rename to on_audio_query/ios/Classes/queries/AudioFromQuery.swift
index ba8139eb..8b4aa069 100644
--- a/on_audio_query/ios/Classes/query/OnAudiosFromQuery.swift
+++ b/on_audio_query/ios/Classes/queries/AudioFromQuery.swift
@@ -1,20 +1,19 @@
import Flutter
import MediaPlayer
-class OnAudiosFromQuery {
+class AudioFromQuery {
var args: [String: Any]
var result: FlutterResult
var type: Int = -1
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- // To make life easy, add all arguments inside a map.
- self.args = call.arguments as! [String: Any]
- self.result = result
+ init() {
+ self.args = try! PluginProvider.call().arguments as! [String: Any]
+ self.result = try! PluginProvider.result()
}
func queryAudiosFrom() {
//
- self.type = args["type"] as! Int
+ type = args["type"] as! Int
let wh3re = args["where"] as Any
// The sortType.
let sortType = args["sortType"] as? Int ?? 0
@@ -27,37 +26,29 @@ class OnAudiosFromQuery {
// send to Dart.
cursor?.groupingType = checkSongSortType(sortType: sortType)
- // We cannot "query" without permission so, just return a empty list.
- let hasPermission = SwiftOnAudioQueryPlugin().checkPermission()
- if hasPermission {
- // Here we'll check if the request is to [Playlist] or other.
- if self.type != 6 && cursor != nil {
+ // Here we'll check if the request is to [Playlist] or other.
+ if type != 6, cursor != nil {
+ // This filter will avoid audios/songs outside phone library(cloud).
+ let cloudFilter = MPMediaPropertyPredicate(
+ value: false,
+ forProperty: MPMediaItemPropertyIsCloudItem
+ )
+ cursor?.addFilterPredicate(cloudFilter)
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
- value: false,
- forProperty: MPMediaItemPropertyIsCloudItem
- )
- cursor?.addFilterPredicate(cloudFilter)
-
- // Query everything in background for a better performance.
- loadQueryAudiosFrom(cursor: cursor!)
- } else {
- // Query everything in background for a better performance.
- cursor = MPMediaQuery.playlists()
+ // Query everything in background for a better performance.
+ loadQueryAudiosFrom(cursor: cursor!)
+ } else {
+ // Query everything in background for a better performance.
+ cursor = MPMediaQuery.playlists()
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
- value: false,
- forProperty: MPMediaItemPropertyIsCloudItem
- )
- cursor?.addFilterPredicate(cloudFilter)
+ // This filter will avoid audios/songs outside phone library(cloud).
+ let cloudFilter = MPMediaPropertyPredicate(
+ value: false,
+ forProperty: MPMediaItemPropertyIsCloudItem
+ )
+ cursor?.addFilterPredicate(cloudFilter)
- loadSongsFromPlaylist(cursor: cursor!.collections)
- }
- } else {
- // There's no permission so, return empty to avoid crashes.
- result([])
+ loadSongsFromPlaylist(cursor: cursor!.collections)
}
}
@@ -70,7 +61,7 @@ class OnAudiosFromQuery {
// platforms so, if you change some key, will have to change the [Android] too.
for song in cursor.items! {
// If the song file don't has a assetURL, is a Cloud item.
- if !song.isCloudItem && song.assetURL != nil {
+ if !song.isCloudItem, song.assetURL != nil {
let songData = loadSongItem(song: song)
listOfSongs.append(songData)
}
@@ -86,7 +77,7 @@ class OnAudiosFromQuery {
}
}
- //Add a better code
+ // Add a better code
private func loadSongsFromPlaylist(cursor: [MPMediaItemCollection]!) {
DispatchQueue.global(qos: .userInitiated).async {
var listOfSongs: [[String: Any?]] = Array()
@@ -102,26 +93,26 @@ class OnAudiosFromQuery {
for playlist in cursor {
let iPlaylist = playlist as! MPMediaPlaylist
let iWhere = self.args["where"] as Any
- //Using this check we can define if [where] is the [Playlist] name or id
+ // Using this check we can define if [where] is the [Playlist] name or id
if iWhere is String {
- //Check if playlist name is equal to defined name
+ // Check if playlist name is equal to defined name
if iPlaylist.name == iWhere as? String {
- //For each song, format and add to the list
+ // For each song, format and add to the list
for song in playlist.items {
// If the song file don't has a assetURL, is a Cloud item.
- if !song.isCloudItem && song.assetURL != nil {
+ if !song.isCloudItem, song.assetURL != nil {
let songData = loadSongItem(song: song)
listOfSongs.append(songData)
}
}
}
} else {
- //Check if playlist id is equal to defined id
+ // Check if playlist id is equal to defined id
if iPlaylist.persistentID == iWhere as! Int {
- //For each song, format and add to the list
+ // For each song, format and add to the list
for song in playlist.items {
// If the song file don't has a assetURL, is a Cloud item.
- if !song.isCloudItem && song.assetURL != nil {
+ if !song.isCloudItem, song.assetURL != nil {
let songData = loadSongItem(song: song)
listOfSongs.append(songData)
}
diff --git a/on_audio_query/ios/Classes/query/OnAudioQuery.swift b/on_audio_query/ios/Classes/queries/AudioQuery.swift
similarity index 51%
rename from on_audio_query/ios/Classes/query/OnAudioQuery.swift
rename to on_audio_query/ios/Classes/queries/AudioQuery.swift
index ec091825..c3c9453f 100644
--- a/on_audio_query/ios/Classes/query/OnAudioQuery.swift
+++ b/on_audio_query/ios/Classes/queries/AudioQuery.swift
@@ -1,43 +1,37 @@
import Flutter
import MediaPlayer
-class OnAudioQuery {
+class AudioQuery {
var args: [String: Any]
var result: FlutterResult
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- // To make life easy, add all arguments inside a map.
- self.args = call.arguments as! [String: Any]
- self.result = result
+ init() {
+ self.args = try! PluginProvider.call().arguments as! [String: Any]
+ self.result = try! PluginProvider.result()
}
func querySongs() {
- // The sortType.
let sortType = args["sortType"] as? Int ?? 0
- // Choose the type(To match android side, let's call "cursor").
let cursor = MPMediaQuery.songs()
+
// Using native sort from [IOS] you can only use the [Title], [Album] and
// [Artist]. The others will be sorted "manually" using [formatSongList] before
// send to Dart.
cursor.groupingType = checkSongSortType(sortType: sortType)
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
+ // Filter to avoid audios/songs from cloud library.
+ let cloudFilter = MPMediaPropertyPredicate(
value: false,
forProperty: MPMediaItemPropertyIsCloudItem
)
cursor.addFilterPredicate(cloudFilter)
- // We cannot "query" without permission so, just return a empty list.
- let hasPermission = SwiftOnAudioQueryPlugin().checkPermission()
- if hasPermission {
- // Query everything in background for a better performance.
- loadSongs(cursor: cursor)
- } else {
- // There's no permission so, return empty to avoid crashes.
- result([])
- }
+ Log.type.debug("Query config: ")
+ Log.type.debug("\tsortType: \(sortType)")
+
+ // Query everything in background for a better performance.
+ loadSongs(cursor: cursor)
}
private func loadSongs(cursor: MPMediaQuery!) {
@@ -46,19 +40,17 @@ class OnAudioQuery {
// For each item(song) inside this "cursor", take one and "format"
// into a [Map], all keys are based on [Android]
- // platforms so, if you change some key, will have to change the [Android] too.
+ // platforms.
for song in cursor.items! {
- // If the song file don't has a assetURL, is a Cloud item.
- if !song.isCloudItem && song.assetURL != nil {
+ // Ignore cloud items.
+ if !song.isCloudItem, song.assetURL != nil {
let songData = loadSongItem(song: song)
listOfSongs.append(songData)
}
}
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
DispatchQueue.main.async {
- // Here we'll check the "custom" sort and define a order to the list.
+ // Custom sort/order.
let finalList = formatSongList(args: self.args, allSongs: listOfSongs)
self.result(finalList)
}
diff --git a/on_audio_query/ios/Classes/query/OnGenresQuery.swift b/on_audio_query/ios/Classes/queries/GenreQuery.swift
similarity index 55%
rename from on_audio_query/ios/Classes/query/OnGenresQuery.swift
rename to on_audio_query/ios/Classes/queries/GenreQuery.swift
index 1b92d9ae..10f58567 100644
--- a/on_audio_query/ios/Classes/query/OnGenresQuery.swift
+++ b/on_audio_query/ios/Classes/queries/GenreQuery.swift
@@ -1,40 +1,31 @@
import Flutter
import MediaPlayer
-class OnGenresQuery {
+class GenreQuery {
var args: [String: Any]
var result: FlutterResult
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- // To make life easy, add all arguments inside a map.
- self.args = call.arguments as! [String: Any]
- self.result = result
+ init() {
+ self.args = try! PluginProvider.call().arguments as! [String: Any]
+ self.result = try! PluginProvider.result()
}
func queryGenres() {
- // Choose the type(To match android side, let's call "cursor").
let cursor = MPMediaQuery.genres()
// We don't need to define a sortType here. [IOS] only support
// the [Artist]. The others will be sorted "manually" using
// [formatSongList] before send to Dart.
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
+ // Filter to avoid audios/songs from cloud library.
+ let cloudFilter = MPMediaPropertyPredicate(
value: false,
forProperty: MPMediaItemPropertyIsCloudItem
)
cursor.addFilterPredicate(cloudFilter)
- // We cannot "query" without permission so, just return a empty list.
- let hasPermission = SwiftOnAudioQueryPlugin().checkPermission()
- if hasPermission {
- // Query everything in background for a better performance.
- loadGenres(cursor: cursor.collections)
- } else {
- // There's no permission so, return empty to avoid crashes.
- result([])
- }
+ // Query everything in background for a better performance.
+ loadGenres(cursor: cursor.collections)
}
private func loadGenres(cursor: [MPMediaItemCollection]!) {
@@ -43,9 +34,9 @@ class OnGenresQuery {
// For each item(genre) inside this "cursor", take one and "format"
// into a [Map], all keys are based on [Android]
- // platforms so, if you change some key, will have to change the [Android] too.
+ // platforms.
for genre in cursor {
- if !genre.items[0].isCloudItem && genre.items[0].assetURL != nil {
+ if !genre.items[0].isCloudItem, genre.items[0].assetURL != nil {
var genreData = loadGenreItem(genre: genre)
// Count and add the number of songs for every genre.
@@ -56,10 +47,8 @@ class OnGenresQuery {
}
}
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
DispatchQueue.main.async {
- // Here we'll check the "custom" sort and define a order to the list.
+ // Custom sort/order.
let finalList = formatGenreList(args: self.args, allGenres: listOfGenres)
self.result(finalList)
}
diff --git a/on_audio_query/ios/Classes/query/OnPlaylistsQuery.swift b/on_audio_query/ios/Classes/queries/PlaylistQuery.swift
similarity index 53%
rename from on_audio_query/ios/Classes/query/OnPlaylistsQuery.swift
rename to on_audio_query/ios/Classes/queries/PlaylistQuery.swift
index 90614dd6..baa1025b 100644
--- a/on_audio_query/ios/Classes/query/OnPlaylistsQuery.swift
+++ b/on_audio_query/ios/Classes/queries/PlaylistQuery.swift
@@ -1,38 +1,29 @@
import Flutter
import MediaPlayer
-class OnPlaylistsQuery {
+class PlaylistQuery {
var args: [String: Any]
var result: FlutterResult
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- // To make life easy, add all arguments inside a map.
- self.args = call.arguments as! [String: Any]
- self.result = result
+ init() {
+ self.args = try! PluginProvider.call().arguments as! [String: Any]
+ self.result = try! PluginProvider.result()
}
func queryPlaylists() {
- // Choose the type(To match android side, let's call "cursor").
let cursor = MPMediaQuery.playlists()
// TODO: Add sort type to [queryPlaylists].
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
+ // Filter to avoid audios/songs from cloud library.
+ let cloudFilter = MPMediaPropertyPredicate(
value: false,
forProperty: MPMediaItemPropertyIsCloudItem
)
cursor.addFilterPredicate(cloudFilter)
- // We cannot "query" without permission so, just return a empty list.
- let hasPermission = SwiftOnAudioQueryPlugin().checkPermission()
- if hasPermission {
- // Query everything in background for a better performance.
- loadPlaylists(cursor: cursor.collections)
- } else {
- // There's no permission so, return empty to avoid crashes.
- result([])
- }
+ // Query everything in background for a better performance.
+ loadPlaylists(cursor: cursor.collections)
}
private func loadPlaylists(cursor: [MPMediaItemCollection]!) {
@@ -41,10 +32,10 @@ class OnPlaylistsQuery {
// For each item(playlist) inside this "cursor", take one and "format"
// into a [Map], all keys are based on [Android]
- // platforms so, if you change some key, will have to change the [Android] too.
+ // platforms.
for playlist in cursor {
- // If the first song file don't has a assetURL, is a Cloud item.
- if !playlist.items[0].isCloudItem && playlist.items[0].assetURL != nil {
+ // Ignore cloud items.
+ if !playlist.items[0].isCloudItem, playlist.items[0].assetURL != nil {
var playlistData = loadPlaylistItem(playlist: playlist)
// Count and add the number of songs for every genre.
@@ -55,8 +46,6 @@ class OnPlaylistsQuery {
}
}
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
DispatchQueue.main.async {
// TODO: Add sort type to [queryPlaylists].
self.result(listOfPlaylists)
diff --git a/on_audio_query/ios/Classes/query/OnWithFiltersQuery.swift b/on_audio_query/ios/Classes/queries/WithFiltersQuery.swift
similarity index 62%
rename from on_audio_query/ios/Classes/query/OnWithFiltersQuery.swift
rename to on_audio_query/ios/Classes/queries/WithFiltersQuery.swift
index 7e3c7cf8..ad0ab30c 100644
--- a/on_audio_query/ios/Classes/query/OnWithFiltersQuery.swift
+++ b/on_audio_query/ios/Classes/queries/WithFiltersQuery.swift
@@ -1,28 +1,29 @@
import Flutter
import MediaPlayer
-class OnWithFiltersQuery {
+class WithFiltersQuery {
var args: [String: Any]
var result: FlutterResult
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- // To make life easy, add all arguments inside a map.
- self.args = call.arguments as! [String: Any]
- self.result = result
+ init() {
+ self.args = try! PluginProvider.call().arguments as! [String: Any]
+ self.result = try! PluginProvider.result()
}
func queryWithFilters() {
// None of this arguments can be null.
// The [type] will be used to define where item will be queried.
let withType = args["withType"] as! Int
+
// The [arg] will be used to define the "search".
let arg = args["args"] as! Int
+
// The [argVal] is the "name" to the "search"
let argVal = args["argsVal"] as! String
// (To match android side, let's call "cursor").
- var cursor: MPMediaQuery? = nil
- var filter: MPMediaPropertyPredicate? = nil
+ var cursor: MPMediaQuery?
+ var filter: MPMediaPropertyPredicate?
// Use the [type] to define the query.
switch withType {
@@ -46,38 +47,37 @@ class OnWithFiltersQuery {
break
}
- // We cannot "query" without permission so, just return a empty list.
- let hasPermission = SwiftOnAudioQueryPlugin().checkPermission()
- if hasPermission {
- // Choose between query [Playlist] or others.
- if filter != nil && withType != 2 {
- // Add the filter.
- cursor?.addFilterPredicate(filter!)
-
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
- value: false,
- forProperty: MPMediaItemPropertyIsCloudItem
- )
- cursor?.addFilterPredicate(cloudFilter)
-
- // Query everything in background for a better performance.
- loadItemsWithFilter(cursor: cursor!, type: withType)
- } else {
+ Log.type.debug("Query config: ")
+ Log.type.debug("\twithType: \(withType)")
+ Log.type.debug("\targ: \(arg)")
+ Log.type.debug("\targVal: \(argVal)")
+ Log.type.debug("\tcursor: \(String(describing: cursor))")
+ Log.type.debug("\tfilter: \(String(describing: filter))")
+
+ // Choose between query [Playlist] or others.
+ if filter != nil, withType != 2 {
+ // Add the filter.
+ cursor?.addFilterPredicate(filter!)
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
- value: false,
- forProperty: MPMediaItemPropertyIsCloudItem
- )
- cursor?.addFilterPredicate(cloudFilter)
+ // Ignore cloud items.
+ let cloudFilter = MPMediaPropertyPredicate(
+ value: false,
+ forProperty: MPMediaItemPropertyIsCloudItem
+ )
+ cursor?.addFilterPredicate(cloudFilter)
- // Query everything in background for a better performance.
- loadPlaylistsWithFilter(cursor: cursor!.collections, argVal: argVal)
- }
+ // Query everything in background for a better performance.
+ loadItemsWithFilter(cursor: cursor!, type: withType)
} else {
- // There's no permission so, return empty to avoid crashes.
- result([])
+ // Ignore cloud items.
+ let cloudFilter = MPMediaPropertyPredicate(
+ value: false,
+ forProperty: MPMediaItemPropertyIsCloudItem
+ )
+ cursor?.addFilterPredicate(cloudFilter)
+
+ // Query everything in background for a better performance.
+ loadPlaylistsWithFilter(cursor: cursor!.collections, argVal: argVal)
}
}
@@ -85,15 +85,15 @@ class OnWithFiltersQuery {
DispatchQueue.global(qos: .userInitiated).async {
var listOfItems: [[String: Any?]] = Array()
- // [0]: Song - We use [MPMediaItem].
- // [1, 3 and 4]: Album, Artist and Genre - We use [MPMediaItemCollection].
+ // 0 -> Song -> We use [MPMediaItem].
+ // 1, 3 or 4 -> Album, Artist or Genre - We use [MPMediaItemCollection].
if type == 0 {
// For each item(song) inside this "cursor", take one and "format"
// into a [Map], all keys are based on [Android]
// platforms so, if you change some key, will have to change the [Android] too.
for song in cursor.items! {
// If the song file don't has a assetURL, is a Cloud item.
- if !song.isCloudItem && song.assetURL != nil {
+ if !song.isCloudItem, song.assetURL != nil {
let songData = loadSongItem(song: song)
listOfItems.append(songData)
}
@@ -106,8 +106,8 @@ class OnWithFiltersQuery {
// have to change the [Android] too.
for item in cursor.collections! {
var itemData: [String: Any?] = [:]
- // If the first song file don't has a assetURL, is a Cloud item.
- if !item.items[0].isCloudItem && item.items[0].assetURL != nil {
+ // Ignore cloud items.
+ if !item.items[0].isCloudItem, item.items[0].assetURL != nil {
switch type {
case 1:
itemData = loadAlbumItem(album: item)
@@ -123,10 +123,7 @@ class OnWithFiltersQuery {
}
}
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
DispatchQueue.main.async {
- // Back to dart.
self.result(listOfItems)
}
}
@@ -145,17 +142,13 @@ class OnWithFiltersQuery {
let iPlaylist = playlist as! MPMediaPlaylist
// Check if some playlist contains the defined argument.
- // If the first song file don't has a assetURL, is a Cloud item.
- if iPlaylist.name!.contains(argVal) && !iPlaylist.items[0].isCloudItem {
+ if iPlaylist.name!.contains(argVal), !iPlaylist.items[0].isCloudItem {
playlistData = loadPlaylistItem(playlist: playlist)
}
listOfPlaylist.append(playlistData)
}
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
DispatchQueue.main.async {
- // Back to dart.
self.result(listOfPlaylist)
}
}
diff --git a/on_audio_query/ios/Classes/query/helper/OnAudioHelper.swift b/on_audio_query/ios/Classes/queries/helper/OnAudioHelper.swift
similarity index 80%
rename from on_audio_query/ios/Classes/query/helper/OnAudioHelper.swift
rename to on_audio_query/ios/Classes/queries/helper/OnAudioHelper.swift
index 2311d7d6..5b85dcfe 100644
--- a/on_audio_query/ios/Classes/query/helper/OnAudioHelper.swift
+++ b/on_audio_query/ios/Classes/queries/helper/OnAudioHelper.swift
@@ -25,7 +25,7 @@ public func loadSongItem(song: MPMediaItem) -> [String: Any?] {
"duration": Int(song.playbackDuration * 1000),
"title": song.title,
"track": song.albumTrackNumber,
- "file_extension": fileExt,
+ "file_extension": fileExt
]
return songData
}
@@ -35,29 +35,29 @@ public func formatSongList(args: [String: Any], allSongs: [[String: Any?]]) -> [
let order = args["orderType"] as? Int
let sortType = args["sortType"] as? Int
let ignoreCase = args["ignoreCase"] as! Bool
-
+
//
switch sortType {
case 3:
- tempList.sort { (val1, val2) -> Bool in
+ tempList.sort { val1, val2 -> Bool in
(val1["duration"] as! Double) > (val2["duration"] as! Double)
}
case 4:
- tempList.sort { (val1, val2) -> Bool in
+ tempList.sort { val1, val2 -> Bool in
(val1["date_added"] as! Int) > (val2["date_added"] as! Int)
}
case 5:
- tempList.sort { (val1, val2) -> Bool in
+ tempList.sort { val1, val2 -> Bool in
(val1["_size"] as! Int) > (val2["_size"] as! Int)
}
case 6:
- tempList.sort { (val1, val2) -> Bool in
+ tempList.sort { val1, val2 -> Bool in
((val1["_display_name"] as! String).isCase(ignoreCase: ignoreCase)) > ((val2["_display_name"] as! String).isCase(ignoreCase: ignoreCase))
}
default:
break
}
-
+
//
if order == 1 {
tempList.reverse()
@@ -65,7 +65,7 @@ public func formatSongList(args: [String: Any], allSongs: [[String: Any?]]) -> [
return tempList
}
-//Albums
+// Albums
func loadAlbumItem(album: MPMediaItemCollection) -> [String: Any?] {
let albumData: [String: Any?] = [
@@ -83,13 +83,13 @@ public func formatAlbumList(args: [String: Any], allAlbums: [[String: Any?]]) ->
var tempList = allAlbums
let order = args["orderType"] as? Int
let sortType = args["sortType"] as? Int
-
+
if sortType == 3 {
- tempList.sort { (val1, val2) -> Bool in
+ tempList.sort { val1, val2 -> Bool in
(val1["numsongs"] as! Int) > (val2["numsongs"] as! Int)
}
}
-
+
//
if order == 1 {
tempList.reverse()
@@ -97,27 +97,25 @@ public func formatAlbumList(args: [String: Any], allAlbums: [[String: Any?]]) ->
return tempList
}
-
-//Artists
+// Artists
func loadArtistItem(artist: MPMediaItemCollection) -> [String: Any?] {
- //Get all albums from artist
+ // Get all albums from artist
let albumsCursor = MPMediaQuery.albums()
- albumsCursor.addFilterPredicate(MPMediaPropertyPredicate.init(value: artist.items[0].albumArtist, forProperty: MPMediaItemPropertyAlbumArtist))
+ albumsCursor.addFilterPredicate(MPMediaPropertyPredicate(value: artist.items[0].albumArtist, forProperty: MPMediaItemPropertyAlbumArtist))
var finalCount: [String] = Array()
-
+
let albums = albumsCursor.collections
-
- //Normally when song don't have a album, will be "nil" or "unknown",
- //Here we'll "filter" the albums, removing this "non-albums".
- //So, if multiples songs don't has a defined album, will be count only 1.
+
+ // Normally when song don't have a album, will be null or unknown,
+ // We'll "filter" the albums, removing this "non-albums".
for album in albums! {
let itemAlbum = album.items[0].albumTitle
- if itemAlbum != nil && !finalCount.contains(itemAlbum!) {
+ if itemAlbum != nil, !finalCount.contains(itemAlbum!) {
finalCount.append(itemAlbum!)
}
}
-
+
//
let artistData: [String: Any?] = [
"_id": artist.items[0].artistPersistentID,
@@ -132,20 +130,20 @@ public func formatArtistList(args: [String: Any], allArtists: [[String: Any?]])
var tempList = allArtists
let order = args["orderType"] as? Int
let sortType = args["sortType"] as? Int
-
+
switch sortType {
case 3:
- tempList.sort { (val1, val2) -> Bool in
+ tempList.sort { val1, val2 -> Bool in
(val1["number_of_tracks"] as! Int) > (val2["number_of_tracks"] as! Int)
}
case 4:
- tempList.sort { (val1, val2) -> Bool in
+ tempList.sort { val1, val2 -> Bool in
(val1["number_of_albums"] as! Int) > (val2["number_of_albums"] as! Int)
}
default:
break
}
-
+
//
if order == 1 {
tempList.reverse()
@@ -153,7 +151,7 @@ public func formatArtistList(args: [String: Any], allArtists: [[String: Any?]])
return tempList
}
-//Genres
+// Genres
func loadGenreItem(genre: MPMediaItemCollection) -> [String: Any?] {
//
@@ -168,7 +166,7 @@ func loadGenreItem(genre: MPMediaItemCollection) -> [String: Any?] {
public func formatGenreList(args: [String: Any], allGenres: [[String: Any?]]) -> [[String: Any?]] {
var tempList = allGenres
let order = args["orderType"] as? Int
-
+
//
if order == 1 {
tempList.reverse()
@@ -179,29 +177,29 @@ public func formatGenreList(args: [String: Any], allGenres: [[String: Any?]]) ->
public func getMediaCount(type: Int, id: UInt64) -> Int {
var cursor: MPMediaQuery? = nil
var filter: MPMediaPropertyPredicate? = nil
-
- if (type == 0) {
- filter = MPMediaPropertyPredicate.init(value: id, forProperty: MPMediaItemPropertyGenrePersistentID)
+
+ if type == 0 {
+ filter = MPMediaPropertyPredicate(value: id, forProperty: MPMediaItemPropertyGenrePersistentID)
cursor = MPMediaQuery.genres()
} else {
- filter = MPMediaPropertyPredicate.init(value: id, forProperty: MPMediaPlaylistPropertyPersistentID)
+ filter = MPMediaPropertyPredicate(value: id, forProperty: MPMediaPlaylistPropertyPersistentID)
cursor = MPMediaQuery.playlists()
}
-
- if (cursor != nil && filter != nil) {
+
+ if cursor != nil && filter != nil {
cursor?.addFilterPredicate(filter!)
-
- if (cursor!.collections?.count != nil) {
+
+ if cursor!.collections?.count != nil {
return cursor!.collections!.count
}
}
-
- return -1;
+
+ return -1
}
func loadPlaylistItem(playlist: MPMediaItemCollection) -> [String: Any?] {
- //Get the artwork from the first song inside the playlist
- var artwork: Data? = nil
+ // Get the artwork from the first song inside the playlist
+ var artwork: Data?
if playlist.items.count >= 1 {
artwork = playlist.items[0].artwork?.image(at: CGSize(width: 150, height: 150))?.jpegData(compressionQuality: 1)
}
@@ -222,7 +220,7 @@ func loadPlaylistItem(playlist: MPMediaItemCollection) -> [String: Any?] {
extension String {
func isCase(ignoreCase: Bool) -> String {
- if (ignoreCase) {
+ if ignoreCase {
return self
} else {
return self.lowercased()
diff --git a/on_audio_query/ios/Classes/query/OnAlbumsQuery.swift b/on_audio_query/ios/Classes/query/OnAlbumsQuery.swift
deleted file mode 100644
index 3065419a..00000000
--- a/on_audio_query/ios/Classes/query/OnAlbumsQuery.swift
+++ /dev/null
@@ -1,66 +0,0 @@
-import Flutter
-import MediaPlayer
-
-class OnAlbumsQuery {
- var args: [String: Any]
- var result: FlutterResult
-
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- // To make life easy, add all arguments inside a map.
- self.args = call.arguments as! [String: Any]
- self.result = result
- }
-
- func queryAlbums() {
- // The sortType, this method will never be will.
- let sortType = args["sortType"] as? Int ?? 0
-
- // Choose the type(To match android side, let's call "cursor").
- let cursor = MPMediaQuery.albums()
- // Using native sort from [IOS] you can only use the [Album] and [Artist].
- // The others will be sorted "manually" using [formatAlbumList] before
- // send to Dart.
- cursor.groupingType = checkAlbumSortType(sortType: sortType)
-
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
- value: false,
- forProperty: MPMediaItemPropertyIsCloudItem
- )
- cursor.addFilterPredicate(cloudFilter)
-
- // We cannot "query" without permission so, just return a empty list.
- let hasPermission = SwiftOnAudioQueryPlugin().checkPermission()
- if hasPermission {
- // Query everything in background for a better performance.
- loadAlbums(cursor: cursor.collections)
- } else {
- // There's no permission so, return empty to avoid crashes.
- result([])
- }
- }
-
- private func loadAlbums(cursor: [MPMediaItemCollection]!) {
- DispatchQueue.global(qos: .userInitiated).async {
- var listOfAlbums: [[String: Any?]] = Array()
-
- // For each item(album) inside this "cursor", take one and "format"
- // into a [Map], all keys are based on [Android]
- // platforms so, if you change some key, will have to change the [Android] too.
- for album in cursor {
- if !album.items[0].isCloudItem && album.items[0].assetURL != nil {
- let albumData = loadAlbumItem(album: album)
- listOfAlbums.append(albumData)
- }
- }
-
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
- DispatchQueue.main.async {
- // Here we'll check the "custom" sort and define a order to the list.
- let finalList = formatAlbumList(args: self.args, allAlbums: listOfAlbums)
- self.result(finalList)
- }
- }
- }
-}
diff --git a/on_audio_query/ios/Classes/query/OnArtistQuery.swift b/on_audio_query/ios/Classes/query/OnArtistQuery.swift
deleted file mode 100644
index 05c3bf64..00000000
--- a/on_audio_query/ios/Classes/query/OnArtistQuery.swift
+++ /dev/null
@@ -1,64 +0,0 @@
-import Flutter
-import MediaPlayer
-
-class OnArtistsQuery {
- var args: [String: Any]
- var result: FlutterResult
-
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- // To make life easy, add all arguments inside a map.
- self.args = call.arguments as! [String: Any]
- self.result = result
- }
-
- func queryArtists() {
- // Choose the type(To match android side, let's call "cursor").
- let cursor = MPMediaQuery.artists()
-
- // We don't need to define a sortType here. [IOS] only support
- // the [Artist]. The others will be sorted "manually" using
- // [formatSongList] before send to Dart.
-
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
- value: false,
- forProperty: MPMediaItemPropertyIsCloudItem
- )
- cursor.addFilterPredicate(cloudFilter)
-
- // We cannot "query" without permission so, just return a empty list.
- let hasPermission = SwiftOnAudioQueryPlugin().checkPermission()
- if hasPermission {
- // Query everything in background for a better performance.
- loadArtists(cursor: cursor.collections)
- } else {
- // There's no permission so, return empty to avoid crashes.
- result([])
- }
- }
-
- private func loadArtists(cursor: [MPMediaItemCollection]!) {
- DispatchQueue.global(qos: .userInitiated).async {
- var listOfArtists: [[String: Any?]] = Array()
-
- // For each item(artist) inside this "cursor", take one and "format"
- // into a [Map], all keys are based on [Android]
- // platforms so, if you change some key, will have to change the [Android] too.
- for artist in cursor {
- // If the first song file don't has a assetURL, is a Cloud item.
- if !artist.items[0].isCloudItem && artist.items[0].assetURL != nil {
- let artistData = loadArtistItem(artist: artist)
- listOfArtists.append(artistData)
- }
- }
-
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
- DispatchQueue.main.async {
- // Here we'll check the "custom" sort and define a order to the list.
- let finalList = formatArtistList(args: self.args, allArtists: listOfArtists)
- self.result(finalList)
- }
- }
- }
-}
diff --git a/on_audio_query/ios/Classes/query/OnArtworkQuery.swift b/on_audio_query/ios/Classes/query/OnArtworkQuery.swift
deleted file mode 100644
index 0a4324e3..00000000
--- a/on_audio_query/ios/Classes/query/OnArtworkQuery.swift
+++ /dev/null
@@ -1,121 +0,0 @@
-import Flutter
-import MediaPlayer
-
-class OnArtworkQuery {
- var args: [String: Any]
- var result: FlutterResult
-
- init(call: FlutterMethodCall, result: @escaping FlutterResult) {
- // To make life easy, add all arguments inside a map.
- self.args = call.arguments as! [String: Any]
- self.result = result
- }
-
- // [IOS] has a different artwork system and you can "query" using normal "querySongs, .."
- // [Android] can't "query" artwork at the same time as "querySongs", so we need to "query"
- // using a different method(queryArtwork).
- //
- // To match both [IOS] and [Android], [queryArtwork] is the only way to get artwork.
- //
- // Not the best solution but, at least here we can select differents formats and size.
- func queryArtwork() {
- // None of this arguments can be null.
- // The id of the [Song] or [Album].
- let id = args["id"] as! Int
- // The size of the image.
- let size = args["size"] as! Int
- // The size of the image.
- var quality = args["quality"] as! Int
- if (quality > 100) {
- quality = 100
- }
- // The format [JPEG] or [PNG].
- let format = args["format"] as! Int
- // The uri [0]: Song and [1]: Album.
- let uri = args["type"] as! Int
-
- // (To match android side, let's call "cursor").
- var cursor: MPMediaQuery?
- var filter: MPMediaPropertyPredicate?
- // If [uri] is 0: artwork from [Song]
- // If [uri] is 1: artwork from [Album]
- // If [uri] is 2: artwork from [Playlist]
- // If [uri] is 3: artwork from [Artist]
- switch uri {
- case 0:
- filter = MPMediaPropertyPredicate.init(value: id, forProperty: MPMediaItemPropertyPersistentID)
- cursor = MPMediaQuery.songs()
- case 1:
- filter = MPMediaPropertyPredicate.init(value: id, forProperty: MPMediaItemPropertyAlbumPersistentID)
- cursor = MPMediaQuery.albums()
- case 2:
- filter = MPMediaPropertyPredicate.init(value: id, forProperty: MPMediaPlaylistPropertyPersistentID)
- cursor = MPMediaQuery.playlists()
- case 3:
- filter = MPMediaPropertyPredicate.init(value: id, forProperty: MPMediaItemPropertyArtistPersistentID)
- cursor = MPMediaQuery.artists()
- case 4:
- filter = MPMediaPropertyPredicate.init(value: id, forProperty: MPMediaItemPropertyGenrePersistentID)
- cursor = MPMediaQuery.genres()
- default:
- filter = nil
- cursor = nil
- }
-
- // If [cursor] is "nil" or has no permission, just return to dart.
- let hasPermission = SwiftOnAudioQueryPlugin().checkPermission()
- if cursor != nil && filter != nil && hasPermission {
- cursor?.addFilterPredicate(filter!)
-
- // This filter will avoid audios/songs outside phone library(cloud).
- let cloudFilter = MPMediaPropertyPredicate.init(
- value: false,
- forProperty: MPMediaItemPropertyIsCloudItem
- )
- cursor?.addFilterPredicate(cloudFilter)
-
- // Query everything in background for a better performance.
- loadArtwork(cursor: cursor, size: size, format: format, uri: uri, quality: quality)
- } else {
- // There's no permission so, return null to avoid crashes.
- result(nil)
- }
- }
-
- private func loadArtwork(cursor: MPMediaQuery!, size: Int, format: Int, uri: Int, quality: Int) {
- DispatchQueue.global(qos: .userInitiated).async {
- var tempArtwork: Data?
- var tempItem: MPMediaItem?
- let fixedQuality = CGFloat(Double(quality) / 100.0)
-
- // If [uri] is 0: artwork is from [Song]
- // If [uri] is 1, 2 or 3: artwork is from [Album], [Playlist] or [Artist]
- if uri == 0 {
- // Since all id are unique, we can safely call the first item.
- tempItem = cursor!.items?.first
- } else {
- // Since all id are unique, we can safely call the first item.
- tempItem = cursor!.collections?.first?.items[0]
- }
-
- // If [format] is 0: will be [JPEG]
- // If [format] is 1: will be [PNG]
- if format == 0 {
- tempArtwork = tempItem?.artwork?.image(at: CGSize(width: size, height: size))?.jpegData(compressionQuality: fixedQuality)
- } else {
- // [PNG] format will return a high image quality.
- tempArtwork = tempItem?.artwork?.image(at: CGSize(width: size, height: size))?.pngData()
- }
-
- // After finish the "query", go back to the "main" thread(You can only call flutter
- // inside the main thread).
- DispatchQueue.main.async {
- // We don't need a "empty" image so, return null to avoid problems.
- if tempArtwork != nil && tempArtwork!.isEmpty {
- tempArtwork = nil
- }
- self.result(tempArtwork)
- }
- }
- }
-}
diff --git a/on_audio_query/ios/Classes/types/AudiosFromType.swift b/on_audio_query/ios/Classes/types/AudiosFromType.swift
index fb67d005..7167fdcb 100644
--- a/on_audio_query/ios/Classes/types/AudiosFromType.swift
+++ b/on_audio_query/ios/Classes/types/AudiosFromType.swift
@@ -1,21 +1,21 @@
import MediaPlayer
func checkAudiosFrom(type: Int, where: Any) -> MPMediaQuery? {
- var filter: MPMediaPropertyPredicate? = nil
+ var filter: MPMediaPropertyPredicate?
let query = MPMediaQuery.songs()
switch type {
case 0:
- filter = MPMediaPropertyPredicate.init(value: `where`, forProperty: MPMediaItemPropertyAlbumTitle)
+ filter = MPMediaPropertyPredicate(value: `where`, forProperty: MPMediaItemPropertyAlbumTitle)
case 1:
- filter = MPMediaPropertyPredicate.init(value: `where`, forProperty: MPMediaItemPropertyAlbumPersistentID)
+ filter = MPMediaPropertyPredicate(value: `where`, forProperty: MPMediaItemPropertyAlbumPersistentID)
case 2:
- filter = MPMediaPropertyPredicate.init(value: `where`, forProperty: MPMediaItemPropertyArtist)
+ filter = MPMediaPropertyPredicate(value: `where`, forProperty: MPMediaItemPropertyArtist)
case 3:
- filter = MPMediaPropertyPredicate.init(value: `where`, forProperty: MPMediaItemPropertyArtistPersistentID)
+ filter = MPMediaPropertyPredicate(value: `where`, forProperty: MPMediaItemPropertyArtistPersistentID)
case 4:
- filter = MPMediaPropertyPredicate.init(value: `where`, forProperty: MPMediaItemPropertyGenre)
+ filter = MPMediaPropertyPredicate(value: `where`, forProperty: MPMediaItemPropertyGenre)
case 5:
- filter = MPMediaPropertyPredicate.init(value: `where`, forProperty: MPMediaItemPropertyGenrePersistentID)
+ filter = MPMediaPropertyPredicate(value: `where`, forProperty: MPMediaItemPropertyGenrePersistentID)
default:
return nil
}
diff --git a/on_audio_query/ios/Classes/types/WithFiltersType.swift b/on_audio_query/ios/Classes/types/WithFiltersType.swift
index a6d360f8..e497bd2c 100644
--- a/on_audio_query/ios/Classes/types/WithFiltersType.swift
+++ b/on_audio_query/ios/Classes/types/WithFiltersType.swift
@@ -4,14 +4,14 @@ func checkSongsArgs(args: Int, argsVal: String) -> MPMediaPropertyPredicate {
var filter: MPMediaPropertyPredicate? = nil
switch args {
case 0:
- filter = MPMediaPropertyPredicate.init(value: argsVal, forProperty: MPMediaItemPropertyTitle, comparisonType: .contains)
+ filter = MPMediaPropertyPredicate(value: argsVal, forProperty: MPMediaItemPropertyTitle, comparisonType: .contains)
case 1:
print("[on_audio_warning] - IOS don't support [DISPLAY_NAME] type. Will be used the [TITLE]")
- filter = MPMediaPropertyPredicate.init(value: argsVal, forProperty: MPMediaItemPropertyTitle, comparisonType: .contains)
+ filter = MPMediaPropertyPredicate(value: argsVal, forProperty: MPMediaItemPropertyTitle, comparisonType: .contains)
case 2:
- filter = MPMediaPropertyPredicate.init(value: argsVal, forProperty: MPMediaItemPropertyAlbumTitle, comparisonType: .contains)
+ filter = MPMediaPropertyPredicate(value: argsVal, forProperty: MPMediaItemPropertyAlbumTitle, comparisonType: .contains)
case 3:
- filter = MPMediaPropertyPredicate.init(value: argsVal, forProperty: MPMediaItemPropertyArtist, comparisonType: .contains)
+ filter = MPMediaPropertyPredicate(value: argsVal, forProperty: MPMediaItemPropertyArtist, comparisonType: .contains)
default:
break
}
@@ -22,22 +22,22 @@ func checkAlbumsArgs(args: Int, argsVal: String) -> MPMediaPropertyPredicate {
var filter: MPMediaPropertyPredicate? = nil
switch args {
case 0:
- filter = MPMediaPropertyPredicate.init(value: argsVal, forProperty: MPMediaItemPropertyAlbumTitle, comparisonType: .contains)
+ filter = MPMediaPropertyPredicate(value: argsVal, forProperty: MPMediaItemPropertyAlbumTitle, comparisonType: .contains)
case 1:
- filter = MPMediaPropertyPredicate.init(value: argsVal, forProperty: MPMediaItemPropertyAlbumArtist, comparisonType: .contains)
+ filter = MPMediaPropertyPredicate(value: argsVal, forProperty: MPMediaItemPropertyAlbumArtist, comparisonType: .contains)
default:
break
}
return filter!
}
-//Playlist
+// Playlist
func checkArtistsArgs(args: Int, argsVal: String) -> MPMediaPropertyPredicate {
var filter: MPMediaPropertyPredicate? = nil
switch args {
case 0:
- filter = MPMediaPropertyPredicate.init(value: argsVal, forProperty: MPMediaItemPropertyArtist, comparisonType: .contains)
+ filter = MPMediaPropertyPredicate(value: argsVal, forProperty: MPMediaItemPropertyArtist, comparisonType: .contains)
default:
break
}
@@ -48,7 +48,7 @@ func checkGenresArgs(args: Int, argsVal: String) -> MPMediaPropertyPredicate {
var filter: MPMediaPropertyPredicate? = nil
switch args {
case 0:
- filter = MPMediaPropertyPredicate.init(value: argsVal, forProperty: MPMediaItemPropertyGenre, comparisonType: .contains)
+ filter = MPMediaPropertyPredicate(value: argsVal, forProperty: MPMediaItemPropertyGenre, comparisonType: .contains)
default:
break
}
diff --git a/on_audio_query/ios/Classes/utils/Log.swift b/on_audio_query/ios/Classes/utils/Log.swift
new file mode 100644
index 00000000..eabf0554
--- /dev/null
+++ b/on_audio_query/ios/Classes/utils/Log.swift
@@ -0,0 +1,40 @@
+import SwiftyBeaver
+
+class Log {
+ private init() {}
+
+ private static let format = "$Dyyyy-MM-dd $DHH:mm:ss.SSS$d $C$L$c $N.$F:$l - $M"
+
+ private static let console = ConsoleDestination()
+
+ static let type = SwiftyBeaver.self
+
+ static func setLogLevel(level: SwiftyBeaver.Level) {
+ type.addDestination(console)
+ console.format = format
+ console.minLevel = level
+ }
+
+ static func setLogLevel(dartLevel: Int) {
+ type.addDestination(console)
+ console.format = format
+ console.minLevel = convertLogLevel(dartLevel)
+ }
+
+ private static func convertLogLevel(_ dartLevel: Int) -> SwiftyBeaver.Level {
+ switch dartLevel {
+ case 2:
+ return SwiftyBeaver.Level.verbose
+ case 3:
+ return SwiftyBeaver.Level.debug
+ case 4:
+ return SwiftyBeaver.Level.info
+ case 5:
+ return SwiftyBeaver.Level.warning
+ case 6:
+ return SwiftyBeaver.Level.error
+ default:
+ return SwiftyBeaver.Level.warning
+ }
+ }
+}
diff --git a/on_audio_query/ios/Classes/utils/OnDeviceInfo.swift b/on_audio_query/ios/Classes/utils/OnDeviceInfo.swift
deleted file mode 100644
index b0466a4d..00000000
--- a/on_audio_query/ios/Classes/utils/OnDeviceInfo.swift
+++ /dev/null
@@ -1,14 +0,0 @@
-import Flutter
-
-// TODO: Add more specific return to [Model].
-// This method will get some basic information about platform.
-public func queryDeviceInfo(result: FlutterResult) {
- let device = UIDevice.current
- let deviceData: [String: Any] = [
- "device_model": device.model,
- "device_sys_type": device.systemName,
- "device_sys_version": device.systemVersion,
- ]
- result(deviceData)
-}
-
diff --git a/on_audio_query/ios/on_audio_query.podspec b/on_audio_query/ios/on_audio_query.podspec
index 7b764af7..34e98111 100644
--- a/on_audio_query/ios/on_audio_query.podspec
+++ b/on_audio_query/ios/on_audio_query.podspec
@@ -4,10 +4,10 @@
#
Pod::Spec.new do |s|
s.name = 'on_audio_query'
- s.version = '2.5.0'
- s.summary = 'Flutter Plugin used to query audios/songs infos [title, artist, album, etc..] from device storage.'
+ s.version = '0.0.1'
+ s.summary = 'Audio query plugin for flutter.'
s.description = <<-DESC
-A new flutter plugin project.
+ Flutter Plugin used to query audios/songs infos [title, artist, album, etc..] from device storage.
DESC
s.homepage = 'https://github.com/LucasPJS/on_audio_query'
s.license = { :file => '../LICENSE' }
@@ -15,6 +15,7 @@ A new flutter plugin project.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'
+ s.dependency 'SwiftyBeaver'
s.platform = :ios, '10.0'
# Flutter.framework does not contain a i386 slice.
diff --git a/on_audio_query/lib/details/on_audio_query_controller.dart b/on_audio_query/lib/details/on_audio_query_controller.dart
index b52c31c5..fbef6742 100644
--- a/on_audio_query/lib/details/on_audio_query_controller.dart
+++ b/on_audio_query/lib/details/on_audio_query_controller.dart
@@ -36,6 +36,40 @@ class OnAudioQuery {
}
}
+ /// Simplified version of [permissionsStatus] and [permissionsRequest].
+ ///
+ /// Will check and request, if necessary, all required permissions.
+ ///
+ /// **OBS: Will always return true on web platform.**
+ Future checkAndRequest({bool retryRequest = false}) async {
+ if (kIsWeb) return true;
+
+ bool hasPermission = await platform.permissionsStatus();
+ if (!hasPermission) {
+ hasPermission = await platform.permissionsRequest(
+ retryRequest: retryRequest,
+ );
+ }
+
+ return hasPermission;
+ }
+
+ /// Used to set the logging behavior.
+ ///
+ /// Parameters:
+ ///
+ /// * [logType] is used to define the logging level. [LogType].
+ /// * [detailedLog] is used to define if detailed log will be shown
+ /// (Disable by default to avoid spam).
+ ///
+ /// Important:
+ ///
+ /// * If [logType] is null, will be set to [WARN].
+ /// * If [detailedLog] is null, will be set to [false].
+ Future setLogConfig(LogConfig? logConfig) async {
+ return platform.setLogConfig(logConfig);
+ }
+
/// Used to return Songs Info based in [SongModel].
///
/// Parameters:
@@ -535,7 +569,8 @@ class OnAudioQuery {
/// Important:
///
/// * This method will always return a bool.
- /// * If return true `[READ]` and `[WRITE]` permissions is Granted, else `[READ]` and `[WRITE]` is Denied.
+ /// * If return true `[READ]` and `[WRITE]` permissions is Granted,
+ /// else `[READ]` and `[WRITE]` is Denied.
///
/// Platforms:
///
@@ -553,7 +588,8 @@ class OnAudioQuery {
/// Important:
///
/// * This method will always return a bool.
- /// * If return true `[READ]` and `[WRITE]` permissions is Granted, else `[READ]` and `[WRITE]` is Denied.
+ /// * If return true `[READ]` and `[WRITE]` permissions is Granted,
+ /// else `[READ]` and `[WRITE]` is Denied.
///
/// Platforms:
///
@@ -562,8 +598,8 @@ class OnAudioQuery {
/// | `✔️` | `✔️` | `❌` |
///
/// See more about [platforms support](https://github.com/LucJosin/on_audio_query/blob/main/PLATFORMS.md)
- Future permissionsRequest() async {
- return platform.permissionsRequest();
+ Future permissionsRequest({bool retryRequest = false}) async {
+ return platform.permissionsRequest(retryRequest: retryRequest);
}
// Device Information
diff --git a/on_audio_query/lib/on_audio_query.dart b/on_audio_query/lib/on_audio_query.dart
index a8898968..1e23f22a 100644
--- a/on_audio_query/lib/on_audio_query.dart
+++ b/on_audio_query/lib/on_audio_query.dart
@@ -16,9 +16,9 @@ library on_audio_query;
//
import 'dart:async';
-import 'dart:typed_data';
//Dart/Flutter
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
//Platform Interface
diff --git a/on_audio_query/lib/widget/query_artwork_widget.dart b/on_audio_query/lib/widget/query_artwork_widget.dart
index ffe9593f..d199d110 100644
--- a/on_audio_query/lib/widget/query_artwork_widget.dart
+++ b/on_audio_query/lib/widget/query_artwork_widget.dart
@@ -18,6 +18,11 @@ part of on_audio_query;
///
/// A simple example on how you can use the [queryArtwork].
///
+/// Important:
+///
+/// * If [controller] is null, will be create a new instance.
+/// * Log set with [setLogConfig] will only work if [controller] is not null.
+///
/// See more: [QueryArtworkWidget](https://pub.dev/documentation/on_audio_query/latest/on_audio_query/QueryArtworkWidget-class.html)
class QueryArtworkWidget extends StatelessWidget {
/// Used to find and get image.
@@ -25,6 +30,14 @@ class QueryArtworkWidget extends StatelessWidget {
/// All Audio/Song has a unique [id].
final int id;
+ /// Used to call the platform specific method.
+ ///
+ /// Important:
+ ///
+ /// * If [controller] is null, will be create a new instance.
+ /// * Log set with [setLogConfig] will only work if [controller] is not null.
+ final OnAudioQuery? controller;
+
/// Used to define artwork [type].
///
/// Opts: [AUDIO] and [ALBUM].
@@ -36,23 +49,23 @@ class QueryArtworkWidget extends StatelessWidget {
///
/// Important:
///
- /// * If [format] is null, will be set to [JPEG].
- final ArtworkFormat? format;
+ /// * If [format] is not defined, will be set to [JPEG].
+ final ArtworkFormat format;
/// Used to define artwork [size].
///
/// Important:
///
- /// * If [size] is null, will be set to [200].
+ /// * If [size] is not defined, will be set to [200].
/// * This value have a directly influence to image quality.
- final int? size;
+ final int size;
/// Used to define artwork [quality].
///
/// Important:
///
- /// * If [quality] is null, will be set to [100].
- final int? quality;
+ /// * If [quality] is not defined, will be set to [100].
+ final int quality;
/// Used to define the artwork [border radius].
///
@@ -65,51 +78,51 @@ class QueryArtworkWidget extends StatelessWidget {
///
/// Important:
///
- /// * If [artworkQuality] is null, will be set to [low].
- /// * This value [don't] have a directly influence to image quality.
- final FilterQuality? artworkQuality;
+ /// * If [artworkQuality] is not defined, will be set to [low].
+ /// * This value doesn't have a directly influence to image quality.
+ final FilterQuality artworkQuality;
/// Used to define artwork [width].
///
/// Important:
///
- /// * If [artworkWidth] is null, will be set to [50].
- final double? artworkWidth;
+ /// * If [artworkWidth] is not defined, will be set to [50].
+ final double artworkWidth;
/// Used to define artwork [height].
///
/// Important:
///
- /// * If [artworkHeight] is null, will be set to [50].
- final double? artworkHeight;
+ /// * If [artworkHeight] is not defined, will be set to [50].
+ final double artworkHeight;
/// Used to define artwork [fit].
///
/// Important:
///
- /// * If [artworkFit] is null, will be set to [cover].
- final BoxFit? artworkFit;
+ /// * If [artworkFit] is not defined, will be set to [cover].
+ final BoxFit artworkFit;
/// Used to define artwork [clip].
///
/// Important:
///
- /// * If [artworkClipBehavior] is null, will be set to [antiAlias].
- final Clip? artworkClipBehavior;
+ /// * If [artworkClipBehavior] is not defined, will be set to [antiAlias].
+ final Clip artworkClipBehavior;
/// Used to define artwork [scale].
///
/// Important:
///
- /// * If [artworkScale] is null, will be set to [1.0].
- final double? artworkScale;
+ /// * If [artworkScale] is not defined, will be set to [1.0].
+ final double artworkScale;
/// Used to define if artwork should [repeat].
///
/// Important:
///
- /// * If [artworkRepeat] is null, will be set to [false].
- final ImageRepeat? artworkRepeat;
+ /// * If [artworkRepeat] is not defined, will be set to [false].
+ final ImageRepeat artworkRepeat;
/// Used to define artwork [color].
///
@@ -149,8 +162,8 @@ class QueryArtworkWidget extends StatelessWidget {
///
/// Important:
///
- /// * If [keepOldArtwork] is null, will be set to [false].
- final bool? keepOldArtwork;
+ /// * If [keepOldArtwork] is not defined, will be set to [false].
+ final bool keepOldArtwork;
/// Used to define a Widget when audio/song don't return any artwork.
///
@@ -251,56 +264,53 @@ class QueryArtworkWidget extends StatelessWidget {
Key? key,
required this.id,
required this.type,
- this.format,
- this.size,
- this.quality,
- this.artworkQuality,
+ this.quality = 50,
+ this.controller,
+ this.format = ArtworkFormat.JPEG,
+ this.size = 200,
+ this.artworkQuality = FilterQuality.low,
this.artworkBorder,
- this.artworkWidth,
- this.artworkHeight,
- this.artworkFit,
- this.artworkClipBehavior,
- this.artworkScale,
- this.artworkRepeat,
+ this.artworkWidth = 50,
+ this.artworkHeight = 50,
+ this.artworkFit = BoxFit.cover,
+ this.artworkClipBehavior = Clip.antiAlias,
+ this.artworkScale = 1.0,
+ this.artworkRepeat = ImageRepeat.noRepeat,
this.artworkColor,
this.artworkBlendMode,
- this.keepOldArtwork,
+ this.keepOldArtwork = false,
this.nullArtworkWidget,
this.errorBuilder,
this.frameBuilder,
- }) : super(key: key);
+ }) : assert(quality <= 100),
+ super(key: key);
@override
Widget build(BuildContext context) {
- if (quality != null && quality! > 100) {
- throw Exception(
- '[quality] value cannot be greater than [100]',
- );
- }
return FutureBuilder(
- future: OnAudioQuery().queryArtwork(
+ future: (controller ?? OnAudioQuery()).queryArtwork(
id,
type,
- format: format ?? ArtworkFormat.JPEG,
- size: size ?? 200,
- quality: quality ?? 100,
+ format: format,
+ size: size,
+ quality: quality,
),
builder: (context, item) {
if (item.data != null && item.data!.isNotEmpty) {
return ClipRRect(
borderRadius: artworkBorder ?? BorderRadius.circular(50),
- clipBehavior: artworkClipBehavior ?? Clip.antiAlias,
+ clipBehavior: artworkClipBehavior,
child: Image.memory(
item.data!,
- gaplessPlayback: keepOldArtwork ?? false,
- repeat: artworkRepeat ?? ImageRepeat.noRepeat,
- scale: artworkScale ?? 1.0,
- width: artworkWidth ?? 50,
- height: artworkHeight ?? 50,
- fit: artworkFit ?? BoxFit.cover,
+ gaplessPlayback: keepOldArtwork,
+ repeat: artworkRepeat,
+ scale: artworkScale,
+ width: artworkWidth,
+ height: artworkHeight,
+ fit: artworkFit,
color: artworkColor,
colorBlendMode: artworkBlendMode,
- filterQuality: artworkQuality ?? FilterQuality.low,
+ filterQuality: artworkQuality,
frameBuilder: frameBuilder,
errorBuilder: errorBuilder ??
(context, exception, stackTrace) {
diff --git a/on_audio_query/pubspec.yaml b/on_audio_query/pubspec.yaml
index 6f845a28..6b44fd9d 100644
--- a/on_audio_query/pubspec.yaml
+++ b/on_audio_query/pubspec.yaml
@@ -1,7 +1,8 @@
name: on_audio_query
description: Flutter Plugin used to query audios/songs infos [title, artist, album, etc..] from device storage.
-version: 2.6.2
+version: 2.7.0
homepage: https://github.com/LucJosin/on_audio_query/tree/main/on_audio_query
+issue_tracker: https://github.com/LucJosin/on_audio_query/issues
# pub.dev: https://pub.dev/packages/on_audio_query
# ========
# author: Lucas Josino
@@ -9,13 +10,15 @@ homepage: https://github.com/LucJosin/on_audio_query/tree/main/on_audio_query
# website: https://www.lucasjosino.com/
environment:
- sdk: ">=2.12.0 <3.0.0"
+ sdk: ">=2.17.0 <3.0.0"
flutter: ">=1.20.0"
dependencies:
# on_audio_query
- on_audio_query_platform_interface: ^1.4.0
- on_audio_query_web: ^1.3.2+2
+ on_audio_query_platform_interface:
+ path: ../on_audio_query_platform_interface
+ on_audio_query_web:
+ path: ../on_audio_query_web
# Flutter
flutter:
diff --git a/on_audio_query_platform_interface/CHANGELOG.md b/on_audio_query_platform_interface/CHANGELOG.md
index 3dada8d9..efca5f17 100644
--- a/on_audio_query_platform_interface/CHANGELOG.md
+++ b/on_audio_query_platform_interface/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 1.5.0 - [29.02.2022]
+- See more [on_audio_query - CHANGELOG](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/CHANGELOG.md).
+
## 1.4.0 - [02.01.2022]
- See more [on_audio_query - CHANGELOG](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/CHANGELOG.md).
diff --git a/on_audio_query_platform_interface/lib/details/log/log_config.dart b/on_audio_query_platform_interface/lib/details/log/log_config.dart
new file mode 100644
index 00000000..b9d783ec
--- /dev/null
+++ b/on_audio_query_platform_interface/lib/details/log/log_config.dart
@@ -0,0 +1,16 @@
+part of on_audio_query_helper;
+
+/// Used to configure the logging behavior.
+class LogConfig {
+ /// Used to configure the logging behavior.
+ LogConfig({
+ this.logType = LogType.WARN,
+ this.detailedLog = false,
+ });
+
+ /// Define the logging level.
+ final LogType logType;
+
+ /// Define if detailed log will be shown (Disable by default to avoid spam).
+ final bool detailedLog;
+}
diff --git a/on_audio_query_platform_interface/lib/details/on_audio_query_helper.dart b/on_audio_query_platform_interface/lib/details/on_audio_query_helper.dart
index 6bd08e61..da09cb89 100644
--- a/on_audio_query_platform_interface/lib/details/on_audio_query_helper.dart
+++ b/on_audio_query_platform_interface/lib/details/on_audio_query_helper.dart
@@ -20,7 +20,11 @@ part 'types/sort_types/playlist_sort_type.dart';
part 'types/sort_types/genre_sort_type.dart';
//
part 'types/order_type.dart';
+part 'types/log_type.dart';
part 'types/artwork_type.dart';
part 'types/audios_from_type.dart';
part 'types/with_filters_type.dart';
part 'types/uri_type.dart';
+
+//Log
+part 'log/log_config.dart';
diff --git a/on_audio_query_platform_interface/lib/details/types/log_type.dart b/on_audio_query_platform_interface/lib/details/types/log_type.dart
new file mode 100644
index 00000000..72df1bc9
--- /dev/null
+++ b/on_audio_query_platform_interface/lib/details/types/log_type.dart
@@ -0,0 +1,50 @@
+// ignore_for_file: constant_identifier_names
+
+part of on_audio_query_helper;
+
+/// Used to represent the various levels of logs.
+enum LogType {
+ /// Show all logs.
+ ///
+ /// Level: 2
+ VERBOSE(2),
+
+ /// Show debug.
+ ///
+ /// Will also log:
+ /// * [DEBUG]
+ /// * [INFO]
+ /// * [WARN]
+ /// * [ERROR]
+ ///
+ /// Level: 3
+ DEBUG(3),
+
+ /// Show some informations.
+ ///
+ /// Will also log
+ /// * [INFO]
+ /// * [WARN]
+ /// * [ERROR]
+ ///
+ /// Level: 4
+ INFO(4),
+
+ /// Show only warnings.
+ ///
+ /// Will also log:
+ /// * [ERROR]
+ ///
+ /// Level: 5
+ WARN(5),
+
+ /// Show only errors.
+ ///
+ /// Level: 6
+ ERROR(6);
+
+ const LogType(this.value);
+
+ /// Int value that represent the log.
+ final int value;
+}
diff --git a/on_audio_query_platform_interface/lib/method_channel_on_audio_query.dart b/on_audio_query_platform_interface/lib/method_channel_on_audio_query.dart
index 4491a8ce..f39b489f 100644
--- a/on_audio_query_platform_interface/lib/method_channel_on_audio_query.dart
+++ b/on_audio_query_platform_interface/lib/method_channel_on_audio_query.dart
@@ -14,7 +14,6 @@ Copyright: © 2021, Lucas Josino. All rights reserved.
*/
import 'dart:async';
-import 'dart:typed_data';
import 'package:flutter/services.dart';
@@ -29,6 +28,18 @@ class MethodChannelOnAudioQuery extends OnAudioQueryPlatform {
/// The MethodChannel that is being used by this implementation of the plugin.
MethodChannel get channel => _channel;
+ LogConfig _logConfig = LogConfig();
+
+ @override
+ Future setLogConfig(LogConfig? logConfig) async {
+ // Override log configuration
+ if (logConfig != null) _logConfig = logConfig;
+
+ await _channel.invokeMethod("setLogConfig", {
+ "level": _logConfig.logType.value,
+ });
+ }
+
@override
Future> querySongs({
SongSortType? sortType,
@@ -189,7 +200,8 @@ class MethodChannelOnAudioQuery extends OnAudioQueryPlatform {
"id": id,
"format": format != null ? format.index : ArtworkFormat.JPEG.index,
"size": size ?? 200,
- "quality": (quality != null && quality <= 100) ? size : 100,
+ "quality": (quality != null && quality <= 100) ? quality : 50,
+ "detailedErrors": _logConfig.detailedLog,
},
);
return finalArtworks;
@@ -309,9 +321,12 @@ class MethodChannelOnAudioQuery extends OnAudioQueryPlatform {
}
@override
- Future permissionsRequest() async {
+ Future permissionsRequest({bool retryRequest = false}) async {
final bool resultRequest = await _channel.invokeMethod(
"permissionsRequest",
+ {
+ "retryRequest": retryRequest,
+ },
);
return resultRequest;
}
diff --git a/on_audio_query_platform_interface/lib/on_audio_query_platform_interface.dart b/on_audio_query_platform_interface/lib/on_audio_query_platform_interface.dart
index a4a04ccb..e90c431a 100644
--- a/on_audio_query_platform_interface/lib/on_audio_query_platform_interface.dart
+++ b/on_audio_query_platform_interface/lib/on_audio_query_platform_interface.dart
@@ -48,6 +48,22 @@ abstract class OnAudioQueryPlatform extends PlatformInterface {
_instance = instance;
}
+ /// Used to set the logging behavior.
+ ///
+ /// Parameters:
+ ///
+ /// * [logType] is used to define the logging level. [LogType].
+ /// * [detailedLog] is used to define if detailed log will be shown
+ /// (Disable by default to avoid spam).
+ ///
+ /// Important:
+ ///
+ /// * If [logType] is null, will be set to [WARN].
+ /// * If [detailedLog] is null, will be set to [false].
+ Future setLogConfig(LogConfig? logConfig) {
+ throw UnimplementedError('setLogConfig() has not been implemented.');
+ }
+
/// Used to return Songs Info based in [SongModel].
///
/// Parameters:
@@ -523,7 +539,7 @@ abstract class OnAudioQueryPlatform extends PlatformInterface {
/// | `✔️` | `✔️` | `❌` |
///
/// See more about [platforms support](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/PLATFORMS.md)
- Future permissionsRequest() {
+ Future permissionsRequest({bool retryRequest = false}) {
throw UnimplementedError('permissionsRequest() has not been implemented.');
}
diff --git a/on_audio_query_platform_interface/pubspec.yaml b/on_audio_query_platform_interface/pubspec.yaml
index a0b19951..b501ce6a 100644
--- a/on_audio_query_platform_interface/pubspec.yaml
+++ b/on_audio_query_platform_interface/pubspec.yaml
@@ -3,10 +3,10 @@ description: A common platform interface for the [on_audio_query] plugin.
homepage: https://github.com/LucJosin/on_audio_query/tree/master/on_audio_query_platform_interface
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 1.4.0
+version: 1.5.0
environment:
- sdk: ">=2.12.0 <3.0.0"
+ sdk: ">=2.17.0 <3.0.0"
flutter: ">=2.0.0"
dependencies:
diff --git a/on_audio_query_web/CHANGELOG.md b/on_audio_query_web/CHANGELOG.md
index bb06435f..10c0be47 100644
--- a/on_audio_query_web/CHANGELOG.md
+++ b/on_audio_query_web/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 1.4.0 - [29.03.2022]
+- See more [on_audio_query - CHANGELOG](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/CHANGELOG.md).
+
## 1.3.2+2 - [02.01.2022]
- See more [on_audio_query - CHANGELOG](https://github.com/LucJosin/on_audio_query/blob/main/on_audio_query/CHANGELOG.md).
diff --git a/on_audio_query_web/lib/on_audio_query_web.dart b/on_audio_query_web/lib/on_audio_query_web.dart
index 44f78be5..0156c255 100644
--- a/on_audio_query_web/lib/on_audio_query_web.dart
+++ b/on_audio_query_web/lib/on_audio_query_web.dart
@@ -18,7 +18,6 @@ library on_audio_query_web;
import 'dart:async';
import 'dart:convert';
import 'dart:html' as html;
-import 'dart:typed_data';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:on_audio_query_platform_interface/details/on_audio_query_helper.dart';
diff --git a/on_audio_query_web/pubspec.yaml b/on_audio_query_web/pubspec.yaml
index 7d00d689..ccdc4967 100644
--- a/on_audio_query_web/pubspec.yaml
+++ b/on_audio_query_web/pubspec.yaml
@@ -1,15 +1,16 @@
name: on_audio_query_web
description: The web implementation of [on_audio_query].
-version: 1.3.2+2
+version: 1.4.0
homepage: https://github.com/LucJosin/on_audio_query/tree/master/on_audio_query_web
environment:
- sdk: ">=2.12.0 <3.0.0"
+ sdk: ">=2.17.0 <3.0.0"
flutter: ">=1.20.0"
dependencies:
# on_audio_query
- on_audio_query_platform_interface: ^1.4.0
+ on_audio_query_platform_interface:
+ path: ../on_audio_query_platform_interface
# Others
path: ^1.8.0