From 5d5bb7d44573709186c7ff56a1d410a9e8e9885a Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 15:41:58 -0300 Subject: [PATCH 01/15] fix: Compress AppImage output to gzip --- AppImageBuilder.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml index c03e9a86..29559fa2 100644 --- a/AppImageBuilder.yml +++ b/AppImageBuilder.yml @@ -84,5 +84,5 @@ AppDir: command: ./AppRun AppImage: arch: x86_64 - comp: None + comp: gzip update-information: guess From 837d083eeb1737fcc5eadef7f8213d3904327a8f Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 22:07:35 -0300 Subject: [PATCH 02/15] feat: Improve error message when a server with an old version is requested. --- lib/api/api.dart | 30 ++++++++++++++----- lib/api/api_helpers.dart | 2 +- ...firebase_messaging_background_handler.dart | 4 +-- lib/l10n/app_en.arb | 3 +- lib/l10n/app_fr.arb | 1 + lib/l10n/app_pl.arb | 1 + lib/l10n/app_pt.arb | 1 + lib/providers/events_provider.dart | 2 +- lib/providers/server_provider.dart | 2 +- .../layouts/desktop/layout_manager.dart | 2 -- lib/screens/servers/configure_dvr_server.dart | 20 +++++++++---- lib/screens/servers/edit_server.dart | 6 ++-- 12 files changed, 51 insertions(+), 23 deletions(-) diff --git a/lib/api/api.dart b/lib/api/api.dart index 681fb04d..c4172788 100644 --- a/lib/api/api.dart +++ b/lib/api/api.dart @@ -29,6 +29,12 @@ import 'package:xml2json/xml2json.dart'; export 'events.dart'; export 'ptz.dart'; +enum ServerAdditionResponse { + validated, + versionMismatch, + unknown; +} + class API { static final API instance = API(); @@ -49,7 +55,10 @@ class API { /// If the attributes present in [Server] are correct, then the /// returned object will have [Server.serverUUID] & [Server.cookie] /// present in it otherwise `null`. - Future checkServerCredentials(Server server) async { + Future<(ServerAdditionResponse response, Server server)> + checkServerCredentials( + Server server, + ) async { debugPrint('Checking server credentials for server ${server.id}'); try { final uri = Uri.https( @@ -79,12 +88,19 @@ class API { ); if (response.statusCode == 200) { + if (body == 'Route error!') { + server.online = false; + return (ServerAdditionResponse.versionMismatch, server); + } final json = await compute(jsonDecode, body); - return server.copyWith( - serverUUID: json['server_uuid'], - cookie: - response.headers['set-cookie'] ?? response.headers['Set-Cookie'], - online: true, + return ( + ServerAdditionResponse.validated, + server.copyWith( + serverUUID: json['server_uuid'], + cookie: response.headers['set-cookie'] ?? + response.headers['Set-Cookie'], + online: true, + ) ); } else { debugPrint(body); @@ -97,7 +113,7 @@ class API { server.online = false; } - return server; + return (ServerAdditionResponse.unknown, server); } /// Gets [Device] devices present on the [server] after login. diff --git a/lib/api/api_helpers.dart b/lib/api/api_helpers.dart index 573199a1..0c6b32d4 100644 --- a/lib/api/api_helpers.dart +++ b/lib/api/api_helpers.dart @@ -87,7 +87,7 @@ abstract class APIHelpers { int attempts = 50, }) async { final events = await API.instance.getEvents( - await API.instance.checkServerCredentials(server), + (await API.instance.checkServerCredentials(server)).$2, ); debugPrint(events.map((e) => e.id).toList().toString()); Future getThumbnailForMediaID(int mediaID) async { diff --git a/lib/firebase_messaging_background_handler.dart b/lib/firebase_messaging_background_handler.dart index 50e5d8a6..61e5775e 100644 --- a/lib/firebase_messaging_background_handler.dart +++ b/lib/firebase_messaging_background_handler.dart @@ -437,7 +437,7 @@ abstract class FirebaseConfiguration { storage.add({kStorageNotificationToken: token}); for (final server in ServersProvider.instance.servers) { API.instance.registerNotificationToken( - await API.instance.checkServerCredentials(server), + (await API.instance.checkServerCredentials(server)).$2, token, ); } @@ -456,7 +456,7 @@ abstract class FirebaseConfiguration { await storage.add({kStorageNotificationToken: token}); for (final server in ServersProvider.instance.servers) { API.instance.registerNotificationToken( - await API.instance.checkServerCredentials(server), + (await API.instance.checkServerCredentials(server)).$2, token, ); } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 92780c6c..bd647aea 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -64,7 +64,7 @@ } } }, - "serverAlreadyAdded": "The {serverName} server is already added", + "serverAlreadyAdded": "The {serverName} server is already added.", "@serverAlreadyAdded": { "placeholders": { "serverName": { @@ -72,6 +72,7 @@ } } }, + "serverVersionMismatch": "Tried to add a server with an unsupported version. Please upgrade your server and try again!", "noServersAvailable": "No servers available", "error": "Error", "videoError": "An error happened while trying to play the video.", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 8cede66e..5cd1f81e 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -68,6 +68,7 @@ } } }, + "serverVersionMismatch": "Tried to add a server with an unsupported version. Please upgrade your server and try again!", "noServersAvailable": "Aucun serveur disponible", "error": "Erreur", "videoError": "Une erreur est survenue lors de la lecture de la vidé.", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 306c2c56..6b7ae496 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -72,6 +72,7 @@ } } }, + "serverVersionMismatch": "Tried to add a server with an unsupported version. Please upgrade your server and try again!", "noServersAvailable": "Brak dostępnych serwerów", "error": "Błąd", "videoError": "An error happened while trying to play the video.", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 2a7d0f1b..b1fffd83 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -72,6 +72,7 @@ } } }, + "serverVersionMismatch": "O versão do servidor não é suportada. Por favor, atualize seu servidor e tente novamente!", "noServersAvailable": "Nenhum servidor disponível.", "error": "Erro", "videoError": "Ocorreu um erro ao tentar reproduzir o vídeo.", diff --git a/lib/providers/events_provider.dart b/lib/providers/events_provider.dart index e1a58205..fc82d69a 100644 --- a/lib/providers/events_provider.dart +++ b/lib/providers/events_provider.dart @@ -129,7 +129,7 @@ extension EventsScreenProvider on EventsProvider { await Future.wait(ServersProvider.instance.servers.map((server) async { if (!server.online || server.devices.isEmpty) return; - server = await API.instance.checkServerCredentials(server); + (_, server) = await API.instance.checkServerCredentials(server); try { final allowedDevices = server.devices diff --git a/lib/providers/server_provider.dart b/lib/providers/server_provider.dart index 5e9d542d..0fe94d28 100644 --- a/lib/providers/server_provider.dart +++ b/lib/providers/server_provider.dart @@ -162,7 +162,7 @@ class ServersProvider extends UnityProvider { notifyListeners(); } - server = await API.instance.checkServerCredentials(server); + (_, server) = await API.instance.checkServerCredentials(server); final devices = await API.instance.getDevices(server); if (devices != null) { server.devices diff --git a/lib/screens/layouts/desktop/layout_manager.dart b/lib/screens/layouts/desktop/layout_manager.dart index fceda9f0..f57e94ee 100644 --- a/lib/screens/layouts/desktop/layout_manager.dart +++ b/lib/screens/layouts/desktop/layout_manager.dart @@ -77,8 +77,6 @@ class _LayoutManagerState extends State with Searchable { @override void dispose() { timer?.cancel(); - searchController.dispose(); - searchFocusNode.dispose(); super.dispose(); } diff --git a/lib/screens/servers/configure_dvr_server.dart b/lib/screens/servers/configure_dvr_server.dart index 96d49e17..dc8fcfb2 100644 --- a/lib/screens/servers/configure_dvr_server.dart +++ b/lib/screens/servers/configure_dvr_server.dart @@ -444,7 +444,7 @@ class _ConfigureDVRServerScreenState extends State { }); } final port = int.parse(portController.text.trim()); - final server = await API.instance.checkServerCredentials( + final (code, server) = await API.instance.checkServerCredentials( Server( name: name, ip: hostname, @@ -457,7 +457,9 @@ class _ConfigureDVRServerScreenState extends State { ); focusScope.unfocus(); - if (server.serverUUID != null && server.hasCookies) { + if (server.serverUUID != null && + server.hasCookies && + code == ServerAdditionResponse.validated) { widget.onServerChange(server); state = _ServerAddState.gettingDevices; await ServersProvider.instance.add(server); @@ -471,10 +473,16 @@ class _ConfigureDVRServerScreenState extends State { final loc = AppLocalizations.of(context); return ServerNotAddedErrorDialog( name: server.name, - description: loc.serverNotAddedErrorDescription( - server.port.toString(), - server.rtspPort.toString(), - ), + description: switch (code) { + ServerAdditionResponse.versionMismatch => + loc.serverVersionMismatch, + ServerAdditionResponse.unknown || + _ => + loc.serverNotAddedErrorDescription( + server.port.toString(), + server.rtspPort.toString(), + ) + }, onRetry: () { Navigator.of(context).maybePop(); if (this.context.mounted) finish(this.context); diff --git a/lib/screens/servers/edit_server.dart b/lib/screens/servers/edit_server.dart index 7f195228..555e8336 100644 --- a/lib/screens/servers/edit_server.dart +++ b/lib/screens/servers/edit_server.dart @@ -46,11 +46,13 @@ Future showEditServer(BuildContext context, Server server) { } Future updateServer(BuildContext context, Server serverCopy) async { - final updatedServer = await API.instance.checkServerCredentials( + final (code, updatedServer) = await API.instance.checkServerCredentials( serverCopy, ); - if (updatedServer.serverUUID != null && updatedServer.hasCookies) { + if (updatedServer.serverUUID != null && + updatedServer.hasCookies && + code == ServerAdditionResponse.validated) { await ServersProvider.instance.update(updatedServer); if (context.mounted) Navigator.of(context).pop(); From 7ded4e5e88790981427a53314be32e5865523a27 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 22:12:42 -0300 Subject: [PATCH 03/15] feat: Localizations for General settings --- lib/l10n/app_en.arb | 15 +++++++++------ lib/l10n/app_fr.arb | 15 +++++++++------ lib/l10n/app_pl.arb | 15 +++++++++------ lib/l10n/app_pt.arb | 15 +++++++++------ lib/screens/settings/general.dart | 7 ++----- 5 files changed, 38 insertions(+), 29 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index bd647aea..24b62377 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -216,8 +216,6 @@ "cameraRefreshPeriodDescription": "The interval between camera refreshes. It ensures the camera video is still valid from time to time.", "@Layouts": {}, "cycle": "Cycle", - "cycleTogglePeriod": "Layout cycle toggle period", - "cycleTogglePeriodDescription": "The interval between layout changes when the cycle mode is enabled.", "fallbackLayoutName": "Layout {layout}", "@fallbackLayoutName": { "placeholders": { @@ -465,6 +463,15 @@ "taskDownloadingEvent": "Downloading event", "@@@SETTINGS": {}, "defaultField": "Default", + "@@GENERAL": {}, + "general": "General", + "cycleTogglePeriod": "Layout cycle toggle period", + "cycleTogglePeriodDescription": "The interval between layout changes when the cycle mode is enabled.", + "notifications": "Notifications", + "notificationsEnabled": "Notifications enabled", + "notificationClickBehavior": "Notification Click Behavior", + "notificationClickBehaviorDescription": "Choose what happens when you click on a notification.", + "showEventsScreen": "Show events history", "@@APPEARANCE": {}, "theme": "Theme", "themeDescription": "Change the appearance of the app", @@ -472,7 +479,6 @@ "light": "Light", "dark": "Dark", "@@MISC": {}, - "general": "General", "miscellaneous": "Miscellaneous", "wakelock": "Keep screen awake", "wakelockDescription": "Keep screen awake while watching live streams or recordings.", @@ -492,9 +498,6 @@ } }, "@Notification click": {}, - "notificationClickBehavior": "Notification Click Behavior", - "notificationClickBehaviorDescription": "Choose what happens when you click on a notification.", - "showEventsScreen": "Show events history", "@@STREAMING": {}, "streamingSettings": "Streaming settings", "streamingType": "Streaming type", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 5cd1f81e..49047083 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -206,8 +206,6 @@ "cameraRefreshPeriodDescription": "The interval between camera refreshes. It ensures the camera video is still valid from time to time.", "@Layouts": {}, "cycle": "Cycle", - "cycleTogglePeriod": "Durée du cycle de basculement", - "cycleTogglePeriodDescription": "The interval between layout changes when the cycle mode is enabled.", "fallbackLayoutName": "Disposition {layout}", "@fallbackLayoutName": { "placeholders": { @@ -441,6 +439,15 @@ "taskDownloadingEvent": "Téléchargement de l'évènement", "@@@SETTINGS": {}, "defaultField": "Default", + "@@GENERAL": {}, + "general": "Général", + "cycleTogglePeriod": "Durée du cycle de basculement", + "cycleTogglePeriodDescription": "The interval between layout changes when the cycle mode is enabled.", + "notifications": "Notifications", + "notificationsEnabled": "Notifications enabled", + "notificationClickBehavior": "Action de clic sur les notifications", + "notificationClickBehaviorDescription": "Choose what happens when you click on a notification.", + "showEventsScreen": "Montrer le navigateur d'événements", "@@APPEARANCE": {}, "theme": "Thème", "themeDescription": "Modifier l'apparence de l'application", @@ -448,7 +455,6 @@ "light": "Clair", "dark": "Sombre", "@@MISC": {}, - "general": "Général", "miscellaneous": "Divers", "wakelock": "Keep screen on", "wakelockDescription": "Keep screen on while watching live streams or recordings.", @@ -466,9 +472,6 @@ } }, "@Notification click": {}, - "notificationClickBehavior": "Action de clic sur les notifications", - "notificationClickBehaviorDescription": "Choose what happens when you click on a notification.", - "showEventsScreen": "Montrer le navigateur d'événements", "@@STREAMING": {}, "streamingSettings": "Paramètre de diffusion", "streamingType": "Type de diffusion", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 6b7ae496..e1bec423 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -216,8 +216,6 @@ "cameraRefreshPeriodDescription": "The interval between camera refreshes. It ensures the camera video is still valid from time to time.", "@Layouts": {}, "cycle": "Cykl", - "cycleTogglePeriod": "Okres cyklicznego przełączania układu", - "cycleTogglePeriodDescription": "The interval between layout changes when the cycle mode is enabled.", "fallbackLayoutName": "Układ {layout}", "@fallbackLayoutName": { "placeholders": { @@ -465,6 +463,15 @@ "taskDownloadingEvent": "Pobieranie zdarzenia", "@@@SETTINGS": {}, "defaultField": "Default", + "@@GENERAL": {}, + "general": "General", + "cycleTogglePeriod": "Okres cyklicznego przełączania układu", + "cycleTogglePeriodDescription": "The interval between layout changes when the cycle mode is enabled.", + "notifications": "Notifications", + "notificationsEnabled": "Notifications enabled", + "notificationClickBehavior": "Zachowanie po kliknięciu na powiadomienie", + "notificationClickBehaviorDescription": "Choose what happens when you click on a notification.", + "showEventsScreen": "Pokaż historię zdarzeń", "@@APPEARANCE": {}, "theme": "Motyw", "themeDescription": "Zmień wygląd aplikacji", @@ -472,7 +479,6 @@ "light": "Jasny", "dark": "Ciemny", "@@MISC": {}, - "general": "General", "miscellaneous": "Różne", "wakelock": "Keep screen on", "wakelockDescription": "Keep screen on while watching live streams or recordings.", @@ -492,9 +498,6 @@ } }, "@Notification click": {}, - "notificationClickBehavior": "Zachowanie po kliknięciu na powiadomienie", - "notificationClickBehaviorDescription": "Choose what happens when you click on a notification.", - "showEventsScreen": "Pokaż historię zdarzeń", "@@STREAMING": {}, "streamingSettings": "Streaming settings", "streamingType": "Streaming type", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index b1fffd83..cc803f08 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -216,8 +216,6 @@ "cameraRefreshPeriodDescription": "O intervalo entre as atualizações da câmera. Isso garante que o vídeo da câmera ainda seja válido de tempos em tempos.", "@Layouts": {}, "cycle": "Ciclo", - "cycleTogglePeriod": "Duração da alternância de layouts", - "cycleTogglePeriodDescription": "O intervalo entre alterações de layout quando a alternância está ativada.", "fallbackLayoutName": "Layout {layout}", "@fallbackLayoutName": { "placeholders": { @@ -465,6 +463,15 @@ "taskDownloadingEvent": "Baixando evento", "@@@SETTINGS": {}, "defaultField": "Padrão", + "@@GENERAL": {}, + "general": "Geral", + "cycleTogglePeriod": "Duração da alternância de layouts", + "cycleTogglePeriodDescription": "O intervalo entre alterações de layout quando a alternância está ativada.", + "notifications": "Notifications", + "notificationsEnabled": "Notifications enabled", + "notificationClickBehavior": "Ação ao clicar na notificação", + "notificationClickBehaviorDescription": "Escolha o que acontece quando você clica em uma notificação.", + "showEventsScreen": "Mostar histórico de eventos", "@@APPEARANCE": {}, "theme": "Aparência", "themeDescription": "Mude a aparência do aplicativo", @@ -472,7 +479,6 @@ "light": "Claro", "dark": "Escuro", "@@MISC": {}, - "general": "Geral", "miscellaneous": "Outros", "wakelock": "Manter tela ativa", "wakelockDescription": "Mantenha a tela ativa enquanto estiver assistindo a transmissões ao vivo ou gravações", @@ -492,9 +498,6 @@ } }, "@Notification click": {}, - "notificationClickBehavior": "Ação ao clicar na notificação", - "notificationClickBehaviorDescription": "Escolha o que acontece quando você clica em uma notificação.", - "showEventsScreen": "Mostar histórico de eventos", "@@STREAMING": {}, "streamingSettings": "Configurações de streaming", "streamingType": "Tipo de streaming", diff --git a/lib/screens/settings/general.dart b/lib/screens/settings/general.dart index 84937c6c..f243f15b 100644 --- a/lib/screens/settings/general.dart +++ b/lib/screens/settings/general.dart @@ -67,10 +67,7 @@ class GeneralSettings extends StatelessWidget { settings.kWakelock.value = !settings.kWakelock.value; }, ), - const SubHeader( - 'Notifications', - padding: DesktopSettings.horizontalPadding, - ), + SubHeader(loc.notifications, padding: DesktopSettings.horizontalPadding), CheckboxListTile.adaptive( secondary: CircleAvatar( backgroundColor: Colors.transparent, @@ -78,7 +75,7 @@ class GeneralSettings extends StatelessWidget { child: const Icon(Icons.crop), ), contentPadding: DesktopSettings.horizontalPadding, - title: const Text('Notifications enabled'), + title: Text(loc.notificationsEnabled), value: settings.kNotificationsEnabled.value, onChanged: (value) { if (value != null) { From a9b9ba1ff749609a47c3cc05616f62797ca929c1 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 22:19:28 -0300 Subject: [PATCH 04/15] feat: Localization for events and downloads settings --- lib/l10n/app_en.arb | 7 +++++++ lib/l10n/app_fr.arb | 7 +++++++ lib/l10n/app_pl.arb | 7 +++++++ lib/l10n/app_pt.arb | 7 +++++++ .../settings/events_and_downloads.dart | 21 +++++++------------ 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 24b62377..42105b7b 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -472,6 +472,13 @@ "notificationClickBehavior": "Notification Click Behavior", "notificationClickBehaviorDescription": "Choose what happens when you click on a notification.", "showEventsScreen": "Show events history", + "@@EVENTS_AND_DOWNLOADS": {}, + "chooseEveryDownloadsLocation": "Choose the location for every download", + "chooseEveryDownloadsLocationDescription": "Whether to choose the location for each download or use the default location. When enabled, you will be prompted to choose the download directory for each download.", + "blockCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", + "events": "Events", + "initialEventSpeed": "Initial event speed", + "intiialEventVolume": "Initial event volume", "@@APPEARANCE": {}, "theme": "Theme", "themeDescription": "Change the appearance of the app", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 49047083..e81e5ed9 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -448,6 +448,13 @@ "notificationClickBehavior": "Action de clic sur les notifications", "notificationClickBehaviorDescription": "Choose what happens when you click on a notification.", "showEventsScreen": "Montrer le navigateur d'événements", + "@@EVENTS_AND_DOWNLOADS": {}, + "chooseEveryDownloadsLocation": "Choose the location for every download", + "chooseEveryDownloadsLocationDescription": "Whether to choose the location for each download or use the default location. When enabled, you will be prompted to choose the download directory for each download.", + "blockCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", + "events": "Events", + "initialEventSpeed": "Initial speed", + "intiialEventVolume": "Initial volume", "@@APPEARANCE": {}, "theme": "Thème", "themeDescription": "Modifier l'apparence de l'application", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index e1bec423..00c29fde 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -472,6 +472,13 @@ "notificationClickBehavior": "Zachowanie po kliknięciu na powiadomienie", "notificationClickBehaviorDescription": "Choose what happens when you click on a notification.", "showEventsScreen": "Pokaż historię zdarzeń", + "@@EVENTS_AND_DOWNLOADS": {}, + "chooseEveryDownloadsLocation": "Choose the location for every download", + "chooseEveryDownloadsLocationDescription": "Whether to choose the location for each download or use the default location. When enabled, you will be prompted to choose the download directory for each download.", + "blockCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", + "events": "Events", + "initialEventSpeed": "Initial speed", + "intiialEventVolume": "Initial volume", "@@APPEARANCE": {}, "theme": "Motyw", "themeDescription": "Zmień wygląd aplikacji", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index cc803f08..ddb6a479 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -472,6 +472,13 @@ "notificationClickBehavior": "Ação ao clicar na notificação", "notificationClickBehaviorDescription": "Escolha o que acontece quando você clica em uma notificação.", "showEventsScreen": "Mostar histórico de eventos", + "@@EVENTS_AND_DOWNLOADS": {}, + "chooseEveryDownloadsLocation": "Choose the location for every download", + "chooseEveryDownloadsLocationDescription": "Whether to choose the location for each download or use the default location. When enabled, you will be prompted to choose the download directory for each download.", + "blockCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", + "events": "Events", + "initialEventSpeed": "Initial speed", + "intiialEventVolume": "Initial volume", "@@APPEARANCE": {}, "theme": "Aparência", "themeDescription": "Mude a aparência do aplicativo", diff --git a/lib/screens/settings/events_and_downloads.dart b/lib/screens/settings/events_and_downloads.dart index 7fa375af..53319d65 100644 --- a/lib/screens/settings/events_and_downloads.dart +++ b/lib/screens/settings/events_and_downloads.dart @@ -51,12 +51,8 @@ class EventsAndDownloadsSettings extends StatelessWidget { foregroundColor: theme.iconTheme.color, child: const Icon(Icons.create_new_folder), ), - title: const Text('Choose location for each download'), - subtitle: const Text( - 'Whether to choose the location for each download or use the default ' - 'location. When enabled, you will be prompted to choose the download ' - 'directory for each download.', - ), + title: Text(loc.chooseEveryDownloadsLocation), + subtitle: Text(loc.chooseEveryDownloadsLocationDescription), ), ListTile( contentPadding: DesktopSettings.horizontalPadding, @@ -91,10 +87,9 @@ class EventsAndDownloadsSettings extends StatelessWidget { foregroundColor: theme.iconTheme.color, child: const Icon(Icons.close), ), - title: const Text( - 'Block the app from closing when there are ongoing downloads'), + title: Text(loc.blockCloseWhenDownloading), ), - const SubHeader('Events'), + SubHeader(loc.events), if (settings.kShowDebugInfo.value) CheckboxListTile.adaptive( value: settings.kPictureInPicture.value, @@ -121,7 +116,7 @@ class EventsAndDownloadsSettings extends StatelessWidget { child: const Icon(Icons.speed), ), contentPadding: DesktopSettings.horizontalPadding, - title: const Text('Default speed'), + title: Text(loc.initialEventSpeed), subtitle: Text(settings.kEventsSpeed.value.toStringAsFixed(1)), trailing: SizedBox( width: 160.0, @@ -145,8 +140,8 @@ class EventsAndDownloadsSettings extends StatelessWidget { child: const Icon(Icons.equalizer), ), contentPadding: DesktopSettings.horizontalPadding, - title: const Text('Default volume'), - subtitle: const Text('1.0'), + title: Text(loc.intiialEventVolume), + subtitle: Text(settings.kEventsVolume.value.toStringAsFixed(1)), trailing: SizedBox( width: 160.0, child: Slider( @@ -159,7 +154,7 @@ class EventsAndDownloadsSettings extends StatelessWidget { ), const SizedBox(height: 20.0), if (settings.kShowDebugInfo.value) ...[ - const SubHeader('Timeline of Events'), + SubHeader(loc.eventsTimeline), CheckboxListTile.adaptive( value: settings.kShowDifferentColorsForEvents.value, onChanged: (v) { From af4ee5b4ce59d1150b77400eb8e7f254e030cff2 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 22:22:41 -0300 Subject: [PATCH 05/15] feat: Localization for Application settings --- lib/l10n/app_en.arb | 8 +++++--- lib/l10n/app_fr.arb | 8 +++++--- lib/l10n/app_pl.arb | 8 +++++--- lib/l10n/app_pt.arb | 8 +++++--- lib/screens/settings/application.dart | 4 ++-- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 42105b7b..d28060b3 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -160,8 +160,6 @@ } } }, - "dateFormat": "Date Format", - "timeFormat": "Time Format", "nDevices": "{n, plural, =0{No devices} =1{1 device} other{{n} devices}}", "@nDevices": { "placeholders": { @@ -479,12 +477,16 @@ "events": "Events", "initialEventSpeed": "Initial event speed", "intiialEventVolume": "Initial event volume", - "@@APPEARANCE": {}, + "@@APPLICATION": {}, "theme": "Theme", "themeDescription": "Change the appearance of the app", "system": "System", "light": "Light", "dark": "Dark", + "dateFormat": "Date Format", + "dateFormatDescription": "What format to use for displaying dates", + "timeFormat": "Time Format", + "timeFormatDescription": "What format to use for displaying time", "@@MISC": {}, "miscellaneous": "Miscellaneous", "wakelock": "Keep screen awake", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index e81e5ed9..6fd364b8 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -154,8 +154,6 @@ } } }, - "dateFormat": "Format de la date", - "timeFormat": "Format de l'heure", "nDevices": "{n} appareils", "@nDevices": { "placeholders": { @@ -455,12 +453,16 @@ "events": "Events", "initialEventSpeed": "Initial speed", "intiialEventVolume": "Initial volume", - "@@APPEARANCE": {}, + "@@APPLICATION": {}, "theme": "Thème", "themeDescription": "Modifier l'apparence de l'application", "system": "Système", "light": "Clair", "dark": "Sombre", + "dateFormat": "Format de la date", + "dateFormatDescription": "What format to use for displaying dates", + "timeFormat": "Format de l'heure", + "timeFormatDescription": "What format to use for displaying time", "@@MISC": {}, "miscellaneous": "Divers", "wakelock": "Keep screen on", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 00c29fde..af6b5555 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -160,8 +160,6 @@ } } }, - "dateFormat": "Format daty", - "timeFormat": "Format czasu", "nDevices": "{n, plural, =0{Brak urządzeń} =1{1 urządzenie} other{{n} urządzeń}}", "@nDevices": { "placeholders": { @@ -479,12 +477,16 @@ "events": "Events", "initialEventSpeed": "Initial speed", "intiialEventVolume": "Initial volume", - "@@APPEARANCE": {}, + "@@APPLICATION": {}, "theme": "Motyw", "themeDescription": "Zmień wygląd aplikacji", "system": "Systemowy", "light": "Jasny", "dark": "Ciemny", + "dateFormat": "Format daty", + "dateFormatDescription": "What format to use for displaying dates", + "timeFormat": "Format czasu", + "timeFormatDescription": "What format to use for displaying time", "@@MISC": {}, "miscellaneous": "Różne", "wakelock": "Keep screen on", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index ddb6a479..f3779767 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -160,8 +160,6 @@ } } }, - "dateFormat": "Formato de Data", - "timeFormat": "Formato de Hora", "nDevices": "{n, plural, =0{Nenhum dispositivo} =1{1 dispositivo} other{{n} dispositivos}}", "@nDevices": { "placeholders": { @@ -479,12 +477,16 @@ "events": "Events", "initialEventSpeed": "Initial speed", "intiialEventVolume": "Initial volume", - "@@APPEARANCE": {}, + "@@APPLICATION": {}, "theme": "Aparência", "themeDescription": "Mude a aparência do aplicativo", "system": "Padrão do Sistema", "light": "Claro", "dark": "Escuro", + "dateFormat": "Formato de Data", + "dateFormatDescription": "What format to use for displaying dates", + "timeFormat": "Formato de Hora", + "timeFormatDescription": "What format to use for displaying time", "@@MISC": {}, "miscellaneous": "Outros", "wakelock": "Manter tela ativa", diff --git a/lib/screens/settings/application.dart b/lib/screens/settings/application.dart index 0d303159..706a9b66 100644 --- a/lib/screens/settings/application.dart +++ b/lib/screens/settings/application.dart @@ -248,7 +248,7 @@ class DateFormatSection extends StatelessWidget { return OptionsChooserTile( title: loc.dateFormat, - description: 'What format to use for displaying dates', + description: loc.dateFormatDescription, icon: Icons.calendar_month, value: '', values: formats.map((format) { @@ -277,7 +277,7 @@ class TimeFormatSection extends StatelessWidget { final date = DateTime.utc(1969, 7, 20, 14, 18, 04); return OptionsChooserTile( title: loc.timeFormat, - description: 'What format to use for displaying time', + description: loc.timeFormatDescription, icon: Icons.hourglass_empty, value: '', values: patterns.map((pattern) { From 5cdc2b685589a278a7e32af391aa43c3233ba888 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 22:26:59 -0300 Subject: [PATCH 06/15] feat: Localization for Privacy and Security --- lib/l10n/app_en.arb | 8 +++++++ lib/l10n/app_fr.arb | 8 +++++++ lib/l10n/app_pl.arb | 8 +++++++ lib/l10n/app_pt.arb | 8 +++++++ .../settings/privacy_and_security.dart | 24 +++++++------------ 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index d28060b3..14b674d2 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -487,6 +487,14 @@ "dateFormatDescription": "What format to use for displaying dates", "timeFormat": "Time Format", "timeFormatDescription": "What format to use for displaying time", + "@@PRIVACY_AND_SECURITY": {}, + "privacyAndSecurity": "Privacy and Security", + "allowDataCollection": "Allow Bluecherry to collect usage data", + "allowDataCollectionDescription": "Allow Bluecherry to collect data to improve the app and provide better services. Data is collected anonymously and does not contain any personal information.", + "automaticallyReportErrors": "Automatically report errors", + "automaticallyReportErrorsDescription": "Automatically send error reports to Bluecherry to help improve the app. Error reports may contain personal information.", + "privacyPolicy": "Privacy Policy", + "termsOfService": "Terms of Service", "@@MISC": {}, "miscellaneous": "Miscellaneous", "wakelock": "Keep screen awake", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 6fd364b8..104b523b 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -463,6 +463,14 @@ "dateFormatDescription": "What format to use for displaying dates", "timeFormat": "Format de l'heure", "timeFormatDescription": "What format to use for displaying time", + "@@PRIVACY_AND_SECURITY": {}, + "privacyAndSecurity": "Privacy and Security", + "allowDataCollection": "Allow Bluecherry to collect usage data", + "allowDataCollectionDescription": "Allow Bluecherry to collect data to improve the app and provide better services. Data is collected anonymously and does not contain any personal information.", + "automaticallyReportErrors": "Automatically report errors", + "automaticallyReportErrorsDescription": "Automatically send error reports to Bluecherry to help improve the app. Error reports may contain personal information.", + "privacyPolicy": "Privacy Policy", + "termsOfService": "Terms of Service", "@@MISC": {}, "miscellaneous": "Divers", "wakelock": "Keep screen on", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index af6b5555..70fb5379 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -487,6 +487,14 @@ "dateFormatDescription": "What format to use for displaying dates", "timeFormat": "Format czasu", "timeFormatDescription": "What format to use for displaying time", + "@@PRIVACY_AND_SECURITY": {}, + "privacyAndSecurity": "Privacy and Security", + "allowDataCollection": "Allow Bluecherry to collect usage data", + "allowDataCollectionDescription": "Allow Bluecherry to collect data to improve the app and provide better services. Data is collected anonymously and does not contain any personal information.", + "automaticallyReportErrors": "Automatically report errors", + "automaticallyReportErrorsDescription": "Automatically send error reports to Bluecherry to help improve the app. Error reports may contain personal information.", + "privacyPolicy": "Privacy Policy", + "termsOfService": "Terms of Service", "@@MISC": {}, "miscellaneous": "Różne", "wakelock": "Keep screen on", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index f3779767..13b7ac86 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -487,6 +487,14 @@ "dateFormatDescription": "What format to use for displaying dates", "timeFormat": "Formato de Hora", "timeFormatDescription": "What format to use for displaying time", + "@@PRIVACY_AND_SECURITY": {}, + "privacyAndSecurity": "Privacy and Security", + "allowDataCollection": "Allow Bluecherry to collect usage data", + "allowDataCollectionDescription": "Allow Bluecherry to collect data to improve the app and provide better services. Data is collected anonymously and does not contain any personal information.", + "automaticallyReportErrors": "Automatically report errors", + "automaticallyReportErrorsDescription": "Automatically send error reports to Bluecherry to help improve the app. Error reports may contain personal information.", + "privacyPolicy": "Privacy Policy", + "termsOfService": "Terms of Service", "@@MISC": {}, "miscellaneous": "Outros", "wakelock": "Manter tela ativa", diff --git a/lib/screens/settings/privacy_and_security.dart b/lib/screens/settings/privacy_and_security.dart index 6b103991..5a955aa5 100644 --- a/lib/screens/settings/privacy_and_security.dart +++ b/lib/screens/settings/privacy_and_security.dart @@ -22,6 +22,7 @@ import 'package:bluecherry_client/screens/settings/settings_desktop.dart'; import 'package:bluecherry_client/screens/settings/shared/options_chooser_tile.dart'; import 'package:bluecherry_client/utils/extensions.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; class PrivacySecuritySettings extends StatelessWidget { @@ -31,6 +32,7 @@ class PrivacySecuritySettings extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final settings = context.watch(); + final loc = AppLocalizations.of(context); return ListView(children: [ CheckboxListTile.adaptive( secondary: CircleAvatar( @@ -39,12 +41,8 @@ class PrivacySecuritySettings extends StatelessWidget { child: const Icon(Icons.analytics), ), contentPadding: DesktopSettings.horizontalPadding, - title: const Text('Allow Bluecherry to collect usage data'), - subtitle: const Text( - 'Allow Bluecherry to collect data to improve the app and provide ' - 'better services. Data is collected anonymously and does not contain ' - 'any personal information.', - ), + title: Text(loc.allowDataCollection), + subtitle: Text(loc.allowDataCollectionDescription), isThreeLine: true, value: settings.kAllowDataCollection.value, onChanged: (value) { @@ -54,16 +52,12 @@ class PrivacySecuritySettings extends StatelessWidget { }, ), OptionsChooserTile( - title: 'Automatically report errors', - description: 'Automatically report errors to Bluecherry to help us ' - 'improve the app. Error reports may contain personal information.', + title: loc.automaticallyReportErrors, + description: loc.automaticallyReportErrorsDescription, icon: Icons.error, value: settings.kAllowCrashReports.value, values: EnabledPreference.values.map( - (e) => Option( - text: e.name.uppercaseFirst, - value: e, - ), + (e) => Option(text: e.name.uppercaseFirst, value: e), ), onChanged: (v) { settings.kAllowCrashReports.value = v; @@ -78,7 +72,7 @@ class PrivacySecuritySettings extends StatelessWidget { foregroundColor: theme.iconTheme.color, child: const Icon(Icons.privacy_tip), ), - title: const Text('Privacy Policy'), + title: Text(loc.privacyPolicy), trailing: const Icon(Icons.chevron_right), onTap: () {}, ), @@ -89,7 +83,7 @@ class PrivacySecuritySettings extends StatelessWidget { foregroundColor: theme.iconTheme.color, child: const Icon(Icons.policy), ), - title: const Text('Terms of Service'), + title: Text(loc.termsOfService), trailing: const Icon(Icons.chevron_right), onTap: () {}, ), From ac641489310f865c2cbe1fbd84121287872016e7 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 22:36:41 -0300 Subject: [PATCH 07/15] feat: Localization for Advanced Options Settings --- lib/l10n/app_en.arb | 17 ++++++++-- lib/l10n/app_fr.arb | 17 ++++++++-- lib/l10n/app_pl.arb | 17 ++++++++-- lib/l10n/app_pt.arb | 17 ++++++++-- lib/screens/settings/advanced_options.dart | 39 +++++++++------------- 5 files changed, 75 insertions(+), 32 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 14b674d2..70732e2d 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -495,7 +495,21 @@ "automaticallyReportErrorsDescription": "Automatically send error reports to Bluecherry to help improve the app. Error reports may contain personal information.", "privacyPolicy": "Privacy Policy", "termsOfService": "Terms of Service", - "@@MISC": {}, + "@@ADVANCED_OPTIONS": {}, + "matrixZoom": "Matrix Zoom", + "defaultMatrixSize": "Default Matrix Size", + "softwareZoom": "Software zoom", + "softwareZoomDescription": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly.", + "softwareZoomDescriptionMacOS": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly. On macOS, this can not be disabled.", + "developerOptions": "Developer options", + "openLogFile": "Open log file", + "openAppDataDirectory": "Open app data directory", + "debugInfo": "Debug info", + "debugInfoDescription": "Display useful information for debugging, such as video metadata and other useful information for debugging purposes.", + "restoreDefaults": "Restore Defaults", + "restoreDefaultsDescription": "Restore all settings to their default values. This will not affect the servers you have added.", + "areYouSure": "Are you sure?", + "areYouSureDescription": "This will restore all settings to their default values. This will not affect your servers or any other data.", "miscellaneous": "Miscellaneous", "wakelock": "Keep screen awake", "wakelockDescription": "Keep screen awake while watching live streams or recordings.", @@ -514,7 +528,6 @@ } } }, - "@Notification click": {}, "@@STREAMING": {}, "streamingSettings": "Streaming settings", "streamingType": "Streaming type", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 104b523b..d988e1d5 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -471,7 +471,21 @@ "automaticallyReportErrorsDescription": "Automatically send error reports to Bluecherry to help improve the app. Error reports may contain personal information.", "privacyPolicy": "Privacy Policy", "termsOfService": "Terms of Service", - "@@MISC": {}, + "@@ADVANCED_OPTIONS": {}, + "matrixZoom": "Matrix Zoom", + "defaultMatrixSize": "Default Matrix Size", + "softwareZoom": "Software zoom", + "softwareZoomDescription": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly.", + "softwareZoomDescriptionMacOS": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly. On macOS, this can not be disabled.", + "developerOptions": "Developer options", + "openLogFile": "Open log file", + "openAppDataDirectory": "Open app data directory", + "debugInfo": "Debug info", + "debugInfoDescription": "Display useful information for debugging, such as video metadata and other useful information for debugging purposes.", + "restoreDefaults": "Restore Defaults", + "restoreDefaultsDescription": "Restore all settings to their default values. This will not affect the servers you have added.", + "areYouSure": "Are you sure?", + "areYouSureDescription": "This will restore all settings to their default values. This will not affect your servers or any other data.", "miscellaneous": "Divers", "wakelock": "Keep screen on", "wakelockDescription": "Keep screen on while watching live streams or recordings.", @@ -488,7 +502,6 @@ "time": {} } }, - "@Notification click": {}, "@@STREAMING": {}, "streamingSettings": "Paramètre de diffusion", "streamingType": "Type de diffusion", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 70fb5379..268c1aac 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -495,7 +495,21 @@ "automaticallyReportErrorsDescription": "Automatically send error reports to Bluecherry to help improve the app. Error reports may contain personal information.", "privacyPolicy": "Privacy Policy", "termsOfService": "Terms of Service", - "@@MISC": {}, + "@@ADVANCED_OPTIONS": {}, + "matrixZoom": "Matrix Zoom", + "defaultMatrixSize": "Default Matrix Size", + "softwareZoom": "Software zoom", + "softwareZoomDescription": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly.", + "softwareZoomDescriptionMacOS": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly. On macOS, this can not be disabled.", + "developerOptions": "Developer options", + "openLogFile": "Open log file", + "openAppDataDirectory": "Open app data directory", + "debugInfo": "Debug info", + "debugInfoDescription": "Display useful information for debugging, such as video metadata and other useful information for debugging purposes.", + "restoreDefaults": "Restore Defaults", + "restoreDefaultsDescription": "Restore all settings to their default values. This will not affect the servers you have added.", + "areYouSure": "Are you sure?", + "areYouSureDescription": "This will restore all settings to their default values. This will not affect your servers or any other data.", "miscellaneous": "Różne", "wakelock": "Keep screen on", "wakelockDescription": "Keep screen on while watching live streams or recordings.", @@ -514,7 +528,6 @@ } } }, - "@Notification click": {}, "@@STREAMING": {}, "streamingSettings": "Streaming settings", "streamingType": "Streaming type", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 13b7ac86..bf60c34c 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -495,7 +495,21 @@ "automaticallyReportErrorsDescription": "Automatically send error reports to Bluecherry to help improve the app. Error reports may contain personal information.", "privacyPolicy": "Privacy Policy", "termsOfService": "Terms of Service", - "@@MISC": {}, + "@@ADVANCED_OPTIONS": {}, + "matrixZoom": "Matrix Zoom", + "defaultMatrixSize": "Default Matrix Size", + "softwareZoom": "Software zoom", + "softwareZoomDescription": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly.", + "softwareZoomDescriptionMacOS": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly. On macOS, this can not be disabled.", + "developerOptions": "Developer options", + "openLogFile": "Open log file", + "openAppDataDirectory": "Open app data directory", + "debugInfo": "Debug info", + "debugInfoDescription": "Display useful information for debugging, such as video metadata and other useful information for debugging purposes.", + "restoreDefaults": "Restore Defaults", + "restoreDefaultsDescription": "Restore all settings to their default values. This will not affect the servers you have added.", + "areYouSure": "Are you sure?", + "areYouSureDescription": "This will restore all settings to their default values. This will not affect your servers or any other data.", "miscellaneous": "Outros", "wakelock": "Manter tela ativa", "wakelockDescription": "Mantenha a tela ativa enquanto estiver assistindo a transmissões ao vivo ou gravações", @@ -514,7 +528,6 @@ } } }, - "@Notification click": {}, "@@STREAMING": {}, "streamingSettings": "Configurações de streaming", "streamingType": "Tipo de streaming", diff --git a/lib/screens/settings/advanced_options.dart b/lib/screens/settings/advanced_options.dart index 40a26f15..74863e28 100644 --- a/lib/screens/settings/advanced_options.dart +++ b/lib/screens/settings/advanced_options.dart @@ -41,7 +41,7 @@ class AdvancedOptionsSettings extends StatelessWidget { final loc = AppLocalizations.of(context); final settings = context.watch(); return ListView(children: [ - const SubHeader('Matrix Zoom'), + SubHeader(loc.matrixZoom), CheckboxListTile.adaptive( secondary: CircleAvatar( backgroundColor: Colors.transparent, @@ -59,7 +59,7 @@ class AdvancedOptionsSettings extends StatelessWidget { }, ), OptionsChooserTile( - title: 'Default Matrix Size', + title: loc.defaultMatrixSize, icon: Icons.view_quilt, value: settings.kMatrixSize.value, values: MatrixType.values.map((size) { @@ -79,11 +79,11 @@ class AdvancedOptionsSettings extends StatelessWidget { foregroundColor: theme.iconTheme.color, child: const Icon(Icons.center_focus_strong), ), - title: const Text('Software zoom'), + title: Text(loc.softwareZoom), subtitle: Text( - 'When enabled, the matrix zoom will not happen in the GPU. This is ' - 'useful when the hardware zoom is not working properly. ' - '${Platform.isMacOS ? 'On macOS, this can not be disabled.' : ''}', + Platform.isMacOS + ? loc.softwareZoomDescriptionMacOS + : loc.softwareZoomDescription, ), value: Platform.isMacOS ? true : settings.kSoftwareZooming.value, onChanged: Platform.isMacOS @@ -95,7 +95,7 @@ class AdvancedOptionsSettings extends StatelessWidget { }, dense: false, ), - const SubHeader('Developer Options'), + SubHeader(loc.developerOptions), if (!kIsWeb) ...[ FutureBuilder( future: getLogFile(), @@ -107,7 +107,7 @@ class AdvancedOptionsSettings extends StatelessWidget { foregroundColor: theme.iconTheme.color, child: const Icon(Icons.bug_report), ), - title: const Text('Open log file'), + title: Text(loc.openLogFile), subtitle: Text(snapshot.data?.path ?? loc.loading), trailing: const Icon(Icons.navigate_next), dense: false, @@ -129,7 +129,7 @@ class AdvancedOptionsSettings extends StatelessWidget { foregroundColor: theme.iconTheme.color, child: const Icon(Icons.home), ), - title: const Text('Open app data'), + title: Text(loc.openAppDataDirectory), subtitle: Text(snapshot.data?.path ?? loc.loading), trailing: const Icon(Icons.navigate_next), dense: false, @@ -148,11 +148,8 @@ class AdvancedOptionsSettings extends StatelessWidget { foregroundColor: theme.iconTheme.color, child: const Icon(Icons.adb), ), - title: const Text('Debug info'), - subtitle: const Text( - 'Display useful information for debugging, such as video metadata ' - 'and other useful information for debugging purposes.', - ), + title: Text(loc.debugInfo), + subtitle: Text(loc.debugInfoDescription), value: settings.kShowDebugInfo.value, onChanged: (v) { if (v != null) { @@ -190,11 +187,8 @@ class AdvancedOptionsSettings extends StatelessWidget { foregroundColor: theme.colorScheme.error, child: const Icon(Icons.restore), ), - title: const Text('Restore Defaults'), - subtitle: const Text( - 'Restore all settings to their default values. This will not ' - 'affect your servers or any other data.', - ), + title: Text(loc.restoreDefaults), + subtitle: Text(loc.restoreDefaultsDescription), trailing: const Icon(Icons.navigate_next), textColor: theme.colorScheme.error, iconColor: theme.colorScheme.error, @@ -203,11 +197,8 @@ class AdvancedOptionsSettings extends StatelessWidget { context: context, builder: (context) { return AlertDialog( - title: const Text('Are you sure?'), - content: const Text( - 'This will restore all settings to their default values. ' - 'This will not affect your servers or any other data.', - ), + title: Text(loc.areYouSure), + content: Text(loc.areYouSureDescription), actions: [ FilledButton( onPressed: () { From c19bcaf72e5c847197e140d9506272b950681391 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 22:49:12 -0300 Subject: [PATCH 08/15] feat: Portuguese translations --- lib/l10n/app_pt.arb | 60 ++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index bf60c34c..18949b84 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -471,45 +471,45 @@ "notificationClickBehaviorDescription": "Escolha o que acontece quando você clica em uma notificação.", "showEventsScreen": "Mostar histórico de eventos", "@@EVENTS_AND_DOWNLOADS": {}, - "chooseEveryDownloadsLocation": "Choose the location for every download", - "chooseEveryDownloadsLocationDescription": "Whether to choose the location for each download or use the default location. When enabled, you will be prompted to choose the download directory for each download.", - "blockCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", - "events": "Events", - "initialEventSpeed": "Initial speed", - "intiialEventVolume": "Initial volume", + "chooseEveryDownloadsLocation": "Escolher a localização de cada download", + "chooseEveryDownloadsLocationDescription": "Se você deseja escolher a localização de cada download ou usar a localização padrão. Quando ativado, você será solicitado a escolher o diretório de download para cada download.", + "blockCloseWhenDownloading": "Impedir o aplicativo de fechar quando houver downloads em andamento.", + "events": "Eventos", + "initialEventSpeed": "Velocidade inicial", + "intiialEventVolume": "Volume inicial", "@@APPLICATION": {}, "theme": "Aparência", "themeDescription": "Mude a aparência do aplicativo", "system": "Padrão do Sistema", "light": "Claro", "dark": "Escuro", - "dateFormat": "Formato de Data", - "dateFormatDescription": "What format to use for displaying dates", + "dateFormat": "Formato da Data", + "dateFormatDescription": "Qual formato usar para exibir datas", "timeFormat": "Formato de Hora", - "timeFormatDescription": "What format to use for displaying time", + "timeFormatDescription": "Qual formato usar para exibir horas", "@@PRIVACY_AND_SECURITY": {}, - "privacyAndSecurity": "Privacy and Security", - "allowDataCollection": "Allow Bluecherry to collect usage data", - "allowDataCollectionDescription": "Allow Bluecherry to collect data to improve the app and provide better services. Data is collected anonymously and does not contain any personal information.", - "automaticallyReportErrors": "Automatically report errors", - "automaticallyReportErrorsDescription": "Automatically send error reports to Bluecherry to help improve the app. Error reports may contain personal information.", - "privacyPolicy": "Privacy Policy", - "termsOfService": "Terms of Service", + "privacyAndSecurity": "Privacidade e Segurança", + "allowDataCollection": "Permitir que Bluecherry colete dados de uso", + "allowDataCollectionDescription": "Permitir que Bluecherry colete dados para melhorar o aplicativo e fornecer serviços melhores. Os dados são coletados anonimamente e não contêm informações pessoais.", + "automaticallyReportErrors": "Relatar erros automaticamente", + "automaticallyReportErrorsDescription": "Enviar automaticamente relatórios de erros para Bluecherry para ajudar a melhorar o aplicativo. Os relatórios de erros podem conter informações pessoais.", + "privacyPolicy": "Política de Privacidade", + "termsOfService": "Termos de Serviço", "@@ADVANCED_OPTIONS": {}, - "matrixZoom": "Matrix Zoom", - "defaultMatrixSize": "Default Matrix Size", - "softwareZoom": "Software zoom", - "softwareZoomDescription": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly.", - "softwareZoomDescriptionMacOS": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly. On macOS, this can not be disabled.", - "developerOptions": "Developer options", - "openLogFile": "Open log file", - "openAppDataDirectory": "Open app data directory", - "debugInfo": "Debug info", - "debugInfoDescription": "Display useful information for debugging, such as video metadata and other useful information for debugging purposes.", - "restoreDefaults": "Restore Defaults", - "restoreDefaultsDescription": "Restore all settings to their default values. This will not affect the servers you have added.", - "areYouSure": "Are you sure?", - "areYouSureDescription": "This will restore all settings to their default values. This will not affect your servers or any other data.", + "matrixZoom": "Zoom da Matriz", + "defaultMatrixSize": "Tamanho Padrão da Matriz", + "softwareZoom": "Zoom do Software", + "softwareZoomDescription": "Quando ativado, o zoom não ocorrerá na GPU. Isso é útil quando o zoom do hardware não está funcionando corretamente.", + "softwareZoomDescriptionMacOS": "Quando ativado, o zoom da matriz não ocorrerá na GPU. Isso é útil quando o zoom do hardware não está funcionando corretamente. No macOS, isso não pode ser desativado.", + "developerOptions": "Opções de Desenvolvedor", + "openLogFile": "Abrir Arquivo de Log", + "openAppDataDirectory": "Abrir Diretório de Dados do Aplicativo", + "debugInfo": "Informações de Depuração", + "debugInfoDescription": "Exibir informações úteis para depuração, como metadados de vídeo e outras informações úteis para fins de depuração.", + "restoreDefaults": "Restaurar Padrões", + "restoreDefaultsDescription": "Restaurar todas as configurações para seus valores padrão. Isso não afetará os servidores que você adicionou.", + "areYouSure": "Você tem certeza?", + "areYouSureDescription": "Isso restaurará todas as configurações para seus valores padrão. Isso não afetará seus servidores ou quaisquer outros dados.", "miscellaneous": "Outros", "wakelock": "Manter tela ativa", "wakelockDescription": "Mantenha a tela ativa enquanto estiver assistindo a transmissões ao vivo ou gravações", From 3dbc7789ba4a0fffc58e24aa9f035951e57a727c Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 23:03:44 -0300 Subject: [PATCH 09/15] fix: Change "matrix zoom" to "area magnification" --- lib/l10n/app_en.arb | 16 +++++++--------- lib/l10n/app_fr.arb | 8 +++----- lib/l10n/app_pl.arb | 8 +++----- lib/l10n/app_pt.arb | 8 +++----- 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 70732e2d..395ae209 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -448,11 +448,6 @@ } } }, - "@BETA FEATURES": {}, - "betaFeatures": "Beta features", - "matrixedViewZoom": "Matrixed view zoom", - "matrixedViewZoomDescription": "Zoom in on a 16x16 matrix view to see more detail.", - "matrixType": "Matrix type", "@CURRENT TASKS": {}, "currentTasks": "Current tasks", "noCurrentTasks": "No tasks", @@ -496,11 +491,14 @@ "privacyPolicy": "Privacy Policy", "termsOfService": "Terms of Service", "@@ADVANCED_OPTIONS": {}, - "matrixZoom": "Matrix Zoom", - "defaultMatrixSize": "Default Matrix Size", + "matrixZoom": "Area Magnification", + "matrixedViewZoom": "Area Magnification enabled", + "matrixedViewZoomDescription": "Magnify a area of the matrix view when selected. This is useful when you have a lot of cameras and want to see a specific area in more detail, or when a multicast stream is provided.", + "matrixType": "Matrix type", + "defaultMatrixSize": "Default Magnification Proportion", "softwareZoom": "Software zoom", - "softwareZoomDescription": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly.", - "softwareZoomDescriptionMacOS": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly. On macOS, this can not be disabled.", + "softwareZoomDescription": "When enabled, the magnification will not happen in the GPU. This is useful when the hardware magnification is not working properly.", + "softwareZoomDescriptionMacOS": "When enabled, the magnification will not happen in the GPU. This is useful when the hardware magnification is not working properly. On macOS, this can not be disabled.", "developerOptions": "Developer options", "openLogFile": "Open log file", "openAppDataDirectory": "Open app data directory", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index d988e1d5..c8102dbc 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -424,11 +424,6 @@ } } }, - "@BETA FEATURES": {}, - "betaFeatures": "Beta features", - "matrixedViewZoom": "Matrixed view zoom", - "matrixedViewZoomDescription": "Zoom in on a 16x16 matrixed view to see more detail.", - "matrixType": "Matrix type", "@CURRENT TASKS": {}, "currentTasks": "Tâche courante", "noCurrentTasks": "Aucune tâche", @@ -473,6 +468,9 @@ "termsOfService": "Terms of Service", "@@ADVANCED_OPTIONS": {}, "matrixZoom": "Matrix Zoom", + "matrixedViewZoom": "Matrixed view zoom", + "matrixedViewZoomDescription": "Zoom in on a 16x16 matrixed view to see more detail.", + "matrixType": "Matrix type", "defaultMatrixSize": "Default Matrix Size", "softwareZoom": "Software zoom", "softwareZoomDescription": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly.", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 268c1aac..b5271030 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -448,11 +448,6 @@ } } }, - "@BETA FEATURES": {}, - "betaFeatures": "Beta features", - "matrixedViewZoom": "Matrixed view zoom", - "matrixedViewZoomDescription": "Zoom in on a 16x16 matrixed view to see more detail.", - "matrixType": "Matrix type", "@CURRENT TASKS": {}, "currentTasks": "Bieżące zadania", "noCurrentTasks": "Brak zadań", @@ -497,6 +492,9 @@ "termsOfService": "Terms of Service", "@@ADVANCED_OPTIONS": {}, "matrixZoom": "Matrix Zoom", + "matrixedViewZoom": "Matrixed view zoom", + "matrixedViewZoomDescription": "Zoom in on a 16x16 matrixed view to see more detail.", + "matrixType": "Matrix type", "defaultMatrixSize": "Default Matrix Size", "softwareZoom": "Software zoom", "softwareZoomDescription": "When enabled, the matrix zoom will not happen in the GPU. This is useful when the hardware zoom is not working properly.", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 18949b84..630dbd76 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -448,11 +448,6 @@ } } }, - "@BETA FEATURES": {}, - "betaFeatures": "Funcionalidades em Beta", - "matrixedViewZoom": "Zoom em Visualização Matrixial", - "matrixedViewZoomDescription": "Dê zoom em uma visualização matrixial 16x16 para ver mais detalhes.", - "matrixType": "Tipo de Matrix", "@CURRENT TASKS": {}, "currentTasks": "Tarefas", "noCurrentTasks": "Nenhuma tarefa", @@ -497,6 +492,9 @@ "termsOfService": "Termos de Serviço", "@@ADVANCED_OPTIONS": {}, "matrixZoom": "Zoom da Matriz", + "matrixedViewZoom": "Zoom em Visualização Matrixial", + "matrixedViewZoomDescription": "Dê zoom em uma visualização matrixial 16x16 para ver mais detalhes.", + "matrixType": "Tipo de Matrix", "defaultMatrixSize": "Tamanho Padrão da Matriz", "softwareZoom": "Zoom do Software", "softwareZoomDescription": "Quando ativado, o zoom não ocorrerá na GPU. Isso é útil quando o zoom do hardware não está funcionando corretamente.", From 34d6e605c51414292cbc23ac52e584c5701ec3b9 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 23:17:03 -0300 Subject: [PATCH 10/15] feat: Implement "Allow closing the app when downloading" --- lib/l10n/app_en.arb | 2 +- lib/l10n/app_fr.arb | 2 +- lib/l10n/app_pl.arb | 2 +- lib/l10n/app_pt.arb | 2 +- lib/main.dart | 11 +++++--- lib/providers/downloads_provider.dart | 25 ++++++++++++++++--- lib/screens/downloads/close_dialog.dart | 2 +- lib/screens/downloads/downloads_manager.dart | 2 +- lib/screens/downloads/indicators.dart | 6 +++-- .../mobile/timeline_device_view.dart | 7 +++--- .../settings/events_and_downloads.dart | 25 +++++++++++-------- 11 files changed, 58 insertions(+), 28 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 395ae209..e311c5a0 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -468,7 +468,7 @@ "@@EVENTS_AND_DOWNLOADS": {}, "chooseEveryDownloadsLocation": "Choose the location for every download", "chooseEveryDownloadsLocationDescription": "Whether to choose the location for each download or use the default location. When enabled, you will be prompted to choose the download directory for each download.", - "blockCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", + "allowCloseWhenDownloading": "Allow closing the app when downloading", "events": "Events", "initialEventSpeed": "Initial event speed", "intiialEventVolume": "Initial event volume", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index c8102dbc..9b1963c3 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -444,7 +444,7 @@ "@@EVENTS_AND_DOWNLOADS": {}, "chooseEveryDownloadsLocation": "Choose the location for every download", "chooseEveryDownloadsLocationDescription": "Whether to choose the location for each download or use the default location. When enabled, you will be prompted to choose the download directory for each download.", - "blockCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", + "allowCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", "events": "Events", "initialEventSpeed": "Initial speed", "intiialEventVolume": "Initial volume", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index b5271030..e43d3408 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -468,7 +468,7 @@ "@@EVENTS_AND_DOWNLOADS": {}, "chooseEveryDownloadsLocation": "Choose the location for every download", "chooseEveryDownloadsLocationDescription": "Whether to choose the location for each download or use the default location. When enabled, you will be prompted to choose the download directory for each download.", - "blockCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", + "allowCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", "events": "Events", "initialEventSpeed": "Initial speed", "intiialEventVolume": "Initial volume", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 630dbd76..887425ce 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -468,7 +468,7 @@ "@@EVENTS_AND_DOWNLOADS": {}, "chooseEveryDownloadsLocation": "Escolher a localização de cada download", "chooseEveryDownloadsLocationDescription": "Se você deseja escolher a localização de cada download ou usar a localização padrão. Quando ativado, você será solicitado a escolher o diretório de download para cada download.", - "blockCloseWhenDownloading": "Impedir o aplicativo de fechar quando houver downloads em andamento.", + "allowCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", "events": "Eventos", "initialEventSpeed": "Velocidade inicial", "intiialEventVolume": "Volume inicial", diff --git a/lib/main.dart b/lib/main.dart index 38c51b98..4c07879d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -250,10 +250,15 @@ class _UnityAppState extends State final context = navigatorKey.currentContext!; if (isPreventClose && mounted && context.mounted) { final downloadsManager = context.read(); + final settings = context.read(); if (downloadsManager.downloading.isNotEmpty) { - final result = await showCloseDownloadsDialog(context); - if (result == null || !result) { - return; + if (settings.kAllowAppCloseWhenDownloading.value) { + downloadsManager.cancelDownloading(); + } else { + final result = await showCloseDownloadsDialog(context); + if (result == null || !result) { + return; + } } } diff --git a/lib/providers/downloads_provider.dart b/lib/providers/downloads_provider.dart index ae7d9599..eadc6dd4 100644 --- a/lib/providers/downloads_provider.dart +++ b/lib/providers/downloads_provider.dart @@ -26,6 +26,7 @@ import 'package:bluecherry_client/providers/app_provider_interface.dart'; import 'package:bluecherry_client/providers/home_provider.dart'; import 'package:bluecherry_client/providers/settings_provider.dart'; import 'package:bluecherry_client/utils/constants.dart'; +import 'package:bluecherry_client/utils/logging.dart'; import 'package:bluecherry_client/utils/storage.dart'; import 'package:dio/dio.dart'; import 'package:file_picker/file_picker.dart'; @@ -132,7 +133,7 @@ class DownloadsManager extends UnityProvider { List downloadedEvents = []; /// The events that are downloading - Map downloading = {}; + Map downloading = {}; Completer? downloadsCompleter; // bool _isProgressBarSet = false; @@ -228,7 +229,7 @@ class DownloadsManager extends UnityProvider { downloadsCompleter = Completer(); } - downloading[event] = 0.0; + downloading[event] = (0.0, ''); notifyListeners(); final dir = await () async { @@ -260,7 +261,7 @@ class DownloadsManager extends UnityProvider { ), onReceiveProgress: (received, total) { if (total != -1) { - downloading[event] = received / total; + downloading[event] = (received / total, fileName); notifyListeners(); } }, @@ -289,4 +290,22 @@ class DownloadsManager extends UnityProvider { if (await file.exists()) await file.delete(); } + + /// Cancels the ongoing downloads and deletes the downloaded files. + Future cancelDownloading() async { + for (final event in downloading.keys.toList()) { + try { + final file = File(downloading[event]!.$2); + if (file.existsSync()) file.deleteSync(); + } catch (e, s) { + debugPrint('Failed to delete file: $e'); + writeLogToFile( + 'Failed to delete file during cancelDownloading: $e, $s', + ); + } + downloading.remove(event); + } + + downloadsCompleter?.complete(); + } } diff --git a/lib/screens/downloads/close_dialog.dart b/lib/screens/downloads/close_dialog.dart index 7ca36302..a80dc275 100644 --- a/lib/screens/downloads/close_dialog.dart +++ b/lib/screens/downloads/close_dialog.dart @@ -61,7 +61,7 @@ class _CloseDownloadsDialogState extends State { contentPadding: EdgeInsets.zero, trailing: SizedBox.fromSize( size: const Size(40.0, 40.0), - child: DownloadProgressIndicator(progress: entry.value), + child: DownloadProgressIndicator(progress: entry.value.$1), ), title: Text(entry.key.deviceName), subtitle: Text(entry.key.server.name), diff --git a/lib/screens/downloads/downloads_manager.dart b/lib/screens/downloads/downloads_manager.dart index 539c3e16..56274823 100644 --- a/lib/screens/downloads/downloads_manager.dart +++ b/lib/screens/downloads/downloads_manager.dart @@ -70,7 +70,7 @@ class DownloadsManagerScreen extends StatelessWidget { itemBuilder: (context, index) { final entry = downloads.downloading.entries.elementAt(index); final event = entry.key; - final progress = entry.value; + final (progress, _) = entry.value; return DownloadTile( key: ValueKey(event.id), diff --git a/lib/screens/downloads/indicators.dart b/lib/screens/downloads/indicators.dart index dbb8f2d5..0e30e319 100644 --- a/lib/screens/downloads/indicators.dart +++ b/lib/screens/downloads/indicators.dart @@ -128,8 +128,10 @@ class DownloadIndicator extends StatelessWidget { if (downloads.isEventDownloading(event.id)) { return DownloadProgressIndicator( - progress: downloads.downloading[downloads.downloading.keys - .firstWhere((e) => e.id == event.id)]!, + progress: downloads + .downloading[downloads.downloading.keys + .firstWhere((e) => e.id == event.id)]! + .$1, color: highlight ? Colors.amber : null, ); } diff --git a/lib/screens/events_timeline/mobile/timeline_device_view.dart b/lib/screens/events_timeline/mobile/timeline_device_view.dart index 90cec3ab..de3c908b 100644 --- a/lib/screens/events_timeline/mobile/timeline_device_view.dart +++ b/lib/screens/events_timeline/mobile/timeline_device_view.dart @@ -609,9 +609,10 @@ class _TimelineDeviceViewState extends State { ) : isDownloading ? DownloadProgressIndicator( - progress: downloads.downloading[downloads - .downloading.keys - .firstWhere((e) => e.id == event.id)]!, + progress: downloads + .downloading[downloads.downloading.keys + .firstWhere((e) => e.id == event.id)]! + .$1, ) : const Icon(Icons.download), text: isDownloaded diff --git a/lib/screens/settings/events_and_downloads.dart b/lib/screens/settings/events_and_downloads.dart index 53319d65..40c50a16 100644 --- a/lib/screens/settings/events_and_downloads.dart +++ b/lib/screens/settings/events_and_downloads.dart @@ -77,18 +77,21 @@ class EventsAndDownloadsSettings extends StatelessWidget { } }, ), - if (settings.kShowDebugInfo.value) - CheckboxListTile.adaptive( - value: false, - onChanged: (v) {}, - contentPadding: DesktopSettings.horizontalPadding, - secondary: CircleAvatar( - backgroundColor: Colors.transparent, - foregroundColor: theme.iconTheme.color, - child: const Icon(Icons.close), - ), - title: Text(loc.blockCloseWhenDownloading), + CheckboxListTile.adaptive( + value: settings.kAllowAppCloseWhenDownloading.value, + onChanged: (v) { + if (v != null) { + settings.kAllowAppCloseWhenDownloading.value = v; + } + }, + contentPadding: DesktopSettings.horizontalPadding, + secondary: CircleAvatar( + backgroundColor: Colors.transparent, + foregroundColor: theme.iconTheme.color, + child: const Icon(Icons.close), ), + title: Text(loc.allowCloseWhenDownloading), + ), SubHeader(loc.events), if (settings.kShowDebugInfo.value) CheckboxListTile.adaptive( From 64beb842e9d0a7b4bdf3991428e651a8b6bcdbd9 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 23:27:39 -0300 Subject: [PATCH 11/15] feat: Implement "Differentitate events colors" --- lib/l10n/app_en.arb | 4 +- lib/l10n/app_fr.arb | 4 +- lib/l10n/app_pl.arb | 4 +- lib/l10n/app_pt.arb | 4 +- .../events_timeline/desktop/timeline.dart | 45 +++++++++++++------ .../settings/events_and_downloads.dart | 39 ++++++++-------- 6 files changed, 62 insertions(+), 38 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index e311c5a0..849ca076 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -471,7 +471,9 @@ "allowCloseWhenDownloading": "Allow closing the app when downloading", "events": "Events", "initialEventSpeed": "Initial event speed", - "intiialEventVolume": "Initial event volume", + "initialEventVolume": "Initial event volume", + "differentEventColors": "Different events colors", + "differentEventColorsDescription": "Whether to show different colors for events in the timeline. This assists to easily differentiate the events.", "@@APPLICATION": {}, "theme": "Theme", "themeDescription": "Change the appearance of the app", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 9b1963c3..d2eeaa73 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -447,7 +447,9 @@ "allowCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", "events": "Events", "initialEventSpeed": "Initial speed", - "intiialEventVolume": "Initial volume", + "initialEventVolume": "Initial volume", + "differentEventColors": "Different event colors", + "differentEventColorsDescription": "Whether to show different colors for events in the timeline. This assists to easily differentiate the events.", "@@APPLICATION": {}, "theme": "Thème", "themeDescription": "Modifier l'apparence de l'application", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index e43d3408..4ee180df 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -471,7 +471,9 @@ "allowCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", "events": "Events", "initialEventSpeed": "Initial speed", - "intiialEventVolume": "Initial volume", + "initialEventVolume": "Initial volume", + "differentEventColors": "Different event colors", + "differentEventColorsDescription": "Whether to show different colors for events in the timeline. This assists to easily differentiate the events.", "@@APPLICATION": {}, "theme": "Motyw", "themeDescription": "Zmień wygląd aplikacji", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 887425ce..2c3d9b76 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -471,7 +471,9 @@ "allowCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", "events": "Eventos", "initialEventSpeed": "Velocidade inicial", - "intiialEventVolume": "Volume inicial", + "initialEventVolume": "Volume inicial", + "differentEventColors": "Different event colors", + "differentEventColorsDescription": "Whether to show different colors for events in the timeline. This assists to easily differentiate the events.", "@@APPLICATION": {}, "theme": "Aparência", "themeDescription": "Mude a aparência do aplicativo", diff --git a/lib/screens/events_timeline/desktop/timeline.dart b/lib/screens/events_timeline/desktop/timeline.dart index ab92ada9..d180ebef 100644 --- a/lib/screens/events_timeline/desktop/timeline.dart +++ b/lib/screens/events_timeline/desktop/timeline.dart @@ -898,7 +898,7 @@ class _TimelineEventsViewState extends State { } } -class _TimelineTile extends StatelessWidget { +class _TimelineTile extends StatefulWidget { final TimelineTile tile; const _TimelineTile({super.key, required this.tile}); @@ -943,6 +943,28 @@ class _TimelineTile extends StatelessWidget { }); } + @override + State<_TimelineTile> createState() => _TimelineTileState(); +} + +class _TimelineTileState extends State<_TimelineTile> { + late final Map colors; + + @override + void initState() { + super.initState(); + colors = Map.fromIterables( + widget.tile.events.map((e) => e.event), + widget.tile.events.indexed.map((e) { + final index = e.$1; + return [ + ...Colors.primaries, + ...Colors.accents, + ][index % [...Colors.primaries, ...Colors.accents].length]; + }), + ); + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -962,14 +984,15 @@ class _TimelineTile extends StatelessWidget { height: _kTimelineTileHeight, decoration: BoxDecoration(border: border), child: LayoutBuilder(builder: (context, constraints) { - if (!tile.events.any((event) => event.startTime.hour == hour)) { + if (!widget.tile.events + .any((event) => event.startTime.hour == hour)) { return const SizedBox.shrink(); } final secondWidth = constraints.maxWidth / 60 / 60; return Stack(clipBehavior: Clip.none, children: [ - for (final event in tile.events + for (final event in widget.tile.events .where((event) => event.startTime.hour == hour)) PositionedDirectional( // the minute (in seconds) + the start second * the width of @@ -980,20 +1003,16 @@ class _TimelineTile extends StatelessWidget { width: event.duration.inSeconds * secondWidth, height: _kTimelineTileHeight, child: ColoredBox( - // color: kDebugMode - // ? [ - // ...Colors.primaries, - // ...Colors.accents - // ][Random().nextInt( - // [...Colors.primaries, ...Colors.accents].length - - // 1)] - // : theme.colorScheme.primary, - color: theme.colorScheme.primary, + color: settings.kShowDebugInfo.value || + settings.kShowDifferentColorsForEvents.value + ? colors[event.event] ?? theme.colorScheme.primary + : theme.colorScheme.primary, + // color: theme.colorScheme.primary, child: settings.kShowDebugInfo.value ? Align( alignment: AlignmentDirectional.centerStart, child: Text( - '${tile.events.indexOf(event)}', + '${widget.tile.events.indexOf(event)}', style: TextStyle( color: theme.colorScheme.onPrimary, fontSize: 10.0, diff --git a/lib/screens/settings/events_and_downloads.dart b/lib/screens/settings/events_and_downloads.dart index 40c50a16..8b04e81c 100644 --- a/lib/screens/settings/events_and_downloads.dart +++ b/lib/screens/settings/events_and_downloads.dart @@ -143,7 +143,7 @@ class EventsAndDownloadsSettings extends StatelessWidget { child: const Icon(Icons.equalizer), ), contentPadding: DesktopSettings.horizontalPadding, - title: Text(loc.intiialEventVolume), + title: Text(loc.initialEventVolume), subtitle: Text(settings.kEventsVolume.value.toStringAsFixed(1)), trailing: SizedBox( width: 160.0, @@ -156,27 +156,24 @@ class EventsAndDownloadsSettings extends StatelessWidget { ), ), const SizedBox(height: 20.0), - if (settings.kShowDebugInfo.value) ...[ - SubHeader(loc.eventsTimeline), - CheckboxListTile.adaptive( - value: settings.kShowDifferentColorsForEvents.value, - onChanged: (v) { - if (v != null) { - settings.kShowDifferentColorsForEvents.value = v; - } - }, - contentPadding: DesktopSettings.horizontalPadding, - secondary: CircleAvatar( - backgroundColor: Colors.transparent, - foregroundColor: theme.iconTheme.color, - child: const Icon(Icons.color_lens), - ), - title: const Text('Show different colors for events'), - subtitle: const Text( - 'Whether to show different colors for events in the timeline. ' - 'This will help you to easily identify the events.', - ), + SubHeader(loc.eventsTimeline), + CheckboxListTile.adaptive( + value: settings.kShowDifferentColorsForEvents.value, + onChanged: (v) { + if (v != null) { + settings.kShowDifferentColorsForEvents.value = v; + } + }, + contentPadding: DesktopSettings.horizontalPadding, + secondary: CircleAvatar( + backgroundColor: Colors.transparent, + foregroundColor: theme.iconTheme.color, + child: const Icon(Icons.color_lens), ), + title: Text(loc.differentEventColors), + subtitle: Text(loc.differentEventColorsDescription), + ), + if (settings.kShowDebugInfo.value) ...[ CheckboxListTile.adaptive( value: settings.kPauseToBuffer.value, onChanged: (v) { From d6d72426f25450006827b23038fdac1adfffa37a Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Tue, 26 Mar 2024 23:59:02 -0300 Subject: [PATCH 12/15] feat: Implement Timeline Initial Point --- lib/l10n/app_en.arb | 7 +++- lib/l10n/app_fr.arb | 5 +++ lib/l10n/app_pl.arb | 5 +++ lib/l10n/app_pt.arb | 11 +++-- lib/providers/settings_provider.dart | 7 ++-- .../events_timeline/desktop/timeline.dart | 21 +++++++++- .../events_timeline/events_playback.dart | 28 ++++++++++++- .../settings/events_and_downloads.dart | 40 +++++++++++++------ 8 files changed, 101 insertions(+), 23 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 849ca076..299a2d89 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -472,8 +472,13 @@ "events": "Events", "initialEventSpeed": "Initial event speed", "initialEventVolume": "Initial event volume", - "differentEventColors": "Different events colors", + "differentEventColors": "Different events by colors", "differentEventColorsDescription": "Whether to show different colors for events in the timeline. This assists to easily differentiate the events.", + "initialTimelinePoint": "Initial point", + "initialTimelinePointDescription": "The initial point of the timeline.", + "beginningInitialPoint": "Beginning", + "firstEventInitialPoint": "First event", + "hourAgoInitialPoint": "1 hour ago", "@@APPLICATION": {}, "theme": "Theme", "themeDescription": "Change the appearance of the app", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index d2eeaa73..6007bb65 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -450,6 +450,11 @@ "initialEventVolume": "Initial volume", "differentEventColors": "Different event colors", "differentEventColorsDescription": "Whether to show different colors for events in the timeline. This assists to easily differentiate the events.", + "initialTimelinePoint": "Initial point", + "initialTimelinePointDescription": "The initial point of the timeline.", + "beginningInitialPoint": "Beginning", + "firstEventInitialPoint": "First event", + "hourAgoInitialPoint": "1 hour ago", "@@APPLICATION": {}, "theme": "Thème", "themeDescription": "Modifier l'apparence de l'application", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 4ee180df..754f634a 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -474,6 +474,11 @@ "initialEventVolume": "Initial volume", "differentEventColors": "Different event colors", "differentEventColorsDescription": "Whether to show different colors for events in the timeline. This assists to easily differentiate the events.", + "initialTimelinePoint": "Initial point", + "initialTimelinePointDescription": "The initial point of the timeline.", + "beginningInitialPoint": "Beginning", + "firstEventInitialPoint": "First event", + "hourAgoInitialPoint": "1 hour ago", "@@APPLICATION": {}, "theme": "Motyw", "themeDescription": "Zmień wygląd aplikacji", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 2c3d9b76..b1bfe1a9 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -468,12 +468,17 @@ "@@EVENTS_AND_DOWNLOADS": {}, "chooseEveryDownloadsLocation": "Escolher a localização de cada download", "chooseEveryDownloadsLocationDescription": "Se você deseja escolher a localização de cada download ou usar a localização padrão. Quando ativado, você será solicitado a escolher o diretório de download para cada download.", - "allowCloseWhenDownloading": "Block the app from closing when there are ongoing downloads", + "allowCloseWhenDownloading": "Permitir fechar o aplicativo quando houver downloads em andamento", "events": "Eventos", "initialEventSpeed": "Velocidade inicial", "initialEventVolume": "Volume inicial", - "differentEventColors": "Different event colors", - "differentEventColorsDescription": "Whether to show different colors for events in the timeline. This assists to easily differentiate the events.", + "differentEventColors": "Diferenciar eventos por cor", + "differentEventColorsDescription": "Se deve mostrar cores diferentes para eventos na linha do tempo. Isso ajuda a diferenciar facilmente os eventos.", + "initialTimelinePoint": "Ponto inicial", + "initialTimelinePointDescription": "O ponto em que a linha do tempo inicia.", + "beginningInitialPoint": "Início", + "firstEventInitialPoint": "Primeiro evento", + "hourAgoInitialPoint": "1 hora atrás", "@@APPLICATION": {}, "theme": "Aparência", "themeDescription": "Mude a aparência do aplicativo", diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index e17f8e27..3e13e164 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -22,6 +22,7 @@ import 'dart:io'; import 'package:bluecherry_client/providers/app_provider_interface.dart'; import 'package:bluecherry_client/providers/downloads_provider.dart'; import 'package:bluecherry_client/providers/update_provider.dart'; +import 'package:bluecherry_client/screens/events_timeline/desktop/timeline.dart'; import 'package:bluecherry_client/utils/storage.dart'; import 'package:bluecherry_client/utils/video_player.dart'; import 'package:flutter/foundation.dart'; @@ -32,8 +33,6 @@ import 'package:unity_video_player/unity_video_player.dart'; enum NetworkUsage { auto, wifiOnly, never } -enum TimelineIntialPoint { beggining, firstEvent, lastEvent } - enum EnabledPreference { on, ask, never } class _SettingsOption { @@ -268,9 +267,9 @@ class SettingsProvider extends UnityProvider { key: 'timeline.pause_to_buffer', ); final kTimelineInitialPoint = _SettingsOption( - def: TimelineIntialPoint.beggining, + def: TimelineInitialPoint.beginning, key: 'timeline.initial_point', - loadFrom: (value) => TimelineIntialPoint.values[int.parse(value)], + loadFrom: (value) => TimelineInitialPoint.values[int.parse(value)], saveAs: (value) => value.index.toString(), ); diff --git a/lib/screens/events_timeline/desktop/timeline.dart b/lib/screens/events_timeline/desktop/timeline.dart index d180ebef..427bf7da 100644 --- a/lib/screens/events_timeline/desktop/timeline.dart +++ b/lib/screens/events_timeline/desktop/timeline.dart @@ -45,6 +45,19 @@ import 'package:unity_video_player/unity_video_player.dart'; final timelineTimeFormat = DateFormat('hh:mm:ss a'); +/// The initial point of the timeline. +enum TimelineInitialPoint { + /// The timeline will start at the beginning of the day (00:00:00). + beginning, + + /// The timeline will start at the first event of the day, if any. Otherwise, + /// it will start at the [beginning] of the day. + firstEvent, + + /// The timeline will start at an hour ago from the current time. + hourAgo, +} + class TimelineTile { final Device device; final List events; @@ -167,7 +180,13 @@ class Timeline extends ChangeNotifier { /// All the events must have happened in the same day final DateTime date; - Timeline({required List tiles, required this.date}) { + Timeline({ + required List tiles, + required this.date, + Duration initialPosition = Duration.zero, + }) { + currentPosition = initialPosition; + add(tiles.where((tile) => tile.events.isNotEmpty)); for (final tile in this.tiles) { diff --git a/lib/screens/events_timeline/events_playback.dart b/lib/screens/events_timeline/events_playback.dart index 6c785329..027c2f7c 100644 --- a/lib/screens/events_timeline/events_playback.dart +++ b/lib/screens/events_timeline/events_playback.dart @@ -23,6 +23,7 @@ import 'package:bluecherry_client/models/device.dart'; import 'package:bluecherry_client/models/event.dart'; import 'package:bluecherry_client/providers/downloads_provider.dart'; import 'package:bluecherry_client/providers/events_provider.dart'; +import 'package:bluecherry_client/providers/settings_provider.dart'; import 'package:bluecherry_client/screens/events_browser/events_screen.dart'; import 'package:bluecherry_client/screens/events_timeline/desktop/timeline.dart'; import 'package:bluecherry_client/screens/events_timeline/desktop/timeline_sidebar.dart'; @@ -72,6 +73,7 @@ class _EventsPlaybackState extends EventsScreenState { @override Future fetch() async { final eventsProvider = context.read(); + final settings = context.read(); setState(() { hasEverFetched = true; date = date.toLocal(); @@ -122,7 +124,31 @@ class _EventsPlaybackState extends EventsScreenState { if (mounted) { setState(() { - timeline = Timeline(tiles: parsedTiles, date: date); + timeline = Timeline( + tiles: parsedTiles, + date: date, + initialPosition: switch (settings.kTimelineInitialPoint.value) { + TimelineInitialPoint.beginning => Duration.zero, + TimelineInitialPoint.firstEvent => () { + final firstEvent = parsedTiles + .map((e) { + final earliestEvent = e.events.reduce( + (a, b) => a.startTime.isBefore(b.startTime) ? a : b); + return earliestEvent; + }) + .reduce((a, b) => a.startTime.isBefore(b.startTime) ? a : b) + .startTime; + return Duration( + hours: firstEvent.hour, + minutes: firstEvent.minute, + seconds: firstEvent.second, + ); + }(), + TimelineInitialPoint.hourAgo => Duration( + hours: DateTime.now().hour - 1, + ), + }, + ); }); } } diff --git a/lib/screens/settings/events_and_downloads.dart b/lib/screens/settings/events_and_downloads.dart index 8b04e81c..d4fece9a 100644 --- a/lib/screens/settings/events_and_downloads.dart +++ b/lib/screens/settings/events_and_downloads.dart @@ -20,6 +20,7 @@ import 'dart:io'; import 'package:bluecherry_client/providers/settings_provider.dart'; +import 'package:bluecherry_client/screens/events_timeline/desktop/timeline.dart'; import 'package:bluecherry_client/screens/settings/settings_desktop.dart'; import 'package:bluecherry_client/screens/settings/shared/options_chooser_tile.dart'; import 'package:bluecherry_client/widgets/misc.dart'; @@ -192,20 +193,33 @@ class EventsAndDownloadsSettings extends StatelessWidget { 'Whether the entire timeline should pause to buffer the events.', ), ), - OptionsChooserTile( - title: 'Initial point', - description: 'When the timeline should begin.', - icon: Icons.flag, - value: '', - values: const [ - Option(value: '', icon: Icons.start, text: 'Beginning'), - Option(value: '', icon: Icons.first_page, text: 'First event'), - Option( - value: '', icon: Icons.hourglass_bottom, text: 'An hour ago'), - ], - onChanged: (v) {}, - ), ], + OptionsChooserTile( + title: loc.initialTimelinePoint, + description: loc.initialTimelinePointDescription, + icon: Icons.flag, + value: settings.kTimelineInitialPoint.value, + values: [ + Option( + value: TimelineInitialPoint.beginning, + icon: Icons.start, + text: loc.beginningInitialPoint, + ), + Option( + value: TimelineInitialPoint.firstEvent, + icon: Icons.first_page, + text: loc.firstEventInitialPoint, + ), + Option( + value: TimelineInitialPoint.hourAgo, + icon: Icons.hourglass_bottom, + text: loc.hourAgoInitialPoint, + ), + ], + onChanged: (v) { + settings.kTimelineInitialPoint.value = v; + }, + ), ]); } } From bceac76797cf25f178b871fc95b98382cc074ebe Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 27 Mar 2024 00:01:04 -0300 Subject: [PATCH 13/15] feat: Implement camera refresh period --- lib/screens/settings/server_and_devices.dart | 44 ++++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/screens/settings/server_and_devices.dart b/lib/screens/settings/server_and_devices.dart index 2f803d66..f4584ed0 100644 --- a/lib/screens/settings/server_and_devices.dart +++ b/lib/screens/settings/server_and_devices.dart @@ -124,29 +124,27 @@ class StreamingSettings extends StatelessWidget { settings.kVideoFit.value = v; }, ), - if (settings.kShowDebugInfo.value) ...[ - const SizedBox(height: 8.0), - OptionsChooserTile( - title: 'Refresh Period', - description: 'How often to refresh the cameras', - icon: Icons.sync, - value: settings.kRefreshRate.value, - values: const [ - Duration.zero, - Duration(seconds: 30), - Duration(minutes: 2), - Duration(minutes: 5), - ].map((q) { - return Option( - value: q, - text: q.humanReadableCompact(context), - ); - }), - onChanged: (v) { - settings.kRefreshRate.value = v; - }, - ), - ], + const SizedBox(height: 8.0), + OptionsChooserTile( + title: loc.cameraRefreshPeriod, + description: loc.cameraRefreshPeriodDescription, + icon: Icons.sync, + value: settings.kRefreshRate.value, + values: const [ + Duration.zero, + Duration(seconds: 30), + Duration(minutes: 2), + Duration(minutes: 5), + ].map((q) { + return Option( + value: q, + text: q.humanReadableCompact(context), + ); + }), + onChanged: (v) { + settings.kRefreshRate.value = v; + }, + ), const SizedBox(height: 8.0), OptionsChooserTile( title: loc.lateStreamBehavior, From aeea189a926ce56a663e54996ac7c2a52a851041 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Thu, 28 Mar 2024 11:05:35 -0300 Subject: [PATCH 14/15] fix: allow crash reports option is properly saved and retrieved --- lib/providers/settings_provider.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 3e13e164..fa8904df 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -216,7 +216,7 @@ class SettingsProvider extends UnityProvider { def: true, key: 'streaming.reload_timed_out_streams', ); - final kUseHardwareDecoding = _SettingsOption( + final kUseHardwareDecoding = _SettingsOption( def: true, key: 'streaming.use_hardware_decoding', ); @@ -325,6 +325,8 @@ class SettingsProvider extends UnityProvider { final kAllowCrashReports = _SettingsOption( def: EnabledPreference.on, key: 'privacy.allow_crash_reports', + loadFrom: (value) => EnabledPreference.values[int.parse(value)], + saveAs: (value) => value.index.toString(), ); // Updates From 40d5016b20e2233603531f376c8375cc05320e25 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Thu, 28 Mar 2024 11:53:58 -0300 Subject: [PATCH 15/15] fix: Create players at startup on the microtask event loop --- lib/providers/desktop_view_provider.dart | 32 ++++++++++--------- lib/providers/mobile_view_provider.dart | 10 +++--- .../lib/unity_video_player_main.dart | 4 +-- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/providers/desktop_view_provider.dart b/lib/providers/desktop_view_provider.dart index db0b62b8..860622a7 100644 --- a/lib/providers/desktop_view_provider.dart +++ b/lib/providers/desktop_view_provider.dart @@ -63,21 +63,23 @@ class DesktopViewProvider extends UnityProvider { await tryReadStorage( () => initializeStorage(desktopView, kStorageDesktopLayouts), ); - await Future.wait( - currentLayout.devices.map((device) { - final completer = Completer(); - UnityPlayers.players[device.uuid] ??= UnityPlayers.forDevice( - device, - () async { - if (Platform.isLinux) { - await Future.delayed(const Duration(milliseconds: 250)); - } - completer.complete(UnityPlayers.players[device.uuid]); - }, - ); - return completer.future; - }), - ); + Future.microtask(() async { + await Future.wait( + currentLayout.devices.map((device) { + final completer = Completer(); + UnityPlayers.players[device.uuid] ??= UnityPlayers.forDevice( + device, + () async { + if (Platform.isAndroid || Platform.isLinux) { + await Future.delayed(const Duration(milliseconds: 250)); + } + completer.complete(UnityPlayers.players[device.uuid]); + }, + ); + return completer.future; + }), + ); + }); } /// Saves current layout/order of [Device]s to cache using `package:hive`. diff --git a/lib/providers/mobile_view_provider.dart b/lib/providers/mobile_view_provider.dart index 50f4827e..115eed18 100644 --- a/lib/providers/mobile_view_provider.dart +++ b/lib/providers/mobile_view_provider.dart @@ -75,11 +75,13 @@ class MobileViewProvider extends UnityProvider { Future initialize() async { await tryReadStorage( () => super.initializeStorage(mobileView, kStorageMobileView)); - for (final device in current) { - if (device != null) { - UnityPlayers.players[device.uuid] ??= UnityPlayers.forDevice(device); + Future.microtask(() { + for (final device in current) { + if (device != null) { + UnityPlayers.players[device.uuid] ??= UnityPlayers.forDevice(device); + } } - } + }); } /// Moves a device tile from [initial] position to [end] position inside a [tab]. diff --git a/packages/unity_video_player/unity_video_player_main/lib/unity_video_player_main.dart b/packages/unity_video_player/unity_video_player_main/lib/unity_video_player_main.dart index c9f34a58..dd0f1f3c 100644 --- a/packages/unity_video_player/unity_video_player_main/lib/unity_video_player_main.dart +++ b/packages/unity_video_player/unity_video_player_main/lib/unity_video_player_main.dart @@ -183,14 +183,14 @@ class UnityVideoPlayerMediaKit extends UnityVideoPlayer { _fpsStreamController.add(_fps); }) ..observeProperty('width', (width) async { - debugPrint('display width: $width'); + debugPrint('$title: display width: $width/${this.width}'); this.width = int.tryParse(width); if (this.width != null && this.width! > maxSize.width) { maxSize = Size(this.width!.toDouble(), maxSize.height); } }) ..observeProperty('height', (height) async { - debugPrint('display height: $height'); + debugPrint('$title: display height: $height/${this.height}'); this.height = int.tryParse(height); if (this.height != null && this.height! > maxSize.height) { maxSize = Size(maxSize.width, this.height!.toDouble());