From 50ccc25eb179fbbeca35cd9c79a0a3df6717804a Mon Sep 17 00:00:00 2001 From: Jordan Bushnell Date: Mon, 23 Nov 2020 20:21:02 +0000 Subject: [PATCH 1/3] Added alternative query syntax for listMedia for Android 11 (no longer supports LIMIT) --- .idea/libraries/Dart_SDK.xml | 29 +++++++++ .idea/libraries/Flutter_Plugins.xml | 7 +++ .idea/modules.xml | 8 +++ .idea/photo_gallery.iml | 17 ++++++ .idea/vcs.xml | 6 ++ .idea/workspace.xml | 60 +++++++++++++++++++ .../morbit/photogallery/PhotoGalleryPlugin.kt | 59 +++++++++++++++--- 7 files changed, 179 insertions(+), 7 deletions(-) create mode 100644 .idea/libraries/Dart_SDK.xml create mode 100644 .idea/libraries/Flutter_Plugins.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/photo_gallery.iml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml new file mode 100644 index 0000000..b6b1f44 --- /dev/null +++ b/.idea/libraries/Dart_SDK.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml new file mode 100644 index 0000000..b0f6971 --- /dev/null +++ b/.idea/libraries/Flutter_Plugins.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..45ce507 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/photo_gallery.iml b/.idea/photo_gallery.iml new file mode 100644 index 0000000..e13ef20 --- /dev/null +++ b/.idea/photo_gallery.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..e764c08 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1606155210205 + + + + + + + + + + \ No newline at end of file diff --git a/android/src/main/kotlin/com/morbit/photogallery/PhotoGalleryPlugin.kt b/android/src/main/kotlin/com/morbit/photogallery/PhotoGalleryPlugin.kt index eaeb0bc..e5d3dc0 100644 --- a/android/src/main/kotlin/com/morbit/photogallery/PhotoGalleryPlugin.kt +++ b/android/src/main/kotlin/com/morbit/photogallery/PhotoGalleryPlugin.kt @@ -277,13 +277,58 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler { val limit = take ?: (total - offset) this.context?.run { - val imageCursor = this.contentResolver.query( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - imageMetadataProjection, - if (albumId == allAlbumId) null else "${MediaStore.Images.Media.BUCKET_ID} = $albumId", - null, - "$imageOrderBy LIMIT $limit OFFSET $offset" - ) + + var imageCursor: Cursor? = null + + /** + * Change the way to fetch Media Store + */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // Get All data in Cursor by sorting in DESC order + imageCursor = this.contentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + imageMetadataProjection, + android.os.Bundle().apply { + // Limit & Offset + putInt(android.content.ContentResolver.QUERY_ARG_LIMIT, limit) + putInt(android.content.ContentResolver.QUERY_ARG_OFFSET, offset) + // Sort function + putStringArray( + android.content.ContentResolver.QUERY_ARG_SORT_COLUMNS, + arrayOf( + MediaStore.Images.Media.DATE_TAKEN, + MediaStore.Images.Media.DATE_MODIFIED + ) + ) + putIntArray( + android.content.ContentResolver.QUERY_ARG_SORT_DIRECTION, + intArrayOf( + android.content.ContentResolver.QUERY_SORT_DIRECTION_DESCENDING, + android.content.ContentResolver.QUERY_SORT_DIRECTION_DESCENDING + ) + ) + // Selection + if (albumId != allAlbumId) { + putString(android.content.ContentResolver.QUERY_ARG_SQL_SELECTION, "${MediaStore.Images.Media.BUCKET_ID} = ?") + putStringArray( + android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, + arrayOf( + albumId.toString() + ) + ) + } + }, + null + ) + } else { + imageCursor = this.contentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + imageMetadataProjection, + if (albumId == allAlbumId) null else "${MediaStore.Images.Media.BUCKET_ID} = $albumId", + null, + "$imageOrderBy LIMIT $limit OFFSET $offset" + ) + } imageCursor?.use { cursor -> while (cursor.moveToNext()) { From eaa1cf2e25ac1229d55f7ee3af9d8294b0e6d727 Mon Sep 17 00:00:00 2001 From: Richard Bushnell Date: Thu, 18 Mar 2021 12:00:31 +0000 Subject: [PATCH 2/3] Upgrade to null safety --- lib/photo_gallery.dart | 49 +++++++++---------- lib/src/common/medium_type.dart | 4 +- .../album_thumbnail_provider.dart | 18 +++---- lib/src/image_providers/photo_provider.dart | 8 ++- .../image_providers/thumbnail_provider.dart | 18 +++---- lib/src/models/album.dart | 12 ++--- lib/src/models/medium.dart | 22 ++++----- pubspec.yaml | 4 +- test/utils/generator.dart | 45 +++++++++-------- test/utils/mock_handler.dart | 2 +- 10 files changed, 85 insertions(+), 97 deletions(-) diff --git a/lib/photo_gallery.dart b/lib/photo_gallery.dart index a7548cc..539c4b7 100644 --- a/lib/photo_gallery.dart +++ b/lib/photo_gallery.dart @@ -2,6 +2,7 @@ library photogallery; import 'dart:async'; import 'dart:io'; +import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; @@ -23,9 +24,8 @@ class PhotoGallery { /// List all available gallery albums and counts number of items of [MediumType]. static Future> listAlbums({ - @required MediumType mediumType, + required MediumType mediumType, }) async { - assert(mediumType != null); final json = await _channel.invokeMethod('listAlbums', { 'mediumType': mediumTypeToJson(mediumType), }); @@ -34,12 +34,11 @@ class PhotoGallery { /// List all available media in a specific album, support pagination of media static Future _listMedia({ - @required Album album, - @required int total, - int skip, - int take, + required Album album, + required int total, + int? skip, + int? take, }) async { - assert(album.id != null); final json = await _channel.invokeMethod('listMedia', { 'albumId': album.id, 'mediumType': mediumTypeToJson(album.mediumType), @@ -52,10 +51,9 @@ class PhotoGallery { /// Get medium metadata by medium id static Future getMedium({ - @required String mediumId, - MediumType mediumType, + required String mediumId, + MediumType? mediumType, }) async { - assert(mediumId != null); final json = await _channel.invokeMethod('getMedium', { 'mediumId': mediumId, 'mediumType': mediumTypeToJson(mediumType), @@ -64,14 +62,13 @@ class PhotoGallery { } /// Get medium thumbnail by medium id - static Future> getThumbnail({ - @required String mediumId, - MediumType mediumType, - int width, - int height, - bool highQuality, + static Future> getThumbnail({ + required String mediumId, + MediumType? mediumType, + int? width, + int? height, + bool? highQuality, }) async { - assert(mediumId != null); final bytes = await _channel.invokeMethod('getThumbnail', { 'mediumId': mediumId, 'mediumType': mediumTypeToJson(mediumType), @@ -83,14 +80,13 @@ class PhotoGallery { } /// Get album thumbnail by album id - static Future> getAlbumThumbnail({ - @required String albumId, - MediumType mediumType, - int width, - int height, - bool highQuality, + static Future> getAlbumThumbnail({ + required String albumId, + MediumType? mediumType, + int? width, + int? height, + bool? highQuality, }) async { - assert(albumId != null); final bytes = await _channel.invokeMethod('getAlbumThumbnail', { 'albumId': albumId, 'mediumType': mediumTypeToJson(mediumType), @@ -103,10 +99,9 @@ class PhotoGallery { /// get medium file by medium id static Future getFile({ - @required String mediumId, - MediumType mediumType, + required String mediumId, + MediumType? mediumType, }) async { - assert(mediumId != null); final path = await _channel.invokeMethod('getFile', { 'mediumId': mediumId, 'mediumType': mediumTypeToJson(mediumType), diff --git a/lib/src/common/medium_type.dart b/lib/src/common/medium_type.dart index 13f1d08..f78210c 100644 --- a/lib/src/common/medium_type.dart +++ b/lib/src/common/medium_type.dart @@ -6,7 +6,7 @@ enum MediumType { video, } -String mediumTypeToJson(MediumType value) { +String? mediumTypeToJson(MediumType? value) { switch (value) { case MediumType.image: return 'image'; @@ -17,7 +17,7 @@ String mediumTypeToJson(MediumType value) { } } -MediumType jsonToMediumType(String value) { +MediumType? jsonToMediumType(String? value) { switch (value) { case 'image': return MediumType.image; diff --git a/lib/src/image_providers/album_thumbnail_provider.dart b/lib/src/image_providers/album_thumbnail_provider.dart index 97dfbfd..80867fd 100644 --- a/lib/src/image_providers/album_thumbnail_provider.dart +++ b/lib/src/image_providers/album_thumbnail_provider.dart @@ -3,18 +3,18 @@ part of photogallery; /// Fetches the given album thumbnail from the gallery. class AlbumThumbnailProvider extends ImageProvider { const AlbumThumbnailProvider({ - @required this.albumId, + required this.albumId, this.mediumType, this.height, this.width, this.highQuality = false, - }) : assert(albumId != null); + }); final String albumId; - final MediumType mediumType; - final int height; - final int width; - final bool highQuality; + final MediumType? mediumType; + final int? height; + final int? width; + final bool? highQuality; @override ImageStreamCompleter load(key, decode) { @@ -37,9 +37,7 @@ class AlbumThumbnailProvider extends ImageProvider { width: width, highQuality: highQuality, ); - if (bytes == null || bytes.length == 0) return null; - - return await decode(bytes); + return await decode(Uint8List.fromList(bytes)); } @override @@ -55,7 +53,7 @@ class AlbumThumbnailProvider extends ImageProvider { } @override - int get hashCode => albumId?.hashCode ?? 0; + int get hashCode => albumId.hashCode; @override String toString() => '$runtimeType("$albumId")'; diff --git a/lib/src/image_providers/photo_provider.dart b/lib/src/image_providers/photo_provider.dart index c53c9e8..7f52305 100644 --- a/lib/src/image_providers/photo_provider.dart +++ b/lib/src/image_providers/photo_provider.dart @@ -3,8 +3,8 @@ part of photogallery; /// Fetches the given image from the gallery. class PhotoProvider extends ImageProvider { PhotoProvider({ - @required this.mediumId, - }) : assert(mediumId != null); + required this.mediumId, + }); final String mediumId; @@ -23,10 +23,8 @@ class PhotoProvider extends ImageProvider { assert(key == this); final file = await PhotoGallery.getFile( mediumId: mediumId, mediumType: MediumType.image); - if (file == null) return null; final bytes = await file.readAsBytes(); - if (bytes.lengthInBytes == 0) return null; return await decode(bytes); } @@ -44,7 +42,7 @@ class PhotoProvider extends ImageProvider { } @override - int get hashCode => mediumId?.hashCode ?? 0; + int get hashCode => mediumId.hashCode; @override String toString() => '$runtimeType("$mediumId")'; diff --git a/lib/src/image_providers/thumbnail_provider.dart b/lib/src/image_providers/thumbnail_provider.dart index 8c9f998..d77c450 100644 --- a/lib/src/image_providers/thumbnail_provider.dart +++ b/lib/src/image_providers/thumbnail_provider.dart @@ -3,18 +3,18 @@ part of photogallery; /// Fetches the given medium thumbnail from the gallery. class ThumbnailProvider extends ImageProvider { const ThumbnailProvider({ - @required this.mediumId, + required this.mediumId, this.mediumType, this.height, this.width, this.highQuality, - }) : assert(mediumId != null); + }); final String mediumId; - final MediumType mediumType; - final int height; - final int width; - final bool highQuality; + final MediumType? mediumType; + final int? height; + final int? width; + final bool? highQuality; @override ImageStreamCompleter load(key, decode) { @@ -37,9 +37,7 @@ class ThumbnailProvider extends ImageProvider { width: width, highQuality: highQuality, ); - if (bytes.length == 0) return null; - - return await decode(bytes); + return await decode(Uint8List.fromList(bytes)); } @override @@ -55,7 +53,7 @@ class ThumbnailProvider extends ImageProvider { } @override - int get hashCode => mediumId?.hashCode ?? 0; + int get hashCode => mediumId.hashCode; @override String toString() => '$runtimeType("$mediumId")'; diff --git a/lib/src/models/album.dart b/lib/src/models/album.dart index 772ea0e..3425a25 100644 --- a/lib/src/models/album.dart +++ b/lib/src/models/album.dart @@ -7,7 +7,7 @@ class Album { final String id; /// The [MediumType] of the album. - final MediumType mediumType; + final MediumType? mediumType; /// The name of the album. final String name; @@ -30,8 +30,8 @@ class Album { /// Pagination can be controlled out of [skip] (defaults to `0`) and /// [take] (defaults to ``). Future listMedia({ - int skip, - int take, + int? skip, + int? take, }) { return PhotoGallery._listMedia( album: this, @@ -45,9 +45,9 @@ class Album { /// /// It will display the lastly taken medium thumbnail. Future> getThumbnail({ - int width, - int height, - bool highQuality = false, + int? width, + int? height, + bool? highQuality = false, }) { return PhotoGallery.getAlbumThumbnail( albumId: id, diff --git a/lib/src/models/medium.dart b/lib/src/models/medium.dart index 7c629d3..3f6ac0c 100644 --- a/lib/src/models/medium.dart +++ b/lib/src/models/medium.dart @@ -9,33 +9,33 @@ class Medium { final String id; /// The medium type. - final MediumType mediumType; + final MediumType? mediumType; /// The medium width. - final int width; + final int? width; /// The medium height. - final int height; + final int? height; /// The medium mimeType. - final String mimeType; + final String? mimeType; /// The duration of video final int duration; /// The date at which the photo or video was taken. - final DateTime creationDate; + final DateTime? creationDate; /// The date at which the photo or video was modified. - final DateTime modifiedDate; + final DateTime? modifiedDate; Medium({ - this.id, + required this.id, this.mediumType, this.width, this.height, this.mimeType, - this.duration, + this.duration = 0, this.creationDate, this.modifiedDate, }); @@ -81,9 +81,9 @@ class Medium { /// Get a JPEG thumbnail's data for this medium. Future> getThumbnail({ - int width, - int height, - bool highQuality = false, + int? width, + int? height, + bool? highQuality = false, }) { return PhotoGallery.getThumbnail( mediumId: id, diff --git a/pubspec.yaml b/pubspec.yaml index 923359e..aae514b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,10 @@ name: photo_gallery description: A Flutter plugin that retrieves images and videos from mobile native gallery. -version: 0.4.0 +version: 0.4.0-nullsafety.0 repository: https://github.com/Firelands128/photo_gallery environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" flutter: ">=1.10.0" dependencies: diff --git a/test/utils/generator.dart b/test/utils/generator.dart index 54c3ddc..c96284e 100644 --- a/test/utils/generator.dart +++ b/test/utils/generator.dart @@ -3,8 +3,7 @@ import 'dart:io'; import 'package:photo_gallery/photo_gallery.dart'; class Generator { - static dynamic generateAlbumsJson({MediumType mediumType}) { - mediumType = mediumType ?? MediumType.image; + static dynamic generateAlbumsJson({MediumType mediumType = MediumType.image}) { return [ { "id": "__ALL__", @@ -21,18 +20,18 @@ class Generator { ]; } - static List generateAlbums({MediumType mediumType}) { + static List generateAlbums({required MediumType mediumType}) { return Generator.generateAlbumsJson(mediumType: mediumType) .map((x) => Album.fromJson(x)) .toList(); } static dynamic generateMediaPageJson({ - String albumId, - MediumType mediumType, - int total, - int skip, - int take, + required String albumId, + required MediumType mediumType, + required int total, + int? skip, + int? take, }) { skip = skip ?? 0; take = take ?? (total - skip); @@ -53,8 +52,8 @@ class Generator { } static dynamic generateMediaJson({ - String mediumId, - MediumType mediumType, + required String mediumId, + required MediumType mediumType, }) { return { "id": mediumId, @@ -69,10 +68,10 @@ class Generator { } static MediaPage generateMediaPage({ - Album album, - MediumType mediumType, - int skip, - int take, + required Album album, + required MediumType mediumType, + int? skip, + int? take, }) { dynamic json = generateMediaPageJson( albumId: album.id, @@ -85,8 +84,8 @@ class Generator { } static Medium generateMedia({ - String mediumId, - MediumType mediumType, + required String mediumId, + required MediumType mediumType, }) { return Medium.fromJson( generateMediaJson(mediumId: mediumId, mediumType: mediumType), @@ -94,28 +93,28 @@ class Generator { } static List generateMockThumbnail({ - String mediumId, - MediumType mediumType, + required String mediumId, + required MediumType mediumType, }) { return [1, 2, 3, 4, 5, 6, 7, 8, 9]; } static List generateMockAlbumThumbnail({ - String albumId, + required String albumId, }) { return [1, 2, 3, 4, 5, 6, 7, 8, 9]; } static String generateFilePath({ - String mediumId, - MediumType mediumType, + required String mediumId, + required MediumType mediumType, }) { return "/path/to/file"; } static File generateFile({ - String mediumId, - MediumType mediumType, + required String mediumId, + required MediumType mediumType, }) { return File(generateFilePath(mediumId: mediumId, mediumType: mediumType)); } diff --git a/test/utils/mock_handler.dart b/test/utils/mock_handler.dart index f6ff959..83ab030 100644 --- a/test/utils/mock_handler.dart +++ b/test/utils/mock_handler.dart @@ -11,7 +11,7 @@ Future mockMethodCallHandler(MethodCall call) async { return albums; } else if (call.method == "listMedia") { String albumId = call.arguments['albumId']; - MediumType mediumType = jsonToMediumType(call.arguments['mediumType']); + MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']); int total = call.arguments['total']; int skip = call.arguments['skip']; int take = call.arguments['take']; From 44fc7e7869015939ec070212b5335f4bc871efa8 Mon Sep 17 00:00:00 2001 From: Richard Bushnell Date: Thu, 18 Mar 2021 12:03:05 +0000 Subject: [PATCH 3/3] Remove .idea files --- .idea/libraries/Dart_SDK.xml | 29 -------------- .idea/libraries/Flutter_Plugins.xml | 7 ---- .idea/modules.xml | 8 ---- .idea/photo_gallery.iml | 17 -------- .idea/vcs.xml | 6 --- .idea/workspace.xml | 60 ----------------------------- 6 files changed, 127 deletions(-) delete mode 100644 .idea/libraries/Dart_SDK.xml delete mode 100644 .idea/libraries/Flutter_Plugins.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/photo_gallery.iml delete mode 100644 .idea/vcs.xml delete mode 100644 .idea/workspace.xml diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml deleted file mode 100644 index b6b1f44..0000000 --- a/.idea/libraries/Dart_SDK.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml deleted file mode 100644 index b0f6971..0000000 --- a/.idea/libraries/Flutter_Plugins.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 45ce507..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/photo_gallery.iml b/.idea/photo_gallery.iml deleted file mode 100644 index e13ef20..0000000 --- a/.idea/photo_gallery.iml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index e764c08..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1606155210205 - - - - - - - - - - \ No newline at end of file