From 6ae77d828700595a47301e177b90d7bb4075a086 Mon Sep 17 00:00:00 2001 From: adeveloper-wq Date: Fri, 4 Oct 2024 15:32:40 +0200 Subject: [PATCH] Add option to enter the password for the live tracking MQTT broker --- lib/ride/services/live_tracking.dart | 8 +-- lib/settings/models/backend.dart | 11 ++++ lib/settings/services/settings.dart | 26 +++++++++ lib/settings/views/internal.dart | 81 ++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 3 deletions(-) diff --git a/lib/ride/services/live_tracking.dart b/lib/ride/services/live_tracking.dart index 1a82a4de5..915b6737a 100644 --- a/lib/ride/services/live_tracking.dart +++ b/lib/ride/services/live_tracking.dart @@ -72,7 +72,7 @@ class LiveTracking { Future _connectMQTTClient() async { // Get the backend that is currently selected. final settings = getIt(); - final backend = settings.city.selectedBackend(true); + final backend = settings.city.selectedBackend(false); final clientId = "priobike-app-$appId"; try { client = MqttServerClient( @@ -81,7 +81,7 @@ class LiveTracking { ); client!.logging(on: false); client!.keepAlivePeriod = 30; - client!.secure = false; + client!.secure = true; client!.port = backend.liveTrackingMQTTPort; client!.autoReconnect = true; client!.resubscribeOnAutoReconnect = true; @@ -97,7 +97,9 @@ class LiveTracking { .startClean() .withWillQos(MqttQos.atMostOnce); log.i("Connecting to live tracking MQTT broker."); - await client!.connect().timeout(const Duration(seconds: 5)); + await client! + .connect(settings.city.selectedBackend(false).liveTrackingMQTTUsername, settings.liveTrackingMQTTPassword) + .timeout(const Duration(seconds: 5)); client!.connectionMessage = MqttConnectMessage() .withClientIdentifier(client!.clientIdentifier) diff --git a/lib/settings/models/backend.dart b/lib/settings/models/backend.dart index f296f3851..6298f3035 100644 --- a/lib/settings/models/backend.dart +++ b/lib/settings/models/backend.dart @@ -277,6 +277,17 @@ extension LiveTracking on Backend { return 20001; } } + + String get liveTrackingMQTTUsername { + switch (this) { + case Backend.production: + return "priobike"; + case Backend.staging: + return "priobike"; + case Backend.release: + return "priobike"; + } + } } extension Simulator on Backend { diff --git a/lib/settings/services/settings.dart b/lib/settings/services/settings.dart index 34e3c7e7c..817cfcc02 100644 --- a/lib/settings/services/settings.dart +++ b/lib/settings/services/settings.dart @@ -86,6 +86,9 @@ class Settings with ChangeNotifier { /// Enable live tracking mode for app. bool enableLiveTrackingMode; + /// The password for the tracking MQTT broker. + String liveTrackingMQTTPassword; + /// If we want to show the speed with increased precision in the speedometer. bool isIncreasedSpeedPrecisionInSpeedometerEnabled = false; @@ -418,6 +421,23 @@ class Settings with ChangeNotifier { notifyListeners(); } + static const liveTrackingMQTTPasswordKey = "priobike.settings.liveTrackingMQTTPassword"; + static const defaultLiveTrackingMQTTPassword = ""; + + Future setLiveTrackingMQTTPassword(String liveTrackingMQTTPassword, [SharedPreferences? storage]) async { + storage ??= await SharedPreferences.getInstance(); + final prev = this.liveTrackingMQTTPassword; + this.liveTrackingMQTTPassword = liveTrackingMQTTPassword; + final bool success = await storage.setString(liveTrackingMQTTPasswordKey, liveTrackingMQTTPassword); + if (!success) { + log.e("Failed to set liveTrackingMQTTPassword to $liveTrackingMQTTPassword"); + this.liveTrackingMQTTPassword = prev; + } else { + notifyListeners(); + } + return success; + } + static const didMigrateBackgroundImagesKey = "priobike.settings.didMigrateBackgroundImages"; static const defaultDidMigrateBackgroundImages = false; @@ -494,6 +514,7 @@ class Settings with ChangeNotifier { this.didMigrateBackgroundImages = defaultDidMigrateBackgroundImages, this.enableSimulatorMode = defaultSimulatorMode, this.enableLiveTrackingMode = defaultLiveTrackingMode, + this.liveTrackingMQTTPassword = defaultLiveTrackingMQTTPassword, this.isIncreasedSpeedPrecisionInSpeedometerEnabled = defaultIsIncreasedSpeedPrecisionInSpeedometerEnabled, this.speechRate = defaultSpeechRate, }); @@ -537,6 +558,11 @@ class Settings with ChangeNotifier { } catch (e) { /* Do nothing and use the default value given by the constructor. */ } + try { + liveTrackingMQTTPassword = storage.getString(liveTrackingMQTTPasswordKey) ?? defaultLiveTrackingMQTTPassword; + } catch (e) { + /* Do nothing and use the default value given by the constructor. */ + } } /// Load the stored settings. diff --git a/lib/settings/views/internal.dart b/lib/settings/views/internal.dart index 7521ec1d1..52ee94fe9 100644 --- a/lib/settings/views/internal.dart +++ b/lib/settings/views/internal.dart @@ -1,7 +1,10 @@ +import 'dart:ui'; + import 'package:flutter/material.dart' hide Shortcuts; import 'package:priobike/common/fcm.dart'; import 'package:priobike/common/layout/annotated_region.dart'; import 'package:priobike/common/layout/buttons.dart'; +import 'package:priobike/common/layout/dialog.dart'; import 'package:priobike/common/layout/modal.dart'; import 'package:priobike/common/layout/spacing.dart'; import 'package:priobike/common/layout/text.dart'; @@ -112,6 +115,76 @@ class InternalSettingsViewState extends State { super.dispose(); } + /// Show a sheet to edit the current live tracking MQTT password. + void showEditLiveTrackingMQTTPasswordSheet(context) { + showGeneralDialog( + context: context, + barrierDismissible: true, + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, + barrierColor: Colors.black.withOpacity(0.4), + transitionBuilder: (context, animation, secondaryAnimation, child) => BackdropFilter( + filter: ImageFilter.blur(sigmaX: 4 * animation.value, sigmaY: 4 * animation.value), + child: FadeTransition( + opacity: animation, + child: child, + ), + ), + pageBuilder: (BuildContext dialogContext, Animation animation, Animation secondaryAnimation) { + final passwordController = TextEditingController(); + passwordController.text = settings.liveTrackingMQTTPassword; + return DialogLayout( + title: 'Live Tracking MQTT Passwort', + text: "Bitte gib ein Passwort ein.", + actions: [ + TextField( + autofocus: false, + controller: passwordController, + maxLength: 20, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + fillColor: Theme.of(context).colorScheme.primary.withOpacity(0.1), + filled: true, + border: const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(16)), + borderSide: BorderSide.none, + ), + suffixIcon: SmallIconButtonTertiary( + icon: Icons.close, + onPressed: () { + passwordController.text = ""; + }, + color: Theme.of(context).colorScheme.onSurface, + fill: Colors.transparent, + // splash: Colors.transparent, + withBorder: false, + ), + contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + counterStyle: TextStyle( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8), + ), + ), + ), + BigButtonPrimary( + label: "Speichern", + onPressed: () async { + final password = passwordController.text; + if (password.trim().isEmpty) { + getIt().showError("Das Passwort darf nicht leer sein."); + return; + } + await settings.setLiveTrackingMQTTPassword(password); + getIt().showSuccess("Passwort gespeichert!"); + Navigator.pop(context); + }, + boxConstraints: BoxConstraints(minWidth: MediaQuery.of(context).size.width, minHeight: 36), + ) + ], + ); + }, + ); + } + /// A callback that is executed when a sg labels mode is selected. Future onSelectSGLabelsMode(SGLabelsMode mode) async { // Tell the settings service that we selected the new sg labels mode. @@ -540,6 +613,14 @@ class InternalSettingsViewState extends State { callback: () => settings.setLiveTrackingMode(!settings.enableLiveTrackingMode), ), ), + Padding( + padding: const EdgeInsets.only(top: 8), + child: SettingsElement( + title: "Live Tracking MQTT Passwort", + icon: Icons.password_rounded, + callback: () => showEditLiveTrackingMQTTPasswordSheet(context), + ), + ), Padding( padding: const EdgeInsets.only(top: 8), child: SettingsElement(