diff --git a/lib/providers/downloads.dart b/lib/providers/downloads.dart index fd0a0116..0cc50890 100644 --- a/lib/providers/downloads.dart +++ b/lib/providers/downloads.dart @@ -127,6 +127,11 @@ class DownloadsManager extends ChangeNotifier { } } + /// Whether there are no events downloaded, nor downloading events + bool get isEmpty { + return downloadedEvents.isEmpty && downloading.isEmpty; + } + /// Whether the given event is downloaded bool isEventDownloaded(int eventId) { return downloadedEvents.any((de) => de.event.id == eventId); diff --git a/lib/widgets/desktop_buttons.dart b/lib/widgets/desktop_buttons.dart index b60f4c5b..52b3a6b0 100644 --- a/lib/widgets/desktop_buttons.dart +++ b/lib/widgets/desktop_buttons.dart @@ -18,6 +18,7 @@ */ import 'dart:async'; +import 'dart:io'; import 'package:bluecherry_client/main.dart'; import 'package:bluecherry_client/models/device.dart'; @@ -27,6 +28,7 @@ import 'package:bluecherry_client/widgets/home.dart'; import 'package:bluecherry_client/widgets/misc.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:unity_video_player/unity_video_player.dart'; import 'package:window_manager/window_manager.dart'; final navigationStream = StreamController.broadcast(); @@ -83,7 +85,26 @@ class WindowButtons extends StatefulWidget { State createState() => _WindowButtonsState(); } -class _WindowButtonsState extends State { +class _WindowButtonsState extends State with WindowListener { + @override + void initState() { + windowManager.addListener(this); + _init(); + super.initState(); + } + + @override + void dispose() { + windowManager.removeListener(this); + super.dispose(); + } + + Future _init() async { + // Add this line to override the default close handler + await windowManager.setPreventClose(true); + setState(() {}); + } + @override Widget build(BuildContext context) { if (!isDesktop) return const SizedBox.shrink(); @@ -230,4 +251,19 @@ class _WindowButtonsState extends State { }, ); } + + @override + Future onWindowClose() async { + final isPreventClose = await windowManager.isPreventClose(); + // We ensure all the players are disposed in order to not keep the app alive + // in background, wasting unecessary resources! + if (isPreventClose) { + for (final player in UnityVideoPlayerInterface.players) { + debugPrint('Disposing player ${player.hashCode}'); + player.dispose(); + } + windowManager.destroy(); + exit(0); + } + } } diff --git a/lib/widgets/device_grid/layout_manager.dart b/lib/widgets/device_grid/layout_manager.dart index a4036854..917c22d2 100644 --- a/lib/widgets/device_grid/layout_manager.dart +++ b/lib/widgets/device_grid/layout_manager.dart @@ -208,25 +208,25 @@ class LayoutTile extends StatelessWidget { } } -IconData iconForLayout(DesktopLayoutType type) { +String textForLayout(BuildContext context, DesktopLayoutType type) { switch (type) { case DesktopLayoutType.singleView: - return Icons.crop_square; + return AppLocalizations.of(context).singleView; case DesktopLayoutType.multipleView: - return Icons.view_comfy_outlined; + return AppLocalizations.of(context).multipleView; case DesktopLayoutType.compactView: - return Icons.view_compact_outlined; + return AppLocalizations.of(context).compactView; } } -String textForLayout(BuildContext context, DesktopLayoutType type) { +IconData iconForLayout(DesktopLayoutType type) { switch (type) { case DesktopLayoutType.singleView: - return AppLocalizations.of(context).singleView; + return Icons.crop_square; case DesktopLayoutType.multipleView: - return AppLocalizations.of(context).multipleView; + return Icons.view_compact_outlined; case DesktopLayoutType.compactView: - return AppLocalizations.of(context).compactView; + return Icons.view_comfy_outlined; } } @@ -235,9 +235,9 @@ IconData selectedIconForLayout(DesktopLayoutType type) { case DesktopLayoutType.singleView: return Icons.square_rounded; case DesktopLayoutType.multipleView: - return Icons.view_comfy; - case DesktopLayoutType.compactView: return Icons.view_compact; + case DesktopLayoutType.compactView: + return Icons.view_comfy; } } diff --git a/lib/widgets/downloads_manager.dart b/lib/widgets/downloads_manager.dart index 3374a8a3..738685fe 100644 --- a/lib/widgets/downloads_manager.dart +++ b/lib/widgets/downloads_manager.dart @@ -59,6 +59,12 @@ class DownloadsManagerScreen extends StatelessWidget { ), ), body: LayoutBuilder(builder: (context, consts) { + if (downloads.isEmpty) { + return Center( + child: Text(AppLocalizations.of(context).noDownloads), + ); + } + final size = consts.biggest; return CustomScrollView( slivers: [ diff --git a/packages/unity_video_player/unity_video_player_desktop/lib/unity_video_player_desktop.dart b/packages/unity_video_player/unity_video_player_desktop/lib/unity_video_player_desktop.dart index 88efc7b2..e3f6fa11 100644 --- a/packages/unity_video_player/unity_video_player_desktop/lib/unity_video_player_desktop.dart +++ b/packages/unity_video_player/unity_video_player_desktop/lib/unity_video_player_desktop.dart @@ -19,7 +19,9 @@ class UnityVideoPlayerDesktopInterface extends UnityVideoPlayerInterface { @override UnityVideoPlayer createPlayer({int? width, int? height}) { - return UnityVideoPlayerDesktop(); + final player = UnityVideoPlayerDesktop(width: width, height: height); + UnityVideoPlayerInterface.registerPlayer(player); + return player; } @override @@ -138,5 +140,6 @@ class UnityVideoPlayerDesktop extends UnityVideoPlayer { @override void dispose() { vlcPlayer.dispose(); + UnityVideoPlayerInterface.unregisterPlayer(this); } } diff --git a/packages/unity_video_player/unity_video_player_desktop/pubspec.yaml b/packages/unity_video_player/unity_video_player_desktop/pubspec.yaml index 39cb7dd5..dfb443a4 100644 --- a/packages/unity_video_player/unity_video_player_desktop/pubspec.yaml +++ b/packages/unity_video_player/unity_video_player_desktop/pubspec.yaml @@ -29,5 +29,3 @@ flutter: dartPluginClass: UnityVideoPlayerDesktopInterface macos: dartPluginClass: UnityVideoPlayerDesktopInterface - windows: - dartPluginClass: UnityVideoPlayerDesktopInterface diff --git a/packages/unity_video_player/unity_video_player_media_kit/lib/unity_video_player_media_kit.dart b/packages/unity_video_player/unity_video_player_media_kit/lib/unity_video_player_media_kit.dart index 2121a077..4d41a9ee 100644 --- a/packages/unity_video_player/unity_video_player_media_kit/lib/unity_video_player_media_kit.dart +++ b/packages/unity_video_player/unity_video_player_media_kit/lib/unity_video_player_media_kit.dart @@ -16,7 +16,9 @@ class UnityVideoPlayerMediaKitInterface extends UnityVideoPlayerInterface { @override UnityVideoPlayer createPlayer({int? width, int? height}) { - return UnityVideoPlayerMediaKit(); + final player = UnityVideoPlayerMediaKit(width: width, height: height); + UnityVideoPlayerInterface.registerPlayer(player); + return player; } @override @@ -78,7 +80,8 @@ class __MKVideoState extends State<_MKVideo> { super.initState(); widget.videoController.then((value) { - setState(() => videoController = value); + videoController = value; + if (mounted) setState(() {}); }); } @@ -106,8 +109,8 @@ class UnityVideoPlayerMediaKit extends UnityVideoPlayer { UnityVideoPlayerMediaKit({int? width, int? height}) { mkVideoController = VideoController.create( mkPlayer.handle, - height: height, - width: width, + // height: height, + // width: width, ); } @@ -118,8 +121,14 @@ class UnityVideoPlayerMediaKit extends UnityVideoPlayer { } @override - String? get dataSource => - mkPlayer.state.playlist.medias[mkPlayer.state.playlist.index].uri; + String? get dataSource { + if (mkPlayer.state.playlist.medias.isEmpty) return null; + + var index = mkPlayer.state.playlist.index; + if (index.isNegative) return null; + + return mkPlayer.state.playlist.medias[index].uri; + } @override String? get error { @@ -188,5 +197,6 @@ class UnityVideoPlayerMediaKit extends UnityVideoPlayer { void dispose() async { await (await mkVideoController).dispose(); await mkPlayer.dispose(); + UnityVideoPlayerInterface.unregisterPlayer(this); } } diff --git a/packages/unity_video_player/unity_video_player_mobile/lib/unity_video_player_mobile.dart b/packages/unity_video_player/unity_video_player_mobile/lib/unity_video_player_mobile.dart index 1585e3ec..f2fd0045 100644 --- a/packages/unity_video_player/unity_video_player_mobile/lib/unity_video_player_mobile.dart +++ b/packages/unity_video_player/unity_video_player_mobile/lib/unity_video_player_mobile.dart @@ -15,7 +15,9 @@ class UnityVideoPlayerMobileInterface extends UnityVideoPlayerInterface { @override UnityVideoPlayer createPlayer({int? width, int? height}) { - return UnityVideoPlayerMobile(); + final player = UnityVideoPlayerMobile(); + UnityVideoPlayerInterface.registerPlayer(player); + return player; } /// Creates a video view @@ -120,5 +122,6 @@ class UnityVideoPlayerMobile extends UnityVideoPlayer { @override void dispose() { ijkPlayer.dispose(); + UnityVideoPlayerInterface.unregisterPlayer(this); } } diff --git a/packages/unity_video_player/unity_video_player_platform_interface/lib/unity_video_player_platform_interface.dart b/packages/unity_video_player/unity_video_player_platform_interface/lib/unity_video_player_platform_interface.dart index c5464345..33a93b5c 100644 --- a/packages/unity_video_player/unity_video_player_platform_interface/lib/unity_video_player_platform_interface.dart +++ b/packages/unity_video_player/unity_video_player_platform_interface/lib/unity_video_player_platform_interface.dart @@ -43,6 +43,17 @@ abstract class UnityVideoPlayerInterface extends PlatformInterface { UnityVideoPaneBuilder? paneBuilder, Color color = const Color(0xFF000000), }); + + static final _appPlayers = []; + static List get players => _appPlayers; + + static void registerPlayer(UnityVideoPlayer player) { + _appPlayers.add(player); + } + + static void unregisterPlayer(UnityVideoPlayer player) { + _appPlayers.remove(player); + } } // ignore: non_constant_identifier_names