diff --git a/lib/main.dart b/lib/main.dart index b64e2f9..a248d1e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -24,6 +24,7 @@ import 'package:rhythm_box/services/server/active_sourced_track.dart'; import 'package:rhythm_box/services/server/routes/playback.dart'; import 'package:rhythm_box/services/server/server.dart'; import 'package:rhythm_box/services/server/sourced_track.dart'; +import 'package:rhythm_box/shells/system_shell.dart'; import 'package:rhythm_box/translations.dart'; import 'package:rhythm_box/widgets/tracks/querying_track_info.dart'; import 'package:smtc_windows/smtc_windows.dart'; @@ -41,6 +42,7 @@ Future main(List rawArgs) async { WidgetsFlutterBinding.ensureInitialized(); if (PlatformInfo.isDesktop) { + await windowManager.ensureInitialized(); await windowManager.setPreventClose(true); } if (PlatformInfo.isWindows) { @@ -82,6 +84,13 @@ class MyApp extends StatelessWidget { themeMode: ThemeMode.system, translations: AppTranslations(), onInit: () => _initializeProviders(context), + builder: (context, child) { + return SystemShell( + child: ScaffoldMessenger( + child: child ?? const SizedBox(), + ), + ); + }, ); } diff --git a/lib/router.dart b/lib/router.dart index 5b7452b..c385759 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -1,6 +1,7 @@ import 'package:animations/animations.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:rhythm_box/screens/album/view.dart'; import 'package:rhythm_box/screens/auth/mobile_login.dart'; import 'package:rhythm_box/screens/explore.dart'; import 'package:rhythm_box/screens/library/view.dart'; @@ -37,6 +38,13 @@ final router = GoRouter(routes: [ playlistId: state.pathParameters['id']!, ), ), + GoRoute( + path: '/albums/:id', + name: 'albumView', + builder: (context, state) => AlbumViewScreen( + albumId: state.pathParameters['id']!, + ), + ), GoRoute( path: '/settings', name: 'settings', diff --git a/lib/screens/album/view.dart b/lib/screens/album/view.dart new file mode 100644 index 0000000..9c8ed52 --- /dev/null +++ b/lib/screens/album/view.dart @@ -0,0 +1,229 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:get/get.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:rhythm_box/providers/audio_player.dart'; +import 'package:rhythm_box/providers/history.dart'; +import 'package:rhythm_box/providers/spotify.dart'; +import 'package:rhythm_box/services/artist.dart'; +import 'package:rhythm_box/services/audio_player/audio_player.dart'; +import 'package:rhythm_box/services/track.dart'; +import 'package:rhythm_box/widgets/auto_cache_image.dart'; +import 'package:rhythm_box/widgets/sized_container.dart'; +import 'package:rhythm_box/widgets/tracks/playlist_track_list.dart'; +import 'package:spotify/spotify.dart'; + +class AlbumViewScreen extends StatefulWidget { + final String albumId; + final Playlist? playlist; + + const AlbumViewScreen({ + super.key, + required this.albumId, + this.playlist, + }); + + @override + State createState() => _AlbumViewScreenState(); +} + +class _AlbumViewScreenState extends State { + late final SpotifyProvider _spotify = Get.find(); + late final AudioPlayerProvider _playback = Get.find(); + + bool get _isCurrentPlaylist => _album != null + ? _playback.state.value.containsCollection(_album!.id!) + : false; + + bool _isLoading = true; + bool _isLoadingTracks = true; + bool _isUpdating = false; + + Album? _album; + List? _tracks; + + Future _pullPlaylist() async { + _album = await _spotify.api.albums.get(widget.albumId); + setState(() => _isLoading = false); + } + + Future _pullTracks() async { + _tracks = (await _spotify.api.albums.tracks(widget.albumId).all()) + .map((x) => x.asTrack(_album!)) + .toList(); + setState(() => _isLoadingTracks = false); + } + + @override + void initState() { + super.initState(); + _pullPlaylist(); + _pullTracks(); + } + + @override + Widget build(BuildContext context) { + const radius = BorderRadius.all(Radius.circular(8)); + + return Material( + color: Theme.of(context).colorScheme.surface, + child: Scaffold( + appBar: AppBar( + title: const Text('Playlist'), + centerTitle: MediaQuery.of(context).size.width >= 720, + ), + body: Builder( + builder: (context) { + if (_isLoading) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + return CenteredContainer( + child: CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Material( + borderRadius: radius, + elevation: 2, + child: ClipRRect( + borderRadius: radius, + child: (_album?.images?.isNotEmpty ?? false) + ? AutoCacheImage( + _album!.images!.first.url!, + width: 160.0, + height: 160.0, + ) + : const SizedBox( + width: 160, + height: 160, + child: Icon(Icons.image), + ), + ), + ), + const Gap(24), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _album!.name ?? 'Playlist', + style: Theme.of(context) + .textTheme + .headlineSmall, + maxLines: 2, + overflow: TextOverflow.fade, + ), + Text( + _album!.artists?.asString() ?? 'A Playlist', + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + const Gap(8), + Text( + "${NumberFormat.compactCurrency(symbol: '', decimalDigits: 2).format(_album!.popularity ?? 0)} views", + ), + Text( + '#${_album!.id}', + style: GoogleFonts.robotoMono(fontSize: 10), + ), + ], + ), + ), + ], + ).paddingOnly(left: 24, right: 24, top: 24), + const Gap(8), + Wrap( + spacing: 8, + children: [ + Obx( + () => ElevatedButton.icon( + icon: (_isCurrentPlaylist && + _playback.isPlaying.value) + ? const Icon(Icons.pause_outlined) + : const Icon(Icons.play_arrow), + label: const Text('Play'), + onPressed: _isUpdating + ? null + : () async { + if (_isCurrentPlaylist && + _playback.isPlaying.value) { + audioPlayer.pause(); + return; + } else if (_isCurrentPlaylist && + !_playback.isPlaying.value) { + audioPlayer.resume(); + return; + } + + setState(() => _isUpdating = true); + + await _playback.load(_tracks!, + autoPlay: true); + _playback.addCollection(_album!.id!); + Get.find() + .addAlbums([_album!]); + + setState(() => _isUpdating = false); + }, + ), + ), + TextButton.icon( + icon: const Icon(Icons.shuffle), + label: const Text('Shuffle'), + onPressed: _isUpdating + ? null + : () async { + setState(() => _isUpdating = true); + + audioPlayer.setShuffle(true); + + await _playback.load( + _tracks!, + autoPlay: true, + initialIndex: + Random().nextInt(_tracks!.length), + ); + _playback.addCollection(_album!.id!); + Get.find() + .addAlbums([_album!]); + + setState(() => _isUpdating = false); + }, + ), + ], + ).paddingSymmetric(horizontal: 24), + const Gap(24), + ], + ), + ), + SliverToBoxAdapter( + child: Text( + 'Songs (${_tracks?.length ?? 0})', + style: Theme.of(context).textTheme.titleLarge, + ).paddingOnly(left: 28, right: 28, bottom: 4), + ), + PlaylistTrackList( + isLoading: _isLoadingTracks, + playlistId: widget.albumId, + tracks: _tracks, + ), + ], + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/services/track.dart b/lib/services/track.dart new file mode 100644 index 0000000..39aecb8 --- /dev/null +++ b/lib/services/track.dart @@ -0,0 +1,70 @@ +import 'dart:io'; + +import 'package:metadata_god/metadata_god.dart'; +import 'package:path/path.dart'; +import 'package:rhythm_box/services/audio_player/audio_player.dart'; +import 'package:spotify/spotify.dart'; + +extension TrackExtensions on Track { + Track fromFile( + File file, { + Metadata? metadata, + String? art, + }) { + album = Album() + ..name = metadata?.album ?? 'Unknown' + ..images = [if (art != null) Image()..url = art] + ..genres = [if (metadata?.genre != null) metadata!.genre!] + ..artists = [ + Artist() + ..name = metadata?.albumArtist ?? 'Unknown' + ..id = metadata?.albumArtist ?? 'Unknown' + ..type = 'artist', + ] + ..id = metadata?.album + ..releaseDate = metadata?.year?.toString(); + artists = [ + Artist() + ..name = metadata?.artist ?? 'Unknown' + ..id = metadata?.artist ?? 'Unknown' + ]; + + id = metadata?.title ?? basenameWithoutExtension(file.path); + name = metadata?.title ?? basenameWithoutExtension(file.path); + type = 'track'; + uri = file.path; + durationMs = (metadata?.durationMs?.toInt() ?? 0); + + return this; + } +} + +extension TrackSimpleExtensions on TrackSimple { + Track asTrack(AlbumSimple album) { + Track track = Track(); + track.name = name; + track.album = album; + track.artists = artists; + track.availableMarkets = availableMarkets; + track.discNumber = discNumber; + track.durationMs = durationMs; + track.explicit = explicit; + track.externalUrls = externalUrls; + track.href = href; + track.id = id; + track.isPlayable = isPlayable; + track.linkedFrom = linkedFrom; + track.name = name; + track.previewUrl = previewUrl; + track.trackNumber = trackNumber; + track.type = type; + track.uri = uri; + return track; + } +} + +extension TracksToMediaExtension on Iterable { + List asMediaList() { + return map((track) => RhythmMedia(track)).toList(); + } +} diff --git a/lib/shells/system_shell.dart b/lib/shells/system_shell.dart new file mode 100644 index 0000000..667662e --- /dev/null +++ b/lib/shells/system_shell.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:rhythm_box/platform.dart'; + +class SystemShell extends StatelessWidget { + final Widget child; + + const SystemShell({super.key, required this.child}); + + @override + Widget build(BuildContext context) { + if (PlatformInfo.isMacOS) { + return Column( + children: [ + Container( + height: 28, + color: Theme.of(context).colorScheme.surface, + ), + const Divider( + thickness: 0.3, + height: 0.3, + ), + Expanded(child: child), + ], + ); + } + + return child; + } +} diff --git a/lib/widgets/playlist/playlist_section.dart b/lib/widgets/playlist/playlist_section.dart index d634375..a505727 100644 --- a/lib/widgets/playlist/playlist_section.dart +++ b/lib/widgets/playlist/playlist_section.dart @@ -47,7 +47,7 @@ class PlaylistSection extends StatelessWidget { onTap: () { if (item == null) return; GoRouter.of(context).pushNamed( - 'playlistView', + 'albumView', pathParameters: {'id': item.id!}, ); }, diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 7a7f996..701f5cf 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -13,6 +13,7 @@ list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST media_kit_native_event_loop + metadata_god ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 0000000..c1e8892 --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,140 @@ +PODS: + - audio_service (0.14.1): + - FlutterMacOS + - audio_session (0.0.1): + - FlutterMacOS + - desktop_webview_window (0.0.1): + - FlutterMacOS + - device_info_plus (0.0.1): + - FlutterMacOS + - flutter_inappwebview_macos (0.0.1): + - FlutterMacOS + - OrderedSet (~> 5.0) + - flutter_secure_storage_macos (6.1.1): + - FlutterMacOS + - FlutterMacOS (1.0.0) + - media_kit_libs_macos_audio (1.0.4): + - FlutterMacOS + - media_kit_native_event_loop (1.0.0): + - FlutterMacOS + - metadata_god (0.0.1): + - FlutterMacOS + - OrderedSet (5.0.0) + - package_info_plus (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - screen_retriever (0.0.1): + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite (0.0.3): + - Flutter + - FlutterMacOS + - "sqlite3 (3.46.1+1)": + - "sqlite3/common (= 3.46.1+1)" + - "sqlite3/common (3.46.1+1)" + - "sqlite3/dbstatvtab (3.46.1+1)": + - sqlite3/common + - "sqlite3/fts5 (3.46.1+1)": + - sqlite3/common + - "sqlite3/perf-threadsafe (3.46.1+1)": + - sqlite3/common + - "sqlite3/rtree (3.46.1+1)": + - sqlite3/common + - sqlite3_flutter_libs (0.0.1): + - FlutterMacOS + - "sqlite3 (~> 3.46.0+1)" + - sqlite3/dbstatvtab + - sqlite3/fts5 + - sqlite3/perf-threadsafe + - sqlite3/rtree + - window_manager (0.2.0): + - FlutterMacOS + +DEPENDENCIES: + - audio_service (from `Flutter/ephemeral/.symlinks/plugins/audio_service/macos`) + - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) + - desktop_webview_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos`) + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) + - flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`) + - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) + - FlutterMacOS (from `Flutter/ephemeral`) + - media_kit_libs_macos_audio (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_audio/macos`) + - media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`) + - metadata_god (from `Flutter/ephemeral/.symlinks/plugins/metadata_god/macos`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`) + - sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`) + - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) + +SPEC REPOS: + trunk: + - OrderedSet + - sqlite3 + +EXTERNAL SOURCES: + audio_service: + :path: Flutter/ephemeral/.symlinks/plugins/audio_service/macos + audio_session: + :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos + desktop_webview_window: + :path: Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos + flutter_inappwebview_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos + flutter_secure_storage_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos + FlutterMacOS: + :path: Flutter/ephemeral + media_kit_libs_macos_audio: + :path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_audio/macos + media_kit_native_event_loop: + :path: Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos + metadata_god: + :path: Flutter/ephemeral/.symlinks/plugins/metadata_god/macos + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + screen_retriever: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + sqflite: + :path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin + sqlite3_flutter_libs: + :path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos + window_manager: + :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos + +SPEC CHECKSUMS: + audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9 + audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72 + desktop_webview_window: 89bb3d691f4c80314a10be312f4cd35db93a9d5a + device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720 + flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d + flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9 + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + media_kit_libs_macos_audio: 3871782a4f3f84c77f04d7666c87800a781c24da + media_kit_native_event_loop: 7321675377cb9ae8596a29bddf3a3d2b5e8792c5 + metadata_god: 829f61208b44ac1173e7cd32ab740d8776be5435 + OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c + package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb + sqlite3_flutter_libs: 5ca46c1a04eddfbeeb5b16566164aa7ad1616e7b + window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 + +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 + +COCOAPODS: 1.15.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 98d2e2d..6d2c7b5 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -21,12 +21,14 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 1489AA3CFCC3A0C3CA533E1F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71EE4B7445B61FD5E5BECBC3 /* Pods_Runner.framework */; }; 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 662B3DAEF129A60A4A18E1F4 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4ED76FDC58E3E6DFA9084D7A /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,11 +62,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 1CB096F9F1C0858C755D83BD /* 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 = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* rhythm_box.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "rhythm_box.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* rhythm_box.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = rhythm_box.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -76,8 +79,15 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 4ED76FDC58E3E6DFA9084D7A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6D3DDF6690286D5FA1568336 /* 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 = ""; }; + 71EE4B7445B61FD5E5BECBC3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9432957068C77D7544947DE0 /* 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 = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + BB4ED370EF97034F76DDBE09 /* 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 = ""; }; + E523450CD8B62CE48D5098CF /* 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 = ""; }; + F89453F80F705EA3CC90967E /* 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 = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,6 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 662B3DAEF129A60A4A18E1F4 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -92,12 +103,27 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1489AA3CFCC3A0C3CA533E1F /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0000804A301843F9BC4FC25D /* Pods */ = { + isa = PBXGroup; + children = ( + 1CB096F9F1C0858C755D83BD /* Pods-Runner.debug.xcconfig */, + E523450CD8B62CE48D5098CF /* Pods-Runner.release.xcconfig */, + BB4ED370EF97034F76DDBE09 /* Pods-Runner.profile.xcconfig */, + 6D3DDF6690286D5FA1568336 /* Pods-RunnerTests.debug.xcconfig */, + F89453F80F705EA3CC90967E /* Pods-RunnerTests.release.xcconfig */, + 9432957068C77D7544947DE0 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 331C80D6294CF71000263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -125,6 +151,7 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + 0000804A301843F9BC4FC25D /* Pods */, ); sourceTree = ""; }; @@ -175,6 +202,8 @@ D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + 71EE4B7445B61FD5E5BECBC3 /* Pods_Runner.framework */, + 4ED76FDC58E3E6DFA9084D7A /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -186,6 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 267F60974B0F58946DDEBCBF /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -204,11 +234,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + F48554762217E62E6C0BA507 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + BCB6DCE828238EC782478754 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -291,6 +323,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 267F60974B0F58946DDEBCBF /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /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; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -329,6 +383,45 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + BCB6DCE828238EC782478754 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F48554762217E62E6C0BA507 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /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; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -380,6 +473,7 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 6D3DDF6690286D5FA1568336 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -394,6 +488,7 @@ }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F89453F80F705EA3CC90967E /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -408,6 +503,7 @@ }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 9432957068C77D7544947DE0 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index dddb8a3..6926603 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -6,6 +6,12 @@ com.apple.security.cs.allow-jit + com.apple.security.device.bluetooth + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + com.apple.security.network.server diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 852fa1a..9a7b7e7 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -4,5 +4,13 @@ com.apple.security.app-sandbox + com.apple.security.device.bluetooth + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + + com.apple.security.network.server + diff --git a/pubspec.lock b/pubspec.lock index ed1e4b0..9305b60 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -894,6 +894,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + metadata_god: + dependency: "direct main" + description: + path: "packages/metadata_god" + ref: cargokit + resolved-ref: "331636d8e378e3ac9ad30a4b0d3eed17d5a85fe9" + url: "https://github.com/KRTirtho/frb_plugins.git" + source: git + version: "0.5.3" mime: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ce858ff..2b332cb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,11 @@ dependencies: flutter_broadcasts: ^0.4.0 audio_session: ^0.1.21 audio_service: ^0.18.15 + metadata_god: + git: + url: https://github.com/KRTirtho/frb_plugins.git + path: packages/metadata_god + ref: cargokit smtc_windows: git: url: https://github.com/KRTirtho/frb_plugins.git diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index d45225c..10a4ac0 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -13,6 +13,7 @@ list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST media_kit_native_event_loop + metadata_god smtc_windows )