Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support macOS #142

Merged
merged 20 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,42 @@ jobs:
with:
access_token: ${{ github.token }}

macos:
needs: set-up
timeout-minutes: 60
strategy:
matrix:
macos-version: [13]
fail-fast: false
runs-on: macos-${{ matrix.macos-version }}
steps:
- name: Check out
uses: actions/checkout@v4

- name: Setup Flutter SDK
timeout-minutes: 10
uses: subosito/flutter-action@v2
with:
channel: beta

- name: Run integration tests
id: Run-integration-tests
continue-on-error: true
timeout-minutes: 15
run: |
cd example
flutter test integration_test/integration_test.dart -d macos

- name: Retry integration tests
id: Retry-integration-tests
timeout-minutes: 15
if: steps.Run-integration-tests.outcome == 'failure'
run: |
flutter clean && flutter pub get
cd example
flutter clean && flutter pub get
flutter test integration_test/integration_test.dart -d macos

ios:
needs: set-up
timeout-minutes: 60
Expand Down
21 changes: 12 additions & 9 deletions .metadata
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
# This file should be version controlled and should not be manually edited.

version:
revision: 74e4b092e5212ebf8292dde2a48d3da960c0920b
channel: beta
revision: "123453dc41ef48e717939dd3c012db6a23138895"
channel: "beta"

project_type: plugin

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 74e4b092e5212ebf8292dde2a48d3da960c0920b
base_revision: 74e4b092e5212ebf8292dde2a48d3da960c0920b
create_revision: 123453dc41ef48e717939dd3c012db6a23138895
base_revision: 123453dc41ef48e717939dd3c012db6a23138895
- platform: android
create_revision: 74e4b092e5212ebf8292dde2a48d3da960c0920b
base_revision: 74e4b092e5212ebf8292dde2a48d3da960c0920b
create_revision: 123453dc41ef48e717939dd3c012db6a23138895
base_revision: 123453dc41ef48e717939dd3c012db6a23138895
- platform: ios
create_revision: 74e4b092e5212ebf8292dde2a48d3da960c0920b
base_revision: 74e4b092e5212ebf8292dde2a48d3da960c0920b
create_revision: 123453dc41ef48e717939dd3c012db6a23138895
base_revision: 123453dc41ef48e717939dd3c012db6a23138895
- platform: macos
create_revision: 123453dc41ef48e717939dd3c012db6a23138895
base_revision: 123453dc41ef48e717939dd3c012db6a23138895

# User provided section

Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@

### Dart3 plugin for saving image/video to gallery <img src="https://is5-ssl.mzstatic.com/image/thumb/Purple122/v4/fe/3a/7e/fe3a7e0e-7f52-b750-0ed2-523998c59d48/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/246x0w.webp" alt="ios photo" width="20" height="20"/> <img src="https://play-lh.googleusercontent.com/ZyWNGIfzUyoajtFcD7NhMksHEZh37f-MkHVGr5Yfefa-IX7yj9SMfI82Z7a2wpdKCA=w240-h480-rw" alt="amdroid photo" width="20" height="20"/>
**Please [like👍](https://pub.dev/packages/gal) and [star⭐️](https://github.com/natsuk4ze/gal) for more features.**
| **Support** | iOS 11.0+ | Android SDK 21+ |
|-------------|----------------|-----------------|
| | Android | iOS | macOS |
|-------------|---------|---------|-------|
| **Support** | SDK 21+ | iOS 11+ | 11+ |

| | iOS | Android |
|-------------|-----|---------|
| **Example** | <img src="https://github.com/natsuk4ze/gal/raw/main/readme_assets/ios.gif" alt="ios" width="270"/> | <img src="https://github.com/natsuk4ze/gal/raw/main/readme_assets/android.gif" alt="android" width="270"/> |


Expand All @@ -36,10 +40,10 @@ You can use the command to add gal as a dependency with the latest stable versio
$ flutter pub add gal
```

### iOS
### iOS & macOS

Add the following key to your _Info.plist_ file, located in
`<project root>/ios/Runner/Info.plist`:
`<project root>/<ios or macos>/Runner/Info.plist`:

* `<key>NSPhotoLibraryAddUsageDescription</key>` Required
* `<key>NSPhotoLibraryUsageDescription</key>` Required for ios < 14 or saving to album
Expand Down
File renamed without changes.
36 changes: 28 additions & 8 deletions ios/Classes/GalPlugin.swift → darwin/Classes/GalPlugin.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import Flutter
import Photos
import UIKit

#if os(iOS)
import Flutter
import UIKit
#else
import Cocoa
import FlutterMacOS
#endif

public class GalPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "gal", binaryMessenger: registrar.messenger())
#if os(iOS)
let messenger = registrar.messenger()
#else
let messenger = registrar.messenger
#endif
let channel = FlutterMethodChannel(name: "gal", binaryMessenger: messenger)
let instance = GalPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
Expand Down Expand Up @@ -89,7 +100,7 @@ public class GalPlugin: NSObject, FlutterPlugin {
PHPhotoLibrary.shared().performChanges({
let albumChangeRequest = PHAssetCollectionChangeRequest(for: collection!)
albumChangeRequest!.addAssets(
[assetChangeRequest().placeholderForCreatedAsset] as NSArray)
[assetChangeRequest().placeholderForCreatedAsset!] as NSArray)
}, completionHandler: completion)
}
return
Expand Down Expand Up @@ -117,12 +128,21 @@ public class GalPlugin: NSObject, FlutterPlugin {
}

private func open(completion: @escaping () -> Void) {
guard let url = URL(string: "photos-redirect://") else { return }
UIApplication.shared.open(url, options: [:]) { _ in completion() }
#if os(iOS)
guard let url = URL(string: "photos-redirect://") else { return }
UIApplication.shared.open(url, options: [:]) { _ in completion() }
#else
guard let url = URL(string: "photos://") else { return }
NSWorkspace.shared.open(url)
completion()
#endif
}

private func hasAccess(toAlbum: Bool) -> Bool {
if #available(iOS 14, *) {
if #available(iOS 14, macOS 11, *) {
print(PHPhotoLibrary.authorizationStatus(for: .addOnly) == .authorized)
print(PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized)
print(PHPhotoLibrary.authorizationStatus(for: .readWrite) == .limited)
return toAlbum
? PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized
|| PHPhotoLibrary.authorizationStatus(for: .readWrite) == .limited
Expand All @@ -135,7 +155,7 @@ public class GalPlugin: NSObject, FlutterPlugin {
/// returns the result immediately, without displaying a dialog.
/// See: https://qiita.com/fuziki/items/87a3a1a8e481a1546b38
private func requestAccess(toAlbum: Bool, completion: @escaping (Bool) -> Void) {
if #available(iOS 14, *) {
if #available(iOS 14, macOS 11, *) {
PHPhotoLibrary.requestAuthorization(for: toAlbum ? .readWrite : .addOnly) { _ in
completion(self.hasAccess(toAlbum: toAlbum))
}
Expand Down
9 changes: 6 additions & 3 deletions ios/gal.podspec → darwin/gal.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ Pod::Spec.new do |s|
s.author = { 'Midori Design Studio' => 'https://midoridesign.studio' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'
s.platform = :ios, '11.0'
s.ios.dependency 'Flutter'
s.osx.dependency 'FlutterMacOS'
s.ios.deployment_target = '11.0'
s.osx.deployment_target = '11.0'

# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
s.ios.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
s.osx.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.swift_version = '5.0'
end
30 changes: 30 additions & 0 deletions example/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "123453dc41ef48e717939dd3c012db6a23138895"
channel: "beta"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 123453dc41ef48e717939dd3c012db6a23138895
base_revision: 123453dc41ef48e717939dd3c012db6a23138895
- platform: macos
create_revision: 123453dc41ef48e717939dd3c012db6a23138895
base_revision: 123453dc41ef48e717939dd3c012db6a23138895

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
34 changes: 18 additions & 16 deletions example/integration_test/integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,43 @@ Future<void> main() async {
() async => Gal.hasAccess(toAlbum: toAlbum));

await run('requestAccess(toAlbum: $toAlbum)',
() async => Gal.requestAccess(toAlbum: toAlbum));
() async => Gal.requestAccess(toAlbum: toAlbum),
skip: Platform.isMacOS);

await run('putImage(album: $album)', () async {
final path = await getFilePath('assets/done.jpg');
await Gal.putImage(path, album: album);
});
}, skip: Platform.isMacOS);

await run('putImageBytes(album: $album)', () async {
final bytes = await getBytesData('assets/done.jpg');
await Gal.putImageBytes(bytes, album: album);
});
}, skip: Platform.isMacOS);

await run('putVideo(album: $album)', () async {
final path = await getFilePath('assets/done.mp4');
await Gal.putVideo(path, album: album);
});
}, skip: Platform.isMacOS);
}
await run('open', () async => Gal.open());
}

Future<void> run(String title, Future<dynamic> Function() function) async =>
test(
title,
() async {
try {
final value = await function();
if (value != null) debugPrint('returned: $value');
} on GalException catch (e, st) {
fail("""
Future<void> run(
String title,
Future<dynamic> Function() function, {
bool skip = false,
}) async =>
test(title, () async {
try {
final value = await function();
if (value != null) debugPrint('returned: $value');
} on GalException catch (e, st) {
fail("""
${e.runtimeType}: $e\n
StackTrace: $st
PlatformException: ${e.platformException}""");
}
},
);
}
}, skip: skip);

Future<String> getFilePath(String path) async {
final byteData = await rootBundle.load(path);
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
platform :ios, '11.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
9 changes: 5 additions & 4 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,28 @@ PODS:
- Flutter (1.0.0)
- gal (1.0.0):
- Flutter
- FlutterMacOS
- integration_test (0.0.1):
- Flutter

DEPENDENCIES:
- Flutter (from `Flutter`)
- gal (from `.symlinks/plugins/gal/ios`)
- gal (from `.symlinks/plugins/gal/darwin`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)

EXTERNAL SOURCES:
Flutter:
:path: Flutter
gal:
:path: ".symlinks/plugins/gal/ios"
:path: ".symlinks/plugins/gal/darwin"
integration_test:
:path: ".symlinks/plugins/integration_test/ios"

SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
gal: a7198871e0d63bdffb906fbbcc9e16b58031a357
gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1
integration_test: 13825b8a9334a850581300559b8839134b124670

PODFILE CHECKSUM: 1a86e7654bb8611f38728384d250276ef708ee5f
PODFILE CHECKSUM: 333cb2cbe8cc8767aab4e2b5a40cd6eb7dff3e71

COCOAPODS: 1.12.1
6 changes: 0 additions & 6 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
05B521EF392CA43B85788001 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
26D070C4DFDEC8340C8E304A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3594B1C680A2D8952A225D84 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
46BA1975B3A9BC4DBF123089 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
4CE5964E1262DC6FFFAFF64F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
Expand All @@ -50,7 +48,6 @@
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B52E291D120E5AFF7F2728C0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
E90B41D7C041B399B7FA8E16 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -125,9 +122,6 @@
B52E291D120E5AFF7F2728C0 /* Pods-Runner.debug.xcconfig */,
46BA1975B3A9BC4DBF123089 /* Pods-Runner.release.xcconfig */,
4CE5964E1262DC6FFFAFF64F /* Pods-Runner.profile.xcconfig */,
05B521EF392CA43B85788001 /* Pods-RunnerTests.debug.xcconfig */,
E90B41D7C041B399B7FA8E16 /* Pods-RunnerTests.release.xcconfig */,
3594B1C680A2D8952A225D84 /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
Expand Down
2 changes: 2 additions & 0 deletions example/macos/Flutter/Flutter-Debug.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
2 changes: 2 additions & 0 deletions example/macos/Flutter/Flutter-Release.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
Loading
Loading