diff --git a/example/ios/Podfile b/example/ios/Podfile index 6697f0a..1e8c3c9 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -10,78 +10,32 @@ project 'Runner', { 'Release' => :release, } -def parse_KV_file(file, separator='=') - file_abs_path = File.expand_path(file) - if !File.exists? file_abs_path - return []; +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" end - generated_key_values = {} - skip_line_start_symbols = ["#", "/"] - File.foreach(file_abs_path) do |line| - next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } - plugin = line.split(pattern=separator) - if plugin.length == 2 - podname = plugin[0].strip() - path = plugin[1].strip() - podpath = File.expand_path("#{path}", file_abs_path) - generated_key_values[podname] = podpath - else - puts "Invalid plugin specification: #{line}" - end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches end - generated_key_values + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" end +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + target 'Runner' do use_frameworks! use_modular_headers! - # Flutter Pod - - copied_flutter_dir = File.join(__dir__, 'Flutter') - copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') - copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') - unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) - # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. - # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. - # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. - - generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') - unless File.exist?(generated_xcode_build_settings_path) - raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) - cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; - - unless File.exist?(copied_framework_path) - FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) - end - unless File.exist?(copied_podspec_path) - FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) - end - end - - # Keep pod path relative so it can be checked into Podfile.lock. - pod 'Flutter', :path => 'Flutter' - - # Plugin Pods - - # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock - # referring to absolute paths on developers' machines. - system('rm -rf .symlinks') - system('mkdir -p .symlinks/plugins') - plugin_pods = parse_KV_file('../.flutter-plugins') - plugin_pods.each do |name, path| - symlink = File.join('.symlinks', 'plugins', name) - File.symlink(path, symlink) - pod name, :path => File.join(symlink, 'ios') - end + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end post_install do |installer| installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' - end + flutter_additional_ios_build_settings(target) end end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 6e880e6..0a8204d 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,20 +1,17 @@ PODS: - Flutter (1.0.0) - - "permission_handler (5.0.1+1)": + - "permission_handler (5.1.0+2)": - Flutter - photo_gallery (0.0.1): - Flutter - video_player (0.0.1): - Flutter - - video_player_web (0.0.1): - - Flutter DEPENDENCIES: - Flutter (from `Flutter`) - permission_handler (from `.symlinks/plugins/permission_handler/ios`) - photo_gallery (from `.symlinks/plugins/photo_gallery/ios`) - video_player (from `.symlinks/plugins/video_player/ios`) - - video_player_web (from `.symlinks/plugins/video_player_web/ios`) EXTERNAL SOURCES: Flutter: @@ -25,16 +22,13 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/photo_gallery/ios" video_player: :path: ".symlinks/plugins/video_player/ios" - video_player_web: - :path: ".symlinks/plugins/video_player_web/ios" SPEC CHECKSUMS: - Flutter: 0e3d915762c693b495b44d77113d4970485de6ec - permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6 + Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c + permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 photo_gallery: 9f95e57747cd22c10676ece3660d1ffe6c603ee5 video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e - video_player_web: da8cadb8274ed4f8dbee8d7171b420dedd437ce7 -PODFILE CHECKSUM: c34e2287a9ccaa606aeceab922830efb9a6ff69a +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c -COCOAPODS: 1.9.3 +COCOAPODS: 1.10.1 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index a69fa81..4322482 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -261,17 +261,12 @@ buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", - "${BUILT_PRODUCTS_DIR}/photo_gallery/photo_gallery.framework", - "${BUILT_PRODUCTS_DIR}/video_player/video_player.framework", + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_gallery.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework", + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -314,7 +309,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -391,7 +385,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -447,7 +440,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a1..919434a 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/example/lib/main.dart b/example/lib/main.dart index 3af9f6e..aef3313 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -18,7 +18,7 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - List _albums; + List? _albums; bool _loading = false; @override @@ -150,7 +150,7 @@ class AlbumPage extends StatefulWidget { } class AlbumPageState extends State { - List _media; + List? _media; @override void initState() { @@ -213,7 +213,7 @@ class ViewerPage extends StatelessWidget { @override Widget build(BuildContext context) { - DateTime date = medium.creationDate ?? medium.modifiedDate; + DateTime? date = medium.creationDate ?? medium.modifiedDate; return MaterialApp( home: Scaffold( appBar: AppBar( @@ -221,7 +221,7 @@ class ViewerPage extends StatelessWidget { onPressed: () => Navigator.of(context).pop(), icon: Icon(Icons.arrow_back_ios), ), - title: Text(date?.toLocal().toString()), + title: date != null ? Text(date.toLocal().toString()) : null, ), body: Container( alignment: Alignment.center, @@ -244,7 +244,7 @@ class VideoProvider extends StatefulWidget { final String mediumId; const VideoProvider({ - @required this.mediumId, + required this.mediumId, }); @override @@ -252,12 +252,12 @@ class VideoProvider extends StatefulWidget { } class _VideoProviderState extends State { - VideoPlayerController _controller; - File _file; + VideoPlayerController? _controller; + File? _file; @override void initState() { - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance?.addPostFrameCallback((_) { initAsync(); }); super.initState(); @@ -266,8 +266,8 @@ class _VideoProviderState extends State { Future initAsync() async { try { _file = await PhotoGallery.getFile(mediumId: widget.mediumId); - _controller = VideoPlayerController.file(_file); - _controller.initialize().then((_) { + _controller = VideoPlayerController.file(_file!); + _controller?.initialize().then((_) { // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. setState(() {}); }); @@ -278,25 +278,25 @@ class _VideoProviderState extends State { @override Widget build(BuildContext context) { - return _controller == null || !_controller.value.initialized + return _controller == null || !_controller!.value.isInitialized ? Container() : Column( mainAxisAlignment: MainAxisAlignment.center, children: [ AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), + aspectRatio: _controller!.value.aspectRatio, + child: VideoPlayer(_controller!), ), FlatButton( onPressed: () { setState(() { - _controller.value.isPlaying - ? _controller.pause() - : _controller.play(); + _controller!.value.isPlaying + ? _controller!.pause() + : _controller!.play(); }); }, child: Icon( - _controller.value.isPlaying ? Icons.pause : Icons.play_arrow, + _controller!.value.isPlaying ? Icons.pause : Icons.play_arrow, ), ), ], diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 3e161b9..8d6e7d4 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -6,7 +6,7 @@ description: Demonstrates how to use the photo_gallery plugin. # publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: @@ -22,10 +22,10 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.3 - transparent_image: ^1.0.0 - permission_handler: ^5.0.1+1 - video_player: ^0.10.12 + cupertino_icons: ^1.0.2 + transparent_image: ^2.0.0 + permission_handler: ^6.1.1 + video_player: ^2.1.0 dev_dependencies: flutter_test: diff --git a/lib/photo_gallery.dart b/lib/photo_gallery.dart index 539c4b7..f543388 100644 --- a/lib/photo_gallery.dart +++ b/lib/photo_gallery.dart @@ -67,7 +67,7 @@ class PhotoGallery { MediumType? mediumType, int? width, int? height, - bool? highQuality, + bool? highQuality = false, }) async { final bytes = await _channel.invokeMethod('getThumbnail', { 'mediumId': mediumId, @@ -76,7 +76,7 @@ class PhotoGallery { 'height': height, 'highQuality': highQuality, }); - return bytes; + return new List.from(bytes); } /// Get album thumbnail by album id @@ -85,7 +85,7 @@ class PhotoGallery { MediumType? mediumType, int? width, int? height, - bool? highQuality, + bool? highQuality = false, }) async { final bytes = await _channel.invokeMethod('getAlbumThumbnail', { 'albumId': albumId, @@ -94,7 +94,7 @@ class PhotoGallery { 'height': height, 'highQuality': highQuality, }); - return bytes; + return new List.from(bytes); } /// get medium file by medium id diff --git a/lib/src/image_providers/thumbnail_provider.dart b/lib/src/image_providers/thumbnail_provider.dart index d77c450..37cd53e 100644 --- a/lib/src/image_providers/thumbnail_provider.dart +++ b/lib/src/image_providers/thumbnail_provider.dart @@ -7,7 +7,7 @@ class ThumbnailProvider extends ImageProvider { this.mediumType, this.height, this.width, - this.highQuality, + this.highQuality = false, }); final String mediumId; diff --git a/test/photo_gallery_test.dart b/test/photo_gallery_test.dart index ac2164a..41ba21c 100644 --- a/test/photo_gallery_test.dart +++ b/test/photo_gallery_test.dart @@ -37,6 +37,7 @@ void main() { MediaPage expected = Generator.generateMediaPage( album: allAlbum, mediumType: mediumType, + total: allAlbum.count, skip: skip, take: take, ); diff --git a/test/utils/generator.dart b/test/utils/generator.dart index c96284e..b9e81e3 100644 --- a/test/utils/generator.dart +++ b/test/utils/generator.dart @@ -3,7 +3,8 @@ import 'dart:io'; import 'package:photo_gallery/photo_gallery.dart'; class Generator { - static dynamic generateAlbumsJson({MediumType mediumType = MediumType.image}) { + static dynamic generateAlbumsJson( + {MediumType? mediumType = MediumType.image}) { return [ { "id": "__ALL__", @@ -20,7 +21,7 @@ class Generator { ]; } - static List generateAlbums({required MediumType mediumType}) { + static List generateAlbums({MediumType? mediumType}) { return Generator.generateAlbumsJson(mediumType: mediumType) .map((x) => Album.fromJson(x)) .toList(); @@ -28,8 +29,8 @@ class Generator { static dynamic generateMediaPageJson({ required String albumId, - required MediumType mediumType, - required int total, + MediumType? mediumType, + int total = 10, int? skip, int? take, }) { @@ -53,7 +54,7 @@ class Generator { static dynamic generateMediaJson({ required String mediumId, - required MediumType mediumType, + MediumType? mediumType, }) { return { "id": mediumId, @@ -69,14 +70,15 @@ class Generator { static MediaPage generateMediaPage({ required Album album, - required MediumType mediumType, + MediumType? mediumType, + required int total, int? skip, int? take, }) { dynamic json = generateMediaPageJson( albumId: album.id, mediumType: mediumType, - total: album.count, + total: total, skip: skip, take: take, ); @@ -85,7 +87,7 @@ class Generator { static Medium generateMedia({ required String mediumId, - required MediumType mediumType, + MediumType? mediumType, }) { return Medium.fromJson( generateMediaJson(mediumId: mediumId, mediumType: mediumType), @@ -94,7 +96,7 @@ class Generator { static List generateMockThumbnail({ required String mediumId, - required MediumType mediumType, + MediumType? mediumType, }) { return [1, 2, 3, 4, 5, 6, 7, 8, 9]; } @@ -107,14 +109,14 @@ class Generator { static String generateFilePath({ required String mediumId, - required MediumType mediumType, + MediumType? mediumType, }) { return "/path/to/file"; } static File generateFile({ required String mediumId, - required MediumType mediumType, + MediumType? mediumType, }) { return File(generateFilePath(mediumId: mediumId, mediumType: mediumType)); } diff --git a/test/utils/mock_handler.dart b/test/utils/mock_handler.dart index 83ab030..f2b104d 100644 --- a/test/utils/mock_handler.dart +++ b/test/utils/mock_handler.dart @@ -6,32 +6,32 @@ import 'generator.dart'; Future mockMethodCallHandler(MethodCall call) async { if (call.method == "listAlbums") { - MediumType mediumType = jsonToMediumType(call.arguments['mediumType']); + MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']); dynamic albums = Generator.generateAlbumsJson(mediumType: mediumType); return albums; } else if (call.method == "listMedia") { String albumId = call.arguments['albumId']; MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']); - int total = call.arguments['total']; - int skip = call.arguments['skip']; - int take = call.arguments['take']; + int? total = call.arguments['total']; + int? skip = call.arguments['skip']; + int? take = call.arguments['take']; dynamic mediaPage = Generator.generateMediaPageJson( albumId: albumId, mediumType: mediumType, - total: total, + total: total ?? 10, skip: skip, take: take, ); return mediaPage; } else if (call.method == "getMedium") { String mediumId = call.arguments['mediumId']; - MediumType mediumType = jsonToMediumType(call.arguments['mediumType']); + MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']); dynamic media = Generator.generateMediaJson(mediumId: mediumId, mediumType: mediumType); return media; } else if (call.method == "getThumbnail") { String mediumId = call.arguments['mediumId']; - MediumType mediumType = jsonToMediumType(call.arguments['mediumType']); + MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']); dynamic thumbnail = Generator.generateMockThumbnail( mediumId: mediumId, mediumType: mediumType); return thumbnail; @@ -41,7 +41,7 @@ Future mockMethodCallHandler(MethodCall call) async { return thumbnail; } else if (call.method == "getFile") { String mediumId = call.arguments['mediumId']; - MediumType mediumType = jsonToMediumType(call.arguments['mediumType']); + MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']); dynamic path = Generator.generateFilePath(mediumId: mediumId, mediumType: mediumType); return path;