diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index cfb2857..6c072dd 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -35,22 +35,16 @@ jobs: flutter-version: ${{ env.FLUTTER_VERSION }} channel: 'stable' - - name: Get dependencies - run: flutter pub get + - name: Install create-dmg + run: brew install create-dmg - - name: Build macOS - run: flutter build macos --release + - name: Build signed DMG + run: ./scripts/build_dmg.sh - - name: Package macOS - run: | - mkdir -p dist - APP=$(find build/macos/Build/Products/Release -maxdepth 1 -name "*.app" | head -1) - ditto -c -k --keepParent "$APP" dist/Komet-macos.zip - - - name: Upload macOS artifact + - name: Upload macOS DMG artifact uses: actions/upload-artifact@v4 with: name: komet-macos - path: dist/Komet-macos.zip + path: dist/*.dmg if-no-files-found: error retention-days: 30 diff --git a/.github/workflows/flutter-main.yml b/.github/workflows/flutter-main.yml index 0eac168..a0f9cdd 100644 --- a/.github/workflows/flutter-main.yml +++ b/.github/workflows/flutter-main.yml @@ -121,5 +121,8 @@ jobs: - name: Build iOS run: flutter build ios --release --no-codesign + - name: Fix ogg_opus_player macOS libs + run: ./scripts/fix_opus_macos.sh + - name: Build macOS run: flutter build macos --release diff --git a/.gitignore b/.gitignore index 6377e21..eba31df 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ analysis_benchmark.json .pub-preload-cache/ .pub/ build/ +dist/ flutter_*.png linked_*.ds unlinked.ds diff --git a/lib/core/storage/token_storage.dart b/lib/core/storage/token_storage.dart index 64002a7..2740a0b 100644 --- a/lib/core/storage/token_storage.dart +++ b/lib/core/storage/token_storage.dart @@ -7,6 +7,7 @@ class TokenStorage { static const _secure = FlutterSecureStorage( aOptions: AndroidOptions(encryptedSharedPreferences: true), + mOptions: MacOsOptions(usesDataProtectionKeychain: false), ); static Future writeSecure(String key, String value) async { diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 14765ba..5b03800 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,54 +1,258 @@ PODS: + - app_links (6.4.1): + - FlutterMacOS - device_info_plus (0.0.1): - FlutterMacOS - dynamic_color (0.0.2): - FlutterMacOS + - file_picker (0.0.1): + - FlutterMacOS + - Firebase/CoreOnly (12.15.0): + - FirebaseCore (~> 12.15.0) + - Firebase/Messaging (12.15.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 12.15.0) + - firebase_core (4.11.0): + - Firebase/CoreOnly (~> 12.15.0) + - FlutterMacOS + - firebase_messaging (16.4.1): + - Firebase/CoreOnly (~> 12.15.0) + - Firebase/Messaging (~> 12.15.0) + - firebase_core + - FlutterMacOS + - FirebaseCore (12.15.0): + - FirebaseCoreInternal (~> 12.15.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - FirebaseCoreInternal (12.15.0): + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - FirebaseInstallations (12.15.0): + - FirebaseCore (~> 12.15.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - PromisesObjC (~> 2.4) + - FirebaseMessaging (12.15.0): + - FirebaseCore (~> 12.15.0) + - FirebaseInstallations (~> 12.15.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Reachability (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - nanopb (~> 3.30910.0) + - flutter_inappwebview_macos (0.0.1): + - FlutterMacOS + - OrderedSet (~> 6.0.3) + - flutter_local_notifications (0.0.1): + - FlutterMacOS - flutter_secure_storage_darwin (10.0.0): - Flutter - FlutterMacOS - flutter_timezone (0.1.0): - FlutterMacOS + - flutter_webrtc (1.4.0): + - FlutterMacOS + - WebRTC-SDK (= 144.7559.09) - FlutterMacOS (1.0.0) + - geolocator_apple (1.2.0): + - Flutter + - FlutterMacOS + - GoogleDataTransport (10.1.0): + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) + - GoogleUtilities/AppDelegateSwizzler (8.1.1): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (8.1.1): + - GoogleUtilities/Privacy + - GoogleUtilities/Logger (8.1.1): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/Network (8.1.1): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (8.1.1)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (8.1.1) + - GoogleUtilities/Reachability (8.1.1): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/UserDefaults (8.1.1): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - media_kit_libs_macos_video (1.0.4): + - FlutterMacOS + - media_kit_video (0.0.1): + - FlutterMacOS + - mobile_scanner (7.0.0): + - Flutter + - FlutterMacOS + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) + - ogg_opus_player (0.0.1): + - Flutter + - FlutterMacOS + - OrderedSet (6.0.3) + - package_info_plus (0.0.1): + - FlutterMacOS + - photo_manager (3.9.0): + - Flutter + - FlutterMacOS + - PromisesObjC (2.4.1) + - record_macos (1.2.1): + - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS + - url_launcher_macos (0.0.1): + - FlutterMacOS + - video_player_avfoundation (0.0.1): + - Flutter + - FlutterMacOS + - wakelock_plus (0.0.1): + - FlutterMacOS + - WebRTC-SDK (144.7559.09) DEPENDENCIES: + - app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`) + - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) + - firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`) + - firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`) + - flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`) + - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`) - flutter_secure_storage_darwin (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_darwin/darwin`) - flutter_timezone (from `Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos`) + - flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`) - FlutterMacOS (from `Flutter/ephemeral`) + - geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/darwin`) + - media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`) + - media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`) + - mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin`) + - ogg_opus_player (from `Flutter/ephemeral/.symlinks/plugins/ogg_opus_player/darwin`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - photo_manager (from `Flutter/ephemeral/.symlinks/plugins/photo_manager/darwin`) + - record_macos (from `Flutter/ephemeral/.symlinks/plugins/record_macos/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) + - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) + - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) + - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) + +SPEC REPOS: + trunk: + - Firebase + - FirebaseCore + - FirebaseCoreInternal + - FirebaseInstallations + - FirebaseMessaging + - GoogleDataTransport + - GoogleUtilities + - nanopb + - OrderedSet + - PromisesObjC + - WebRTC-SDK EXTERNAL SOURCES: + app_links: + :path: Flutter/ephemeral/.symlinks/plugins/app_links/macos device_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos dynamic_color: :path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos + file_picker: + :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos + firebase_core: + :path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos + firebase_messaging: + :path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos + flutter_inappwebview_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos + flutter_local_notifications: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos flutter_secure_storage_darwin: :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_darwin/darwin flutter_timezone: :path: Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos + flutter_webrtc: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos FlutterMacOS: :path: Flutter/ephemeral + geolocator_apple: + :path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/darwin + media_kit_libs_macos_video: + :path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos + media_kit_video: + :path: Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos + mobile_scanner: + :path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin + ogg_opus_player: + :path: Flutter/ephemeral/.symlinks/plugins/ogg_opus_player/darwin + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + photo_manager: + :path: Flutter/ephemeral/.symlinks/plugins/photo_manager/darwin + record_macos: + :path: Flutter/ephemeral/.symlinks/plugins/record_macos/macos shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin sqflite_darwin: :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin + url_launcher_macos: + :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos + video_player_avfoundation: + :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin + wakelock_plus: + :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos SPEC CHECKSUMS: - device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215 - dynamic_color: 5fdff3953fb3457311091863f72914fc76ea3209 - flutter_secure_storage_darwin: 557817588b80e60213cbecb573c45c76b788018d - flutter_timezone: b3bc0c587d8780d395651284a1ff46eb1e5753ac + app_links: 05a6ec2341985eb05e9f97dc63f5837c39895c3f + device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76 + dynamic_color: cb7c2a300ee67ed3bd96c3e852df3af0300bf610 + file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a + Firebase: a8539b633d474fbeb654c7043f9c1649e274045b + firebase_core: 4922bf7ac16c97fb67008a1a00842059b345b0d9 + firebase_messaging: 6c506190a7499e67e644957961f86efe1ac1c3e3 + FirebaseCore: 2e86a4ea1684d4381707069e4a6d89ac808e901e + FirebaseCoreInternal: 6ab6a02c94446c026d2cf35cf5383842ebaa4992 + FirebaseInstallations: eb29ccbf64eaedf86fd5b2ccc7fabde567660b52 + FirebaseMessaging: 40017d7bc8457ee295b0f41d480a80fdabc9994e + flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d + flutter_local_notifications: 1fc7ffb10a83d6a2eeeeddb152d43f1944b0aad0 + flutter_secure_storage_darwin: acdb3f316ed05a3e68f856e0353b133eec373a23 + flutter_timezone: d272288c69082ad571630e0d17140b3d6b93dc0c + flutter_webrtc: fec3dc9284a1f6d21ec2caa91c90bb4c1824eb04 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 - shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 - sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e + GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 + GoogleUtilities: 4f2618a4a1e762a1ee134a1e2323bba9843e06da + media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65 + media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758 + mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93 + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + ogg_opus_player: 954784304f4e2722780018c4abf284e4f93cddf5 + OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 + package_info_plus: f0052d280d17aa382b932f399edf32507174e870 + photo_manager: 25fd77df14f4f0ba5ef99e2c61814dde77e2bceb + PromisesObjC: 752c3227f599e3467650e47ea36f433eeb10c273 + record_macos: 5d55909f9650314be6424ffd6b123ac75a08c3c1 + shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 + url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd + video_player_avfoundation: 3453f792138786248960ca029747fcd9f318ef52 + wakelock_plus: 917609be14d812ddd9e9528876538b2263aaa03b + WebRTC-SDK: e6006119cd730d6315d875e4a421b6cc8bb88833 PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 7188c51..25f11f8 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -241,6 +241,7 @@ 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, AAC6FCD26EB13D7261C78A3C /* [CP] Embed Pods Frameworks */, + BF4E11513C18E180EA08A66C /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -400,6 +401,23 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + BF4E11513C18E180EA08A66C /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; C78DF3AD05984D1177E03977 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png index a8c3408..ed3c19f 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png index 2af46a1..46aaf87 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png index 9f4a440..2a40bf5 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png index 7f51376..21421f1 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png index 6fe15ca..4405688 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png index ac881d5..9b1d347 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png index 0447837..ba63220 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index 8ac0d2a..3d6369d 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -2,8 +2,6 @@ - com.apple.security.app-sandbox - com.apple.security.cs.allow-jit com.apple.security.network.server diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index b993342..6c02f2e 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -2,8 +2,6 @@ - com.apple.security.app-sandbox - com.apple.security.network.client com.apple.security.device.audio-input diff --git a/macos/prebuilt/opus/libogg.a b/macos/prebuilt/opus/libogg.a new file mode 100644 index 0000000..bde5e4b Binary files /dev/null and b/macos/prebuilt/opus/libogg.a differ diff --git a/macos/prebuilt/opus/libopus.a b/macos/prebuilt/opus/libopus.a new file mode 100644 index 0000000..02cd2e4 Binary files /dev/null and b/macos/prebuilt/opus/libopus.a differ diff --git a/macos/prebuilt/opus/libopusenc.a b/macos/prebuilt/opus/libopusenc.a new file mode 100644 index 0000000..2995aef Binary files /dev/null and b/macos/prebuilt/opus/libopusenc.a differ diff --git a/macos/prebuilt/opus/libopusfile.a b/macos/prebuilt/opus/libopusfile.a new file mode 100644 index 0000000..e3743f2 Binary files /dev/null and b/macos/prebuilt/opus/libopusfile.a differ diff --git a/scripts/build_dmg.sh b/scripts/build_dmg.sh new file mode 100755 index 0000000..2be1724 --- /dev/null +++ b/scripts/build_dmg.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP_NAME="Komet" +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +APP_PATH="$PROJECT_ROOT/build/macos/Build/Products/Release/$APP_NAME.app" + +VERSION="$(grep '^version:' "$PROJECT_ROOT/pubspec.yaml" | sed 's/version: *//' | cut -d'+' -f1)" +DIST_DIR="$PROJECT_ROOT/dist" +DMG_PATH="$DIST_DIR/$APP_NAME-$VERSION.dmg" + +echo "==> Building release macOS app" +cd "$PROJECT_ROOT" +flutter pub get +"$PROJECT_ROOT/scripts/fix_opus_macos.sh" +flutter build macos --release + +if [[ ! -d "$APP_PATH" ]]; then + echo "error: $APP_PATH not found after build" >&2 + exit 1 +fi + +echo "==> Verifying ad-hoc signature (Xcode signs with CODE_SIGN_IDENTITY=- and Release.entitlements)" +codesign --verify --deep --strict "$APP_PATH" +if codesign -d --entitlements :- "$APP_PATH" 2>/dev/null | grep -q "com.apple.security.app-sandbox"; then + echo "error: app-sandbox is enabled; flutter_secure_storage keychain will fail (-34018) without a Developer Team" >&2 + exit 1 +fi + +mkdir -p "$DIST_DIR" +rm -f "$DMG_PATH" + +BACKGROUND="$PROJECT_ROOT/scripts/dmg/background.png" + +if command -v create-dmg >/dev/null 2>&1; then + echo "==> Packaging with create-dmg" + create-dmg \ + --volname "$APP_NAME $VERSION" \ + --background "$BACKGROUND" \ + --window-pos 200 120 \ + --window-size 640 400 \ + --icon-size 128 \ + --text-size 13 \ + --icon "$APP_NAME.app" 170 190 \ + --app-drop-link 470 190 \ + --hide-extension "$APP_NAME.app" \ + --no-internet-enable \ + "$DMG_PATH" \ + "$APP_PATH" +else + echo "==> create-dmg not found, packaging with hdiutil" + STAGING="$(mktemp -d)" + cp -R "$APP_PATH" "$STAGING/" + ln -s /Applications "$STAGING/Applications" + hdiutil create \ + -volname "$APP_NAME $VERSION" \ + -srcfolder "$STAGING" \ + -ov -format UDZO \ + "$DMG_PATH" + rm -rf "$STAGING" +fi + +echo "==> Done: $DMG_PATH" diff --git a/scripts/dmg/background.png b/scripts/dmg/background.png new file mode 100644 index 0000000..3ed142f Binary files /dev/null and b/scripts/dmg/background.png differ diff --git a/scripts/fix_opus_macos.sh b/scripts/fix_opus_macos.sh new file mode 100755 index 0000000..c3ab77a --- /dev/null +++ b/scripts/fix_opus_macos.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Replaces the broken bitcode-only opus/ogg static libraries shipped by +# ogg_opus_player with real machine-code universal libraries vendored in +# macos/prebuilt/opus. Run after `flutter pub get`, before building macOS. + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SRC="$PROJECT_ROOT/macos/prebuilt/opus" + +PKG_CFG="$PROJECT_ROOT/.dart_tool/package_config.json" +LIBS="" + +if [ -f "$PKG_CFG" ] && command -v python3 >/dev/null 2>&1; then + ROOT="$(python3 - "$PKG_CFG" <<'PY' +import json, sys, urllib.parse +data = json.load(open(sys.argv[1])) +for pkg in data.get("packages", []): + if pkg["name"] == "ogg_opus_player": + print(urllib.parse.unquote(urllib.parse.urlparse(pkg["rootUri"]).path)) + break +PY +)" + if [ -n "$ROOT" ]; then + case "$ROOT" in + /*) PKGDIR="$ROOT" ;; + *) PKGDIR="$(cd "$PROJECT_ROOT/.dart_tool/$ROOT" && pwd)" ;; + esac + LIBS="$PKGDIR/darwin/Libs" + fi +fi + +if [ -z "$LIBS" ] || [ ! -d "$LIBS" ]; then + for cand in "${PUB_CACHE:-$HOME/.pub-cache}/hosted"/*/ogg_opus_player-*/darwin/Libs; do + [ -d "$cand" ] && LIBS="$cand" && break + done +fi + +if [ -z "$LIBS" ] || [ ! -d "$LIBS" ]; then + echo "warning: ogg_opus_player macOS Libs dir not found; skipping opus fix" >&2 + exit 0 +fi + +echo "==> Installing prebuilt opus libs into: $LIBS" +for f in libogg.a libopus.a libopusfile.a libopusenc.a; do + cp -f "$SRC/$f" "$LIBS/$f" + echo " installed $f ($(lipo -archs "$LIBS/$f" 2>/dev/null))" +done +echo "==> opus libs fixed" diff --git a/tool/make_dmg_background.dart b/tool/make_dmg_background.dart new file mode 100644 index 0000000..a00b8e5 --- /dev/null +++ b/tool/make_dmg_background.dart @@ -0,0 +1,32 @@ +import 'dart:io'; +import 'package:image/image.dart' as img; + +void main() { + const w = 1280; + const h = 800; + final bg = img.Image(width: w, height: h, numChannels: 4); + + final topR = 251, topG = 251, topB = 253; + final botR = 233, botG = 233, botB = 237; + for (var y = 0; y < h; y++) { + final t = y / (h - 1); + final r = (topR + (botR - topR) * t).round(); + final g = (topG + (botG - topG) * t).round(); + final b = (topB + (botB - topB) * t).round(); + img.fillRect(bg, x1: 0, y1: y, x2: w - 1, y2: y, color: img.ColorRgb8(r, g, b)); + } + + final green = img.ColorRgb8(124, 191, 64); + const ay = 380; + img.fillRect(bg, x1: 548, y1: ay - 9, x2: 704, y2: ay + 9, color: green); + img.fillPolygon(bg, vertices: [ + img.Point(700, ay - 30), + img.Point(700, ay + 30), + img.Point(752, ay), + ], color: green); + + final outDir = Directory('scripts/dmg'); + outDir.createSync(recursive: true); + File('scripts/dmg/background.png').writeAsBytesSync(img.encodePng(bg)); + stdout.writeln('wrote scripts/dmg/background.png (${w}x$h)'); +} diff --git a/tool/make_macos_icon.dart b/tool/make_macos_icon.dart new file mode 100644 index 0000000..e0905b4 --- /dev/null +++ b/tool/make_macos_icon.dart @@ -0,0 +1,62 @@ +import 'dart:io'; +import 'dart:math' as math; +import 'package:image/image.dart' as img; + +const _dir = 'macos/Runner/Assets.xcassets/AppIcon.appiconset'; + +const _sizes = { + 'app_icon_16.png': 16, + 'app_icon_32.png': 32, + 'app_icon_64.png': 64, + 'app_icon_128.png': 128, + 'app_icon_256.png': 256, + 'app_icon_512.png': 512, + 'app_icon_1024.png': 1024, +}; + +void main() { + final source = img.decodePng(File('assets/meteor_icon.png').readAsBytesSync()) ?? + img.decodePng(File('$_dir/app_icon_1024.png').readAsBytesSync())!; + + const canvas = 1024; + const content = 824; + const margin = (canvas - content) / 2; + const radius = 184.0; + + final body = img.copyResize(source, + width: content, height: content, interpolation: img.Interpolation.cubic); + + final master = img.Image(width: canvas, height: canvas, numChannels: 4); + img.compositeImage(master, body, dstX: margin.toInt(), dstY: margin.toInt()); + + _applyRoundedMask(master, margin, margin, margin + content, margin + content, radius); + + for (final entry in _sizes.entries) { + final out = img.copyResize(master, + width: entry.value, + height: entry.value, + interpolation: img.Interpolation.cubic); + File('$_dir/${entry.key}').writeAsBytesSync(img.encodePng(out)); + stdout.writeln('wrote ${entry.key} (${entry.value}x${entry.value})'); + } +} + +void _applyRoundedMask( + img.Image im, double x0, double y0, double x1, double y1, double r) { + final cx = (x0 + x1) / 2; + final cy = (y0 + y1) / 2; + final hw = (x1 - x0) / 2; + final hh = (y1 - y0) / 2; + for (final p in im) { + final qx = (p.x + 0.5 - cx).abs() - (hw - r); + final qy = (p.y + 0.5 - cy).abs() - (hh - r); + final dx = math.max(qx, 0.0); + final dy = math.max(qy, 0.0); + final dist = + math.sqrt(dx * dx + dy * dy) + math.min(math.max(qx, qy), 0.0) - r; + final coverage = (0.5 - dist).clamp(0.0, 1.0); + if (coverage < 1.0) { + p.a = (p.a * coverage).round().clamp(0, 255); + } + } +}