diff --git a/lib/constants.dart b/lib/constants.dart index d398f59..4e47917 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -65,7 +65,7 @@ class AppConstants { if (context != null) { return chipsWolPorts .map((e) => CustomChoiceChip( - label: "${AppLocalizations.of(context)!.formPort} ${e.value}", + label: AppLocalizations.of(context)!.formPort(e.value), value: e.value)) .toList(); } else { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 0d05008..f00392f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -15,15 +15,84 @@ "back": "Back", "genericWarning": "Warning", "@HOME": {}, + "homeSortAlphabetical": "alphabetical", + "homeSortRecent": "recently", + "homeSortType": "type", "homeAddDeviceButton": "Add Devices", "homeNoDevices": "No devices yet. Add devices by clicking the + button below", "homeFilterDevicesTitle": "Filter Devices", "homeDeviceListTitle": "Devices", + "homeEditDeviceAlertTitle": "Edit Device", "homeDeviceCardWakeButton": "Wake Up", "homeDeviceCardEditButton": "Edit", "homeDeviceCardOnline": "Device is online", "homeDeviceCardOffline": "Device is offline", "homeWolCardTitle": "Waking up...", + "homeWolCardIp": "'{ip}' is an invalid IPv4 address", + "@homeWolCardIp": { + "placeholders": { + "ip": { + "type": "String", + "example": "192.168.0.1", + "description": "The IP address which was provided by the user." + } + } + }, + "homeWolCardMac": "'{mac}' is an invalid MAC address", + "@homeWolCardMac": { + "placeholders": { + "mac": { + "type": "String", + "example": "12:AB:00:00:00:00", + "description": "The MAC address which was provided by the user." + } + } + }, + "homeWolCardPort": "'{port}' is an invalid port", + "@homeWolCardPort": { + "placeholders": { + "port": { + "type": "String", + "example": "9", + "description": "The port number which was provided by the user." + } + } + }, + "homeWolCardInvalid": "There was an error trying to send a WOL Package", + "homeWolCardValid": "Provided device details are valid", + "homeWolCardSendWol": "Trying to send WOL Packages", + "homeWolCardSendWolSuccess": "Successfully send WOL packages to {ip}", + "@homeWolCardSendWolSuccess": { + "placeholders": { + "ip": { + "type": "String", + "example": "192.168.0.1", + "description": "The IP address of the device which was woken up." + } + } + }, + "homeWolCardSendWolFail": "There was an error when trying to send WOL Packages to the host {ip}", + "@homeWolCardSendWolFail": { + "placeholders": { + "ip": { + "type": "String", + "example": "192.168.0.1", + "description": "The IP address of the device which could not be woken up." + } + } + }, + "homeWolCardPingInfo": "Trying to ping device until it is online...", + "homeWolCardPing": "Sending ping #{count}", + "@homeWolCardPing": { + "placeholders": { + "count": { + "type": "int", + "example": "1" + } + } + }, + "homeWolCardPingSuccess": "Device is online", + "homeWolCardPingFail": "Device is still offline", "@DISCOVER": {}, "discoverTitle": "Discover Devices", "discoverCardTitle": "Network Scan", @@ -49,7 +118,15 @@ "formIconLabel": "Icon", "formIconError": "no device type selected", "formIconErrorTitle": "Wrong Format", - "formPort": "Port", + "formPort": "Port {port}", + "@formPort": { + "placeholders": { + "port": { + "type": "int", + "example": "9" + } + } + }, "formErrorMessageName": "device has no name", "formErrorMessageIp": "invalid IP Address", "formErrorMessageMac": "invalid MAC Address", @@ -104,5 +181,24 @@ "aboutInfoText": "A simple tool to wake devices in the local network remotely if they are turned off. This app tries to make this process easy for the user.", "aboutOpenSourceTitle": "Open Source", "aboutOpenSourceCodeButton": "Source Code", - "aboutOpenSourceLicenseButton": "Licenses" + "aboutOpenSourceLicenseButton": "Licenses", + "aboutVersionText": "Version: {version} ({buildNumber})", + "@aboutVersionText": { + "placeholders": { + "version": { + "type": "String", + "example": "1.0.0" + }, + "buildNumber": { + "type": "String", + "example": "1" + } + } + }, + "aboutWebPlatformError": "Web platform isn't supported", + "aboutFuchsiaPlatformError": "Fuchsia platform isn't supported", + "aboutLinuxPlatformError": "Linux platform isn't supported", + "aboutMacOSPlatformError": "macOS platform isn't supported", + "aboutWindowsPlatformError": "Windows platform isn't supported", + "aboutNoPlatformDetected": "Failed to get platform version" } diff --git a/lib/screens/about/about.dart b/lib/screens/about/about.dart index 0b0e142..7cd83b6 100644 --- a/lib/screens/about/about.dart +++ b/lib/screens/about/about.dart @@ -37,17 +37,23 @@ class _AboutPageState extends State { @override void initState() { super.initState(); - initPlatformState(); initWifiAddress(); } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + // not in initSate because buildContext is needed + initPlatformState(); + } + Future initPlatformState() async { var deviceData = {}; try { if (kIsWeb) { deviceData = { - 'Error:': 'Web platform isn\'t supported' + 'Error:': AppLocalizations.of(context)!.aboutWebPlatformError }; } else { switch (defaultTargetPlatform) { @@ -60,29 +66,29 @@ class _AboutPageState extends State { break; case TargetPlatform.fuchsia: deviceData = { - 'Error:': 'Fuchsia platform isn\'t supported' + 'Error:': AppLocalizations.of(context)!.aboutFuchsiaPlatformError }; break; case TargetPlatform.linux: deviceData = { - 'Error:': 'Linux platform isn\'t supported' + 'Error:': AppLocalizations.of(context)!.aboutLinuxPlatformError }; break; case TargetPlatform.macOS: deviceData = { - 'Error:': 'MacOS platform isn\'t supported' + 'Error:': AppLocalizations.of(context)!.aboutMacOSPlatformError }; break; case TargetPlatform.windows: deviceData = { - 'Error:': 'Windows platform isn\'t supported' + 'Error:': AppLocalizations.of(context)!.aboutWindowsPlatformError }; break; } } } on PlatformException { deviceData = { - 'Error:': 'Failed to get platform version.' + 'Error:': AppLocalizations.of(context)!.aboutNoPlatformDetected }; } @@ -163,14 +169,10 @@ class _AboutPageState extends State { children: [ VersionText(text: widget.packageInfo.appName), VersionText(text: widget.packageInfo.packageName), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const VersionText(text: "Version "), - VersionText(text: widget.packageInfo.version), - VersionText(text: " (${widget.packageInfo.buildNumber})"), - ], - ), + VersionText( + text: AppLocalizations.of(context)!.aboutVersionText( + widget.packageInfo.version, + widget.packageInfo.buildNumber)), ], ) ], @@ -188,15 +190,14 @@ class _AboutPageState extends State { child: InkWell( borderRadius: AppConstants.borderRadius, child: ListTile( - // title: const Text("iPhone XR"), title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: _deviceData.keys.map((String property) { - return Text('${_deviceData[property]}'); - }).toList(), // Text("IP: $_wifiAddress \nMAC: 12:12:12:12:12") + return Text(_deviceData[property].toString()); + }).toList(), ), subtitle: Text( - "IP: $_wifiAddress", + "${AppConstants.ipText}: $_wifiAddress", style: Theme.of(context).textTheme.bodySmall, ), minLeadingWidth: 0, diff --git a/lib/screens/home/bottom_sheet_form.dart b/lib/screens/home/bottom_sheet_form.dart index e69c9cf..4237921 100644 --- a/lib/screens/home/bottom_sheet_form.dart +++ b/lib/screens/home/bottom_sheet_form.dart @@ -519,7 +519,7 @@ class _ModularBottomFormPageState extends State { TextSpan(text: AppLocalizations.of(context)!.formDeleteAlertText), if (widget.controllerName.text.isNotEmpty) TextSpan( - text: " ${widget.controllerName.text}", + text: widget.controllerName.text, style: const TextStyle(fontWeight: FontWeight.bold)), const TextSpan(text: '?'), ], diff --git a/lib/screens/home/home.dart b/lib/screens/home/home.dart index b6ef092..08eb814 100644 --- a/lib/screens/home/home.dart +++ b/lib/screens/home/home.dart @@ -186,17 +186,17 @@ class _HomePageState extends State { sortDevices(); }, itemBuilder: (BuildContext context) => >[ - const PopupMenuItem( + PopupMenuItem( value: SortingOrder.alphabetical, - child: Text('alphabetical'), + child: Text(AppLocalizations.of(context)!.homeSortAlphabetical), ), - const PopupMenuItem( + PopupMenuItem( value: SortingOrder.recently, - child: Text('recently'), + child: Text(AppLocalizations.of(context)!.homeSortRecent), ), - const PopupMenuItem( + PopupMenuItem( value: SortingOrder.type, - child: Text('type'), + child: Text(AppLocalizations.of(context)!.homeSortType), ), ], ), @@ -458,7 +458,8 @@ class _HomePageState extends State { showCustomBottomSheet( context: context, formPage: EditDeviceFormPage( - title: "Edit Device", + title: AppLocalizations.of(context)! + .homeEditDeviceAlertTitle, device: device, devices: _devices, onSubmitDeviceCallback: updateDevicesList)) @@ -472,7 +473,8 @@ class _HomePageState extends State { context: context, builder: (context) { return StreamBuilder>( - stream: sendWolAndGetMessages(device: device.toNetworkDevice()), + stream: sendWolAndGetMessages( + context: context, device: device.toNetworkDevice()), builder: (BuildContext context, AsyncSnapshot> snapshot) { // set color, text and icon of dialog box according to the arrived messages diff --git a/lib/services/network.dart b/lib/services/network.dart index 3959648..ff282ea 100644 --- a/lib/services/network.dart +++ b/lib/services/network.dart @@ -1,10 +1,12 @@ import 'dart:async'; import 'package:dart_ping/dart_ping.dart'; +import 'package:flutter/material.dart'; import 'package:simple_wake_on_lan/constants.dart'; import 'dart:io'; import 'package:wake_on_lan/wake_on_lan.dart'; import 'data.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; Stream findDevicesInNetwork( String networkPrefix, @@ -61,7 +63,8 @@ Stream findDevicesInNetwork( } /// sends the magic packet to the [device] that should receive a magic wol package in order to get woken up -Stream sendWolPackage({required NetworkDevice device}) async* { +Stream sendWolPackage( + {required BuildContext context, required NetworkDevice device}) async* { // Validate correct formatting of ip and mac addresses final ip = device.ipAddress; final mac = device.macAddress; @@ -69,29 +72,38 @@ Stream sendWolPackage({required NetworkDevice device}) async* { bool invalid = false; if (!IPv4Address.validate(ip)) { - yield Message(text: "'$ip' is a invalid IPv4 address", type: MsgType.error); + yield Message( + text: AppLocalizations.of(context)!.homeWolCardIp(ip), + type: MsgType.error); invalid = true; } if (!MACAddress.validate(mac)) { - yield Message(text: "'$mac' is a invalid MAC address", type: MsgType.error); + yield Message( + text: AppLocalizations.of(context)!.homeWolCardMac(mac), + type: MsgType.error); invalid = true; } //validate port if (port == null || port < 0 || port > 65535) { - yield Message(text: "'$port' is an invalid port", type: MsgType.error); + String portString = port == null ? "" : port.toString(); + yield Message( + text: AppLocalizations.of(context)!.homeWolCardPort(portString), + type: MsgType.error); invalid = true; } if (invalid) { - // yield Message(text: "There was a error when trying to send a WOL Package to this host", type: MsgType.error); + yield Message( + text: AppLocalizations.of(context)!.homeWolCardInvalid, + type: MsgType.error); return; } // if no error occurred: try to send wol package - yield Message(text: "Provided ip and mac address are both valid"); - yield Message(text: "Trying to send WOL Packages"); + yield Message(text: AppLocalizations.of(context)!.homeWolCardValid); + yield Message(text: AppLocalizations.of(context)!.homeWolCardSendWol); IPv4Address ipv4Address = IPv4Address(ip); MACAddress macAddress = MACAddress(mac); @@ -102,28 +114,43 @@ Stream sendWolPackage({required NetworkDevice device}) async* { final broadcast = "$subnet.255"; IPv4Address ipv4Broadcast = IPv4Address(broadcast); + // get localisation string beforehand to avoid using BuildContexts across async gaps + String homeWolCardSendWolSuccess = + AppLocalizations.of(context)!.homeWolCardSendWolSuccess(ip); + String homeWolCardPingInfo = + AppLocalizations.of(context)!.homeWolCardPingInfo; + String homeWolCardPingSuccess = + AppLocalizations.of(context)!.homeWolCardPingSuccess; + String homeWolCardPingFail = + AppLocalizations.of(context)!.homeWolCardPingFail; + try { WakeOnLAN wol = WakeOnLAN(ipv4Address, macAddress, port: port!); - await wol.wake(repeat: 5); + await wol.wake(repeat: 3); await Future.delayed(const Duration(seconds: 1)); WakeOnLAN wolBroadcast = WakeOnLAN(ipv4Broadcast, macAddress, port: port); - await wolBroadcast.wake(repeat: 5); - yield Message( - text: "Successfully send WOL packages to $ip", type: MsgType.check); + await wolBroadcast.wake(repeat: 3); + yield Message(text: homeWolCardSendWolSuccess, type: MsgType.check); } catch (e) { yield Message( - text: "There was a error when trying to send WOL Packages to this host", + text: AppLocalizations.of(context)!.homeWolCardSendWolFail(ip), type: MsgType.error); } // ping device until it is online - yield Message(text: "Trying to ping device until it is online..."); + yield Message(text: homeWolCardPingInfo); bool online = false; int tries = 0; const maxPings = 25; while (!online && tries < maxPings) { tries++; - yield Message(text: "Sending ping #$tries", type: MsgType.ping); + + // BuildContext has to be used async here to get the current tries in the message + // ignore: use_build_context_synchronously + if (!context.mounted) return; + yield Message( + text: AppLocalizations.of(context)!.homeWolCardPing(tries), + type: MsgType.ping); final ping = Ping(ip, count: 1, timeout: 5); @@ -135,18 +162,19 @@ Stream sendWolPackage({required NetworkDevice device}) async* { } } if (online) { - yield Message(text: "Device is online", type: MsgType.online); + yield Message(text: homeWolCardPingSuccess, type: MsgType.online); } else { - yield Message(text: "Device is not online", type: MsgType.error); + yield Message(text: homeWolCardPingFail, type: MsgType.error); } } /// returns a list of Messages by using the sendWolPackage function /// accumulates the messages in a list and yields the list after each message Stream> sendWolAndGetMessages( - {required NetworkDevice device}) async* { + {required BuildContext context, required NetworkDevice device}) async* { List messages = []; - await for (Message message in sendWolPackage(device: device)) { + await for (Message message + in sendWolPackage(context: context, device: device)) { // if last message is ping, replace it with the new one if (messages.isNotEmpty && messages.last.type == MsgType.ping &&