Implement password protection

This commit is contained in:
Anton Tananaev 2025-06-28 14:57:56 -07:00
parent 52a0dd006f
commit 565ddb090f
7 changed files with 209 additions and 10 deletions

View file

@ -95,6 +95,8 @@ PODS:
- flutter_background_geolocation (4.16.11):
- CocoaLumberjack (~> 3.8.5)
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
- GoogleAppMeasurement (11.13.0):
- GoogleAppMeasurement/AdIdSupport (= 11.13.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
@ -153,6 +155,9 @@ PODS:
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- PromisesObjC (2.4.0)
- PromisesSwift (2.4.0):
- PromisesObjC (= 2.4.0)
@ -173,7 +178,9 @@ DEPENDENCIES:
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`)
- flutter_background_geolocation (from `.symlinks/plugins/flutter_background_geolocation/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- quick_actions_ios (from `.symlinks/plugins/quick_actions_ios/ios`)
- rate_my_app (from `.symlinks/plugins/rate_my_app/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
@ -213,8 +220,12 @@ EXTERNAL SOURCES:
:path: Flutter
flutter_background_geolocation:
:path: ".symlinks/plugins/flutter_background_geolocation/ios"
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
mobile_scanner:
:path: ".symlinks/plugins/mobile_scanner/darwin"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
quick_actions_ios:
:path: ".symlinks/plugins/quick_actions_ios/ios"
rate_my_app:
@ -241,11 +252,13 @@ SPEC CHECKSUMS:
FirebaseSessions: eaa8ec037e7793769defe4201c20bd4d976f9677
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_background_geolocation: defe705fe7c50b1be772e0d298ed3765fa17c022
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
GoogleAppMeasurement: 0dfca1a4b534d123de3945e28f77869d10d0d600
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
quick_actions_ios: 4b07fb49d8d8f3518d7565fbb7a91014067a7d82

View file

@ -28,7 +28,9 @@
"trackingLabel": "Continuous tracking",
"motionLabel": "Active movement",
"advancedLabel": "Advanced settings",
"passwordLabel": "Password",
"optimizationMessage": "To ensure reliable tracking, please disable battery optimization for this app.",
"passwordError": "Wrong password",
"startAction": "Start service",
"stopAction": "Stop service",
"sosAction": "Send SOS"

View file

@ -1,6 +1,7 @@
import 'dart:developer' as developer;
import 'package:flutter/material.dart';
import 'package:traccar_client/password_service.dart';
import 'package:traccar_client/preferences.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
@ -93,13 +94,15 @@ class _MainScreenState extends State<MainScreen> {
contentPadding: EdgeInsets.zero,
title: Text(AppLocalizations.of(context)!.trackingLabel),
value: trackingEnabled,
onChanged: (bool value) {
onChanged: (bool value) async {
if (await PasswordService.authenticate(context) && mounted) {
if (value) {
bg.BackgroundGeolocation.start();
_checkBatteryOptimizations(context);
} else {
bg.BackgroundGeolocation.stop();
}
}
},
),
if (stopDetection == false)
@ -166,10 +169,12 @@ class _MainScreenState extends State<MainScreen> {
children: [
FilledButton.tonal(
onPressed: () async {
if (await PasswordService.authenticate(context) && mounted) {
await Navigator.push(context, MaterialPageRoute(builder: (_) => const SettingsScreen()));
setState(() {
stopDetection = Preferences.instance.getBool(Preferences.stopDetection);
});
}
},
child: Text(AppLocalizations.of(context)!.settingsButton),
),

57
lib/password_service.dart Normal file
View file

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:traccar_client/l10n/app_localizations.dart';
class PasswordService {
static final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();
static const String _passwordKey = 'password';
static Future<bool> authenticate(BuildContext context) async {
if (!await _secureStorage.containsKey(key: _passwordKey)) return true;
final controller = TextEditingController();
bool? result;
if (context.mounted) {
result = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
content: TextField(
controller: controller,
autofocus: true,
obscureText: true,
decoration: InputDecoration(labelText: AppLocalizations.of(context)!.passwordLabel),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: Text(AppLocalizations.of(context)!.cancelButton),
),
TextButton(
onPressed: () async {
final password = await _secureStorage.read(key: _passwordKey);
if (context.mounted) {
Navigator.pop(context, password == controller.text);
}
},
child: Text(AppLocalizations.of(context)!.okButton),
),
],
),
);
}
if (result != true && context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context)!.passwordError)),
);
return false;
}
return result == true;
}
static Future<void> setPassword(String password) async {
if (password.isNotEmpty) {
await _secureStorage.write(key: _passwordKey, value: password);
} else {
await _secureStorage.delete(key: _passwordKey);
}
}
}

View file

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
import 'package:traccar_client/password_service.dart';
import 'package:traccar_client/qr_code_screen.dart';
import 'package:wakelock_partial_android/wakelock_partial_android.dart';
@ -83,6 +84,33 @@ class _SettingsScreenState extends State<SettingsScreen> {
}
}
Future<void> _changePassword() async {
final controller = TextEditingController();
final result = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
content: TextField(
controller: controller,
decoration: InputDecoration(labelText: AppLocalizations.of(context)!.passwordLabel),
obscureText: true,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: Text(AppLocalizations.of(context)!.cancelButton),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text(AppLocalizations.of(context)!.saveButton),
),
],
),
);
if (result == true) {
await PasswordService.setPassword(controller.text);
}
}
Widget _buildListTile(String title, String key, bool isInt) {
String? value;
if (isInt) {
@ -201,6 +229,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
setState(() {});
},
),
if (advanced)
ListTile(
title: Text(AppLocalizations.of(context)!.passwordLabel),
onTap: _changePassword,
),
],
),
);

View file

@ -203,6 +203,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.1"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
url: "https://pub.dev"
source: hosted
version: "9.2.4"
flutter_secure_storage_linux:
dependency: transitive
description:
name: flutter_secure_storage_linux
sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
url: "https://pub.dev"
source: hosted
version: "1.2.3"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
url: "https://pub.dev"
source: hosted
version: "1.2.1"
flutter_secure_storage_windows:
dependency: transitive
description:
name: flutter_secure_storage_windows
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
url: "https://pub.dev"
source: hosted
version: "3.1.2"
flutter_test:
dependency: "direct dev"
description: flutter
@ -221,6 +269,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.20.2"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
leak_tracker:
dependency: transitive
description:
@ -293,6 +349,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
path_provider:
dependency: transitive
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
@ -514,6 +594,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
win32:
dependency: transitive
description:
name: win32
sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03"
url: "https://pub.dev"
source: hosted
version: "5.14.0"
xdg_directories:
dependency: transitive
description:

View file

@ -23,6 +23,7 @@ dependencies:
shared_preferences_android: ^2.4.10
wakelock_partial_android: ^1.0.0
mobile_scanner: ^7.0.1
flutter_secure_storage: ^9.2.4
dev_dependencies:
flutter_test: