diff --git a/README.md b/README.md index 32513a58..5d0a24e5 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ | [x86_64 `.apk`](https://github.com/bluecherrydvr/unity/releases/download/v3.0.0-beta23/bluecherry-android-x86_64-release.apk) | | 🚧 **SOON** ~~Microsoft Store~~ | [Raw Executable `.tar.gz`](https://github.com/bluecherrydvr/unity/releases/download/v3.0.0-beta23/bluecherry-linux-x86_64.tar.gz) | | | 🚧 **SOON** ~~Play Store~~ | | | [Fedora/Red Hat Linux `.rpm`](https://github.com/bluecherrydvr/unity/releases/download/v3.0.0-beta23/bluecherry-linux-x86_64.rpm) | | -Or download the latest release [here](https://github.com/bluecherrydvr/unity/releases/tag/bleeding_edge)*. +Or download the latest release [here](https://github.com/bluecherrydvr/unity/releases/tag/bleeding_edge)\*. ### Installation @@ -174,13 +174,13 @@ We support multiple platforms and each platform uses its own rendering backend. | ------------ | ----------------- | --------------- | | Android | MPV | media_kit | | iOS | MPV | media_kit | -| Windows | MPV | media_kit | -| MacOS | MPV | media_kit | -| Web | HTML5 | fvp | +| Windows | MDK | fvp | +| MacOS | MDK | fvp | +| Web | HTML5 | media_kit | | Linux | MDK | fvp | | Raspberry Pi | MDK | fvp | -MDK is used for Linux and Raspberry Pi because MPV has shown to be unstable on these platforms, causing crashes. Additionally, `fvp` doesn't require the user to install any additional dependencies. +MDK is used for Desktop platforms because MPV has shown to be unstable on these platforms, causing crashes. Additionally, `fvp` doesn't require the user to install any additional dependencies on Linux and Raspberry Pi. ### Build @@ -221,3 +221,11 @@ When running on debug, you must disable the CORS policy in your browser. Note th ```bash flutter run -d chrome --web-browser-flag "--disable-web-security" ``` + +### Running and debugging + +If running on a desktop platform, it is possible to emulate a mobile platform by passing the `--dart-define="FORCE_MOBILE=true"` argument when running. This will force the app to adapt itself to the mobile layout. This will remove any desktop-specific features and will make the app look like a mobile app. + +```bash +flutter run -d [windows|linux|macos] --dart-define="FORCE_MOBILE=true" +``` \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index c0bf0f39..e0dae8e0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -94,6 +94,10 @@ Future main(List args) async { runApp(const SplashScreen()); } + if (kForceMobile) { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + } + DevHttpOverrides.configureCertificates(); API.initialize(); await UnityVideoPlayerInterface.instance.initialize(); diff --git a/lib/screens/layouts/desktop/multicast_view.dart b/lib/screens/layouts/desktop/multicast_view.dart index 4041ee44..d8b9fca6 100644 --- a/lib/screens/layouts/desktop/multicast_view.dart +++ b/lib/screens/layouts/desktop/multicast_view.dart @@ -24,6 +24,7 @@ import 'package:bluecherry_client/models/device.dart'; import 'package:bluecherry_client/providers/layouts_provider.dart'; import 'package:bluecherry_client/providers/settings_provider.dart'; import 'package:bluecherry_client/utils/constants.dart'; +import 'package:bluecherry_client/utils/methods.dart'; import 'package:bluecherry_client/widgets/hover_button.dart'; import 'package:bluecherry_client/widgets/misc.dart'; import 'package:flutter/gestures.dart'; @@ -33,8 +34,9 @@ import 'package:unity_video_player/unity_video_player.dart'; class MulticastViewport extends StatefulWidget { final Device? device; + final bool showMobileGrid; - const MulticastViewport({super.key, this.device}); + const MulticastViewport({super.key, this.device, this.showMobileGrid = true}); @override State createState() => _MulticastViewportState(); @@ -187,13 +189,14 @@ class _MulticastViewportState extends State { (context, states) => SizedBox.expand( child: IgnorePointer( child: - states.isHovering + (isMobile && widget.showMobileGrid) || + states.isHovering ? Container( decoration: BoxDecoration( border: Border.all( color: theme.colorScheme.secondary, - width: 2.25, + width: isMobile ? 0.5 : 2.25, ), ), ) diff --git a/lib/screens/players/live_player.dart b/lib/screens/players/live_player.dart index 0d01ff19..971e0e31 100644 --- a/lib/screens/players/live_player.dart +++ b/lib/screens/players/live_player.dart @@ -101,7 +101,7 @@ class _LivePlayerState extends State { @override Widget build(BuildContext context) { - if (isMobilePlatform) { + if (isMobile) { return _MobileLivePlayer( player: widget.player, device: widget.device, @@ -133,12 +133,14 @@ class _MobileLivePlayer extends StatefulWidget { } class __MobileLivePlayerState extends State<_MobileLivePlayer> { - bool overlay = true; + var overlay = true; late UnityVideoFit fit = widget.device.server.additionalSettings.videoFit ?? SettingsProvider.instance.kVideoFit.value; late bool ptzEnabled = widget.ptzEnabled; + var showMagnificationGrid = + SettingsProvider.instance.kMatrixedZoomEnabled.value; @override void initState() { @@ -197,8 +199,26 @@ class __MobileLivePlayerState extends State<_MobileLivePlayer> { strokeWidth: 3.45, ), ) - else if (commands.isNotEmpty) - PTZData(commands: commands), + else ...[ + if (showMagnificationGrid) + Positioned.fill( + child: Center( + child: AspectRatio( + aspectRatio: + controller.aspectRatio == 0 || + controller.aspectRatio == + double.infinity + ? 16 / 9 + : controller.aspectRatio, + child: MulticastViewport( + device: widget.device, + showMobileGrid: showMagnificationGrid, + ), + ), + ), + ), + if (commands.isNotEmpty) PTZData(commands: commands), + ], PositionedDirectional( top: 0.0, start: 0.0, @@ -235,12 +255,29 @@ class __MobileLivePlayerState extends State<_MobileLivePlayer> { trailing: Row( mainAxisSize: MainAxisSize.min, children: [ - CameraViewFitButton( - fit: fit, - onChanged: (newFit) { - setState(() => fit = newFit); - }, + SquaredIconButton( + icon: Icon( + showMagnificationGrid + ? Icons.grid_on_rounded + : Icons.grid_off_rounded, + ), + tooltip: + showMagnificationGrid + ? 'Disable magnification grid' + : 'Enable magnification grid', + onPressed: + () => setState( + () => + showMagnificationGrid = + !showMagnificationGrid, + ), ), + // CameraViewFitButton( + // fit: fit, + // onChanged: (newFit) { + // setState(() => fit = newFit); + // }, + // ), if (widget.device.hasPTZ) PTZToggleButton( ptzEnabled: ptzEnabled, diff --git a/lib/screens/settings/server_and_devices.dart b/lib/screens/settings/server_and_devices.dart index 909954c0..7b76cb25 100644 --- a/lib/screens/settings/server_and_devices.dart +++ b/lib/screens/settings/server_and_devices.dart @@ -504,6 +504,8 @@ class AreaMagnificationSettings extends StatelessWidget { ), OptionsChooserTile( title: loc.defaultMatrixSize, + description: + 'Double tap on the view to change the magnification area size', icon: Icons.view_quilt, value: settings.kMatrixSize.value, values: MatrixType.values.map((size) { diff --git a/lib/utils/methods.dart b/lib/utils/methods.dart index f4a3f6fc..9be401e6 100644 --- a/lib/utils/methods.dart +++ b/lib/utils/methods.dart @@ -74,6 +74,10 @@ Widget wrapExpandedIf(bool condition, {required Widget child}) { return child; } +/// Whether the app should force mobile mode. This is useful for testing mobile +/// specific UI elements on a desktop platform. +const kForceMobile = bool.fromEnvironment('FORCE_MOBILE', defaultValue: false); + /// Returns true if the app is running on a desktop platform. This is useful /// for determining whether to show desktop-specific UI elements. ///