diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile index f09126bb..ee8ffa93 100644 --- a/android/fastlane/Fastfile +++ b/android/fastlane/Fastfile @@ -40,7 +40,8 @@ platform :android do "../build/app/outputs/flutter-apk/app-arm64-v8a-release.apk" ], overwrite_upload: true, - overwrite_upload_mode: 'reuse' + overwrite_upload_mode: 'reuse', + changes_not_sent_for_review: true ) end end diff --git a/lib/main.dart b/lib/main.dart index 7f1cebd4..dec72530 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,7 +8,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'screens/main_screen.dart'; import 'screens/auth_screen.dart'; import 'services/storage_service.dart'; -import 'services/macos_titlebar_service.dart'; +import 'services/macos_window_service.dart'; import 'services/fullscreen_state_manager.dart'; import 'services/settings_service.dart'; import 'utils/platform_detector.dart'; @@ -100,7 +100,7 @@ void main() async { } // Configure macOS window with custom titlebar (depends on window manager) - futures.add(MacOSTitlebarService.setupCustomTitlebar()); + futures.add(MacOSWindowService.setupCustomTitlebar()); // Initialize storage service futures.add(StorageService.getInstance()); diff --git a/lib/models/companion_remote/remote_command.dart b/lib/models/companion_remote/remote_command.dart index 233c63fd..09a69268 100644 --- a/lib/models/companion_remote/remote_command.dart +++ b/lib/models/companion_remote/remote_command.dart @@ -1,4 +1,56 @@ -import 'remote_command_type.dart'; +enum RemoteCommandType { + // Navigation + dpadUp, + dpadDown, + dpadLeft, + dpadRight, + select, + back, + contextMenu, + + // Playback + play, + pause, + playPause, + stop, + seekForward, + seekBackward, + nextTrack, + previousTrack, + skipIntro, + skipCredits, + + // Volume + volumeUp, + volumeDown, + volumeMute, + volumeSet, + + // Tab Navigation + tabNext, + tabPrevious, + tabDiscover, + tabLibraries, + tabSearch, + tabDownloads, + tabSettings, + + // Quick Actions + home, + search, + subtitles, + audioTracks, + qualitySettings, + fullscreen, + + // Session Management + ping, + pong, + deviceInfo, + disconnect, + ack, + syncState, +} class RemoteCommand { final RemoteCommandType type; diff --git a/lib/models/companion_remote/remote_command_type.dart b/lib/models/companion_remote/remote_command_type.dart deleted file mode 100644 index d7285ca1..00000000 --- a/lib/models/companion_remote/remote_command_type.dart +++ /dev/null @@ -1,53 +0,0 @@ -enum RemoteCommandType { - // Navigation - dpadUp, - dpadDown, - dpadLeft, - dpadRight, - select, - back, - contextMenu, - - // Playback - play, - pause, - playPause, - stop, - seekForward, - seekBackward, - nextTrack, - previousTrack, - skipIntro, - skipCredits, - - // Volume - volumeUp, - volumeDown, - volumeMute, - volumeSet, - - // Tab Navigation - tabNext, - tabPrevious, - tabDiscover, - tabLibraries, - tabSearch, - tabDownloads, - tabSettings, - - // Quick Actions - home, - search, - subtitles, - audioTracks, - qualitySettings, - fullscreen, - - // Session Management - ping, - pong, - deviceInfo, - disconnect, - ack, - syncState, -} diff --git a/lib/models/companion_remote/trusted_device.dart b/lib/models/companion_remote/trusted_device.dart deleted file mode 100644 index e367c852..00000000 --- a/lib/models/companion_remote/trusted_device.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'trusted_device.g.dart'; - -@JsonSerializable() -class TrustedDevice { - final String peerId; - final String deviceName; - final String platform; - final DateTime firstConnected; - final DateTime lastConnected; - final bool isApproved; - - TrustedDevice({ - required this.peerId, - required this.deviceName, - required this.platform, - DateTime? firstConnected, - DateTime? lastConnected, - this.isApproved = false, - }) : firstConnected = firstConnected ?? DateTime.now(), - lastConnected = lastConnected ?? DateTime.now(); - - factory TrustedDevice.fromJson(Map json) => _$TrustedDeviceFromJson(json); - - Map toJson() => _$TrustedDeviceToJson(this); - - TrustedDevice copyWith({ - String? peerId, - String? deviceName, - String? platform, - DateTime? firstConnected, - DateTime? lastConnected, - bool? isApproved, - }) { - return TrustedDevice( - peerId: peerId ?? this.peerId, - deviceName: deviceName ?? this.deviceName, - platform: platform ?? this.platform, - firstConnected: firstConnected ?? this.firstConnected, - lastConnected: lastConnected ?? this.lastConnected, - isApproved: isApproved ?? this.isApproved, - ); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - return other is TrustedDevice && other.peerId == peerId; - } - - @override - int get hashCode => peerId.hashCode; -} diff --git a/lib/models/companion_remote/trusted_device.g.dart b/lib/models/companion_remote/trusted_device.g.dart deleted file mode 100644 index 38a195dc..00000000 --- a/lib/models/companion_remote/trusted_device.g.dart +++ /dev/null @@ -1,25 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'trusted_device.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -TrustedDevice _$TrustedDeviceFromJson(Map json) => TrustedDevice( - peerId: json['peerId'] as String, - deviceName: json['deviceName'] as String, - platform: json['platform'] as String, - firstConnected: json['firstConnected'] == null ? null : DateTime.parse(json['firstConnected'] as String), - lastConnected: json['lastConnected'] == null ? null : DateTime.parse(json['lastConnected'] as String), - isApproved: json['isApproved'] as bool? ?? false, -); - -Map _$TrustedDeviceToJson(TrustedDevice instance) => { - 'peerId': instance.peerId, - 'deviceName': instance.deviceName, - 'platform': instance.platform, - 'firstConnected': instance.firstConnected.toIso8601String(), - 'lastConnected': instance.lastConnected.toIso8601String(), - 'isApproved': instance.isApproved, -}; diff --git a/lib/mpv/player/player_android.dart b/lib/mpv/player/platform/player_android.dart similarity index 100% rename from lib/mpv/player/player_android.dart rename to lib/mpv/player/platform/player_android.dart diff --git a/lib/mpv/player/player.dart b/lib/mpv/player/player.dart index 8bd629f8..4c512d18 100644 --- a/lib/mpv/player/player.dart +++ b/lib/mpv/player/player.dart @@ -1,7 +1,7 @@ import 'dart:io' show Platform; import '../models.dart'; -import 'player_android.dart'; +import 'platform/player_android.dart'; import 'player_native.dart'; import 'player_state.dart'; import 'player_streams.dart'; diff --git a/lib/providers/companion_remote_provider.dart b/lib/providers/companion_remote_provider.dart index 3e9f5107..f7c2f9a0 100644 --- a/lib/providers/companion_remote_provider.dart +++ b/lib/providers/companion_remote_provider.dart @@ -1,31 +1,23 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:device_info_plus/device_info_plus.dart'; import '../models/companion_remote/remote_command.dart'; -import '../models/companion_remote/remote_command_type.dart'; import '../models/companion_remote/remote_session.dart'; -import '../models/companion_remote/trusted_device.dart'; import '../services/companion_remote/companion_remote_peer_service.dart'; -import '../services/storage_service.dart'; import '../utils/app_logger.dart'; typedef CommandReceivedCallback = void Function(RemoteCommand command); -typedef DeviceApprovalCallback = Future Function(RemoteDevice device); class CompanionRemoteProvider with ChangeNotifier { RemoteSession? _session; CompanionRemotePeerService? _peerService; String _deviceName = 'Unknown Device'; String _platform = 'unknown'; - final List _trustedDevices = []; bool _isPlayerActive = false; - static const String _storageKey = 'companion_remote_trusted_devices'; - static const String _lastDeviceKey = 'companion_remote_last_device'; static const int _maxReconnectAttempts = 5; Timer? _reconnectTimer; @@ -44,7 +36,6 @@ class CompanionRemoteProvider with ChangeNotifier { StreamSubscription? _statusSubscription; CommandReceivedCallback? onCommandReceived; - DeviceApprovalCallback? onDeviceApprovalRequired; bool get isInSession => _session != null && _session!.status != RemoteSessionStatus.disconnected; bool get isHost => _session?.isHost ?? false; @@ -55,12 +46,10 @@ class CompanionRemoteProvider with ChangeNotifier { String? get sessionId => _session?.sessionId; String? get pin => _session?.pin; RemoteDevice? get connectedDevice => _session?.connectedDevice; - List get trustedDevices => List.unmodifiable(_trustedDevices); bool get isPlayerActive => _isPlayerActive; CompanionRemoteProvider() { _initializeDeviceInfo(); - _loadTrustedDevices(); } Future _initializeDeviceInfo() async { @@ -117,12 +106,10 @@ class CompanionRemoteProvider with ChangeNotifier { }, ); - _deviceConnectedSubscription = _peerService!.onDeviceConnected.listen((device) async { + _deviceConnectedSubscription = _peerService!.onDeviceConnected.listen((device) { appLogger.d('CompanionRemote: Device connected: ${device.name}'); _session = _session?.copyWith(status: RemoteSessionStatus.connected, connectedDevice: device); notifyListeners(); - - await addTrustedDevice(device, requireApproval: isHost); }); _deviceDisconnectedSubscription = _peerService!.onDeviceDisconnected.listen((_) { @@ -375,92 +362,6 @@ class CompanionRemoteProvider with ChangeNotifier { notifyListeners(); } - Future _loadTrustedDevices() async { - try { - final storage = await StorageService.getInstance(); - final json = storage.prefs.getString(_storageKey); - if (json != null) { - final List list = jsonDecode(json); - _trustedDevices.clear(); - _trustedDevices.addAll(list.map((e) => TrustedDevice.fromJson(e as Map))); - appLogger.d('CompanionRemote: Loaded ${_trustedDevices.length} trusted devices'); - } - } catch (e) { - appLogger.e('CompanionRemote: Failed to load trusted devices', error: e); - } - } - - Future _saveTrustedDevices() async { - try { - final storage = await StorageService.getInstance(); - final json = jsonEncode(_trustedDevices.map((e) => e.toJson()).toList()); - await storage.prefs.setString(_storageKey, json); - appLogger.d('CompanionRemote: Saved ${_trustedDevices.length} trusted devices'); - } catch (e) { - appLogger.e('CompanionRemote: Failed to save trusted devices', error: e); - } - } - - bool isDeviceTrusted(String peerId) { - return _trustedDevices.any((d) => d.peerId == peerId && d.isApproved); - } - - Future addTrustedDevice(RemoteDevice device, {bool requireApproval = true}) async { - final existing = _trustedDevices.where((d) => d.peerId == device.id).firstOrNull; - - if (existing != null) { - final updated = existing.copyWith( - deviceName: device.name, - platform: device.platform, - lastConnected: DateTime.now(), - isApproved: !requireApproval || existing.isApproved, - ); - _trustedDevices.remove(existing); - _trustedDevices.add(updated); - } else { - bool approved = !requireApproval; - - if (requireApproval && onDeviceApprovalRequired != null) { - approved = await onDeviceApprovalRequired!(device); - } - - _trustedDevices.add( - TrustedDevice(peerId: device.id, deviceName: device.name, platform: device.platform, isApproved: approved), - ); - } - - await _saveTrustedDevices(); - - if (isRemote) { - final storage = await StorageService.getInstance(); - await storage.prefs.setString(_lastDeviceKey, device.id); - } - - notifyListeners(); - } - - Future removeTrustedDevice(String peerId) async { - _trustedDevices.removeWhere((d) => d.peerId == peerId); - await _saveTrustedDevices(); - notifyListeners(); - } - - Future approveTrustedDevice(String peerId) async { - final device = _trustedDevices.where((d) => d.peerId == peerId).firstOrNull; - if (device != null) { - final updated = device.copyWith(isApproved: true); - _trustedDevices.remove(device); - _trustedDevices.add(updated); - await _saveTrustedDevices(); - notifyListeners(); - } - } - - Future getLastConnectedDevicePeerId() async { - final storage = await StorageService.getInstance(); - return storage.prefs.getString(_lastDeviceKey); - } - @override void dispose() { _reconnectTimer?.cancel(); diff --git a/lib/screens/companion_remote/mobile_remote_screen.dart b/lib/screens/companion_remote/mobile_remote_screen.dart index 677b70b7..5c51dae4 100644 --- a/lib/screens/companion_remote/mobile_remote_screen.dart +++ b/lib/screens/companion_remote/mobile_remote_screen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; -import '../../models/companion_remote/remote_command_type.dart'; +import '../../models/companion_remote/remote_command.dart'; import '../../models/companion_remote/remote_session.dart'; import '../../i18n/strings.g.dart'; import '../../providers/companion_remote_provider.dart'; diff --git a/lib/widgets/alpha_jump_bar.dart b/lib/screens/libraries/alpha_jump_bar.dart similarity index 99% rename from lib/widgets/alpha_jump_bar.dart rename to lib/screens/libraries/alpha_jump_bar.dart index f06e69bb..6fa0dd49 100644 --- a/lib/widgets/alpha_jump_bar.dart +++ b/lib/screens/libraries/alpha_jump_bar.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import '../models/plex_first_character.dart'; +import '../../models/plex_first_character.dart'; import 'alpha_jump_helper.dart'; /// Vertical strip of letters for jumping through sorted library items. diff --git a/lib/widgets/alpha_jump_helper.dart b/lib/screens/libraries/alpha_jump_helper.dart similarity index 97% rename from lib/widgets/alpha_jump_helper.dart rename to lib/screens/libraries/alpha_jump_helper.dart index 5c7ae1e4..ac92ad8c 100644 --- a/lib/widgets/alpha_jump_helper.dart +++ b/lib/screens/libraries/alpha_jump_helper.dart @@ -1,5 +1,5 @@ -import '../data/ducet_order.dart'; -import '../models/plex_first_character.dart'; +import '../../data/ducet_order.dart'; +import '../../models/plex_first_character.dart'; /// Shared letter-index mapping logic used by both [AlphaJumpBar] (desktop/tablet/TV) /// and [AlphaScrollHandle] (phone). diff --git a/lib/widgets/alpha_scroll_handle.dart b/lib/screens/libraries/alpha_scroll_handle.dart similarity index 99% rename from lib/widgets/alpha_scroll_handle.dart rename to lib/screens/libraries/alpha_scroll_handle.dart index b34fbb26..4b097433 100644 --- a/lib/widgets/alpha_scroll_handle.dart +++ b/lib/screens/libraries/alpha_scroll_handle.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import '../models/plex_first_character.dart'; +import '../../models/plex_first_character.dart'; import 'alpha_jump_helper.dart'; /// Phone-optimized draggable scroll handle that appears on scroll and shows diff --git a/lib/screens/libraries/tabs/library_browse_tab.dart b/lib/screens/libraries/tabs/library_browse_tab.dart index 276975e9..a4887a1b 100644 --- a/lib/screens/libraries/tabs/library_browse_tab.dart +++ b/lib/screens/libraries/tabs/library_browse_tab.dart @@ -14,9 +14,9 @@ import '../../../providers/settings_provider.dart'; import '../../../utils/error_message_utils.dart'; import '../../../utils/grid_size_calculator.dart'; import '../../../utils/layout_constants.dart'; -import '../../../widgets/alpha_jump_bar.dart'; -import '../../../widgets/alpha_jump_helper.dart'; -import '../../../widgets/alpha_scroll_handle.dart'; +import '../alpha_jump_bar.dart'; +import '../alpha_jump_helper.dart'; +import '../alpha_scroll_handle.dart'; import '../../../widgets/focusable_media_card.dart'; import '../../../widgets/media_card.dart'; import '../../../widgets/focusable_filter_chip.dart'; diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index b2629ff8..bb844658 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -22,7 +22,7 @@ import '../../providers/settings_provider.dart'; import '../../providers/theme_provider.dart'; import '../../providers/user_profile_provider.dart'; import '../../services/keyboard_shortcuts_service.dart'; -import '../../mpv/player/player_android.dart'; +import '../../mpv/player/platform/player_android.dart'; import '../../services/settings_service.dart' as settings; import '../../services/update_service.dart'; import '../../utils/snackbar_helper.dart'; diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index 7c4899f4..bba46692 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -11,7 +11,7 @@ import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:window_manager/window_manager.dart'; import '../mpv/mpv.dart'; -import '../mpv/player/player_android.dart'; +import '../mpv/player/platform/player_android.dart'; import '../../services/plex_client.dart'; import '../models/livetv_channel.dart'; @@ -24,7 +24,7 @@ import '../models/plex_media_info.dart'; import '../providers/download_provider.dart'; import '../providers/multi_server_provider.dart'; import '../providers/playback_state_provider.dart'; -import '../models/companion_remote/remote_command_type.dart'; +import '../models/companion_remote/remote_command.dart'; import '../providers/companion_remote_provider.dart'; import '../services/companion_remote/companion_remote_receiver.dart'; import '../services/fullscreen_state_manager.dart'; diff --git a/lib/services/companion_remote/companion_remote_peer_service.dart b/lib/services/companion_remote/companion_remote_peer_service.dart index fb6f0559..a394da60 100644 --- a/lib/services/companion_remote/companion_remote_peer_service.dart +++ b/lib/services/companion_remote/companion_remote_peer_service.dart @@ -6,7 +6,6 @@ import 'dart:math'; import 'package:web_socket_channel/io.dart'; import '../../models/companion_remote/remote_command.dart'; -import '../../models/companion_remote/remote_command_type.dart'; import '../../models/companion_remote/remote_session.dart'; import '../../utils/app_logger.dart'; diff --git a/lib/services/companion_remote/companion_remote_receiver.dart b/lib/services/companion_remote/companion_remote_receiver.dart index bd31cb77..d04d80d7 100644 --- a/lib/services/companion_remote/companion_remote_receiver.dart +++ b/lib/services/companion_remote/companion_remote_receiver.dart @@ -2,7 +2,6 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import '../../models/companion_remote/remote_command.dart'; -import '../../models/companion_remote/remote_command_type.dart'; import '../../utils/app_logger.dart'; import '../../utils/key_event_simulator.dart'; diff --git a/lib/services/fullscreen_window_delegate.dart b/lib/services/fullscreen_window_delegate.dart deleted file mode 100644 index 276cd70a..00000000 --- a/lib/services/fullscreen_window_delegate.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'fullscreen_state_manager.dart'; -import 'macos_window_delegate.dart'; - -/// Custom window delegate that manages fullscreen state -/// Note: Window manipulation (toolbar, titlebar, traffic lights) is now handled -/// directly in Swift's WindowDelegate. This class only updates Dart-side state. -class FullscreenWindowDelegate extends MacOSWindowDelegate { - @override - void windowWillEnterFullScreen() { - FullscreenStateManager().setFullscreen(true); - } - - @override - void windowDidExitFullScreen() { - FullscreenStateManager().setFullscreen(false); - } -} diff --git a/lib/services/macos_titlebar_service.dart b/lib/services/macos_titlebar_service.dart deleted file mode 100644 index cc302806..00000000 --- a/lib/services/macos_titlebar_service.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'dart:io' show Platform; -import 'fullscreen_window_delegate.dart'; -import 'macos_window_service.dart'; - -/// Service to manage macOS titlebar configuration -class MacOSTitlebarService { - static bool _initialized = false; - - /// Initialize the custom titlebar setup. - /// - /// Note: The initial window configuration (transparent titlebar, toolbar, - /// button positions, fullscreen presentation options) is now applied in - /// MainFlutterWindow.swift / WindowDelegate.swift BEFORE frame restoration - /// to prevent the window from shrinking on launch. - /// - /// This method only sets up the Dart-side callbacks. - static Future setupCustomTitlebar() async { - if (!Platform.isMacOS || _initialized) return; - _initialized = true; - - await MacOSWindowService.initialize(enableWindowDelegate: true); - final delegate = FullscreenWindowDelegate(); - MacOSWindowService.addWindowDelegate(delegate); - } -} diff --git a/lib/services/macos_window_delegate.dart b/lib/services/macos_window_delegate.dart deleted file mode 100644 index 215b032a..00000000 --- a/lib/services/macos_window_delegate.dart +++ /dev/null @@ -1,20 +0,0 @@ -/// Abstract class for receiving macOS window delegate callbacks. -/// Extend this class and register with MacOSWindowService to receive -/// fullscreen transition events. -abstract class MacOSWindowDelegate { - /// Called when the window is about to enter fullscreen mode. - // ignore: no-empty-block - default no-op, subclasses override as needed - void windowWillEnterFullScreen() {} - - /// Called when the window has entered fullscreen mode. - // ignore: no-empty-block - default no-op, subclasses override as needed - void windowDidEnterFullScreen() {} - - /// Called when the window is about to exit fullscreen mode. - // ignore: no-empty-block - default no-op, subclasses override as needed - void windowWillExitFullScreen() {} - - /// Called when the window has exited fullscreen mode. - // ignore: no-empty-block - default no-op, subclasses override as needed - void windowDidExitFullScreen() {} -} diff --git a/lib/services/macos_window_service.dart b/lib/services/macos_window_service.dart index c301c866..770c6616 100644 --- a/lib/services/macos_window_service.dart +++ b/lib/services/macos_window_service.dart @@ -1,6 +1,27 @@ import 'dart:io' show Platform; import 'package:flutter/services.dart'; -import 'macos_window_delegate.dart'; +import 'fullscreen_state_manager.dart'; + +/// Abstract class for receiving macOS window delegate callbacks. +/// Extend this class and register with [MacOSWindowService] to receive +/// fullscreen transition events. +abstract class MacOSWindowDelegate { + /// Called when the window is about to enter fullscreen mode. + // ignore: no-empty-block - default no-op, subclasses override as needed + void windowWillEnterFullScreen() {} + + /// Called when the window has entered fullscreen mode. + // ignore: no-empty-block - default no-op, subclasses override as needed + void windowDidEnterFullScreen() {} + + /// Called when the window is about to exit fullscreen mode. + // ignore: no-empty-block - default no-op, subclasses override as needed + void windowWillExitFullScreen() {} + + /// Called when the window has exited fullscreen mode. + // ignore: no-empty-block - default no-op, subclasses override as needed + void windowDidExitFullScreen() {} +} /// Service for manipulating macOS window properties. /// This is a native implementation replacing the macos_window_utils package. @@ -45,6 +66,21 @@ class MacOSWindowService { // MARK: - Initialization + /// Initialize the window service and set up the titlebar. + /// + /// Note: The initial window configuration (transparent titlebar, toolbar, + /// button positions, fullscreen presentation options) is now applied in + /// MainFlutterWindow.swift / WindowDelegate.swift BEFORE frame restoration + /// to prevent the window from shrinking on launch. + /// + /// This method sets up the Dart-side callbacks for fullscreen state tracking. + static Future setupCustomTitlebar() async { + if (!Platform.isMacOS || _initialized) return; + + await initialize(enableWindowDelegate: true); + addWindowDelegate(_FullscreenWindowDelegate()); + } + /// Initialize the window service. /// Must be called before using other methods. /// Set [enableWindowDelegate] to true to receive fullscreen callbacks. @@ -94,3 +130,18 @@ class MacOSWindowService { return await _channel.invokeMethod('isFullscreen') ?? false; } } + +/// Internal window delegate that manages fullscreen state. +/// Note: Window manipulation (toolbar, titlebar, traffic lights) is now handled +/// directly in Swift's WindowDelegate. This class only updates Dart-side state. +class _FullscreenWindowDelegate extends MacOSWindowDelegate { + @override + void windowWillEnterFullScreen() { + FullscreenStateManager().setFullscreen(true); + } + + @override + void windowDidExitFullScreen() { + FullscreenStateManager().setFullscreen(false); + } +} diff --git a/lib/widgets/video_controls/widgets/performance_overlay/performance_stats_service.dart b/lib/widgets/video_controls/widgets/performance_overlay/performance_stats_service.dart index d85fedb4..be11cb26 100644 --- a/lib/widgets/video_controls/widgets/performance_overlay/performance_stats_service.dart +++ b/lib/widgets/video_controls/widgets/performance_overlay/performance_stats_service.dart @@ -4,7 +4,7 @@ import 'dart:io' show ProcessInfo; import 'package:flutter/scheduler.dart'; import '../../../../mpv/mpv.dart'; -import '../../../../mpv/player/player_android.dart'; +import '../../../../mpv/player/platform/player_android.dart'; import '../../../../utils/app_logger.dart'; import 'performance_stats.dart';