Implement password protection
This commit is contained in:
parent
52a0dd006f
commit
565ddb090f
7 changed files with 209 additions and 10 deletions
|
|
@ -95,6 +95,8 @@ PODS:
|
||||||
- flutter_background_geolocation (4.16.11):
|
- flutter_background_geolocation (4.16.11):
|
||||||
- CocoaLumberjack (~> 3.8.5)
|
- CocoaLumberjack (~> 3.8.5)
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- flutter_secure_storage (6.0.0):
|
||||||
|
- Flutter
|
||||||
- GoogleAppMeasurement (11.13.0):
|
- GoogleAppMeasurement (11.13.0):
|
||||||
- GoogleAppMeasurement/AdIdSupport (= 11.13.0)
|
- GoogleAppMeasurement/AdIdSupport (= 11.13.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
|
@ -153,6 +155,9 @@ PODS:
|
||||||
- nanopb/encode (= 3.30910.0)
|
- nanopb/encode (= 3.30910.0)
|
||||||
- nanopb/decode (3.30910.0)
|
- nanopb/decode (3.30910.0)
|
||||||
- nanopb/encode (3.30910.0)
|
- nanopb/encode (3.30910.0)
|
||||||
|
- path_provider_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
- PromisesObjC (2.4.0)
|
- PromisesObjC (2.4.0)
|
||||||
- PromisesSwift (2.4.0):
|
- PromisesSwift (2.4.0):
|
||||||
- PromisesObjC (= 2.4.0)
|
- PromisesObjC (= 2.4.0)
|
||||||
|
|
@ -173,7 +178,9 @@ DEPENDENCIES:
|
||||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- flutter_background_geolocation (from `.symlinks/plugins/flutter_background_geolocation/ios`)
|
- 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`)
|
- 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`)
|
- quick_actions_ios (from `.symlinks/plugins/quick_actions_ios/ios`)
|
||||||
- rate_my_app (from `.symlinks/plugins/rate_my_app/darwin`)
|
- rate_my_app (from `.symlinks/plugins/rate_my_app/darwin`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
|
|
@ -213,8 +220,12 @@ EXTERNAL SOURCES:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
flutter_background_geolocation:
|
flutter_background_geolocation:
|
||||||
:path: ".symlinks/plugins/flutter_background_geolocation/ios"
|
:path: ".symlinks/plugins/flutter_background_geolocation/ios"
|
||||||
|
flutter_secure_storage:
|
||||||
|
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||||
mobile_scanner:
|
mobile_scanner:
|
||||||
:path: ".symlinks/plugins/mobile_scanner/darwin"
|
:path: ".symlinks/plugins/mobile_scanner/darwin"
|
||||||
|
path_provider_foundation:
|
||||||
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
quick_actions_ios:
|
quick_actions_ios:
|
||||||
:path: ".symlinks/plugins/quick_actions_ios/ios"
|
:path: ".symlinks/plugins/quick_actions_ios/ios"
|
||||||
rate_my_app:
|
rate_my_app:
|
||||||
|
|
@ -241,11 +252,13 @@ SPEC CHECKSUMS:
|
||||||
FirebaseSessions: eaa8ec037e7793769defe4201c20bd4d976f9677
|
FirebaseSessions: eaa8ec037e7793769defe4201c20bd4d976f9677
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_background_geolocation: defe705fe7c50b1be772e0d298ed3765fa17c022
|
flutter_background_geolocation: defe705fe7c50b1be772e0d298ed3765fa17c022
|
||||||
|
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
|
||||||
GoogleAppMeasurement: 0dfca1a4b534d123de3945e28f77869d10d0d600
|
GoogleAppMeasurement: 0dfca1a4b534d123de3945e28f77869d10d0d600
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
|
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
|
||||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||||
|
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
||||||
quick_actions_ios: 4b07fb49d8d8f3518d7565fbb7a91014067a7d82
|
quick_actions_ios: 4b07fb49d8d8f3518d7565fbb7a91014067a7d82
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@
|
||||||
"trackingLabel": "Continuous tracking",
|
"trackingLabel": "Continuous tracking",
|
||||||
"motionLabel": "Active movement",
|
"motionLabel": "Active movement",
|
||||||
"advancedLabel": "Advanced settings",
|
"advancedLabel": "Advanced settings",
|
||||||
|
"passwordLabel": "Password",
|
||||||
"optimizationMessage": "To ensure reliable tracking, please disable battery optimization for this app.",
|
"optimizationMessage": "To ensure reliable tracking, please disable battery optimization for this app.",
|
||||||
|
"passwordError": "Wrong password",
|
||||||
"startAction": "Start service",
|
"startAction": "Start service",
|
||||||
"stopAction": "Stop service",
|
"stopAction": "Stop service",
|
||||||
"sosAction": "Send SOS"
|
"sosAction": "Send SOS"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:developer' as developer;
|
import 'dart:developer' as developer;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:traccar_client/password_service.dart';
|
||||||
import 'package:traccar_client/preferences.dart';
|
import 'package:traccar_client/preferences.dart';
|
||||||
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
|
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
|
||||||
|
|
||||||
|
|
@ -93,12 +94,14 @@ class _MainScreenState extends State<MainScreen> {
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
title: Text(AppLocalizations.of(context)!.trackingLabel),
|
title: Text(AppLocalizations.of(context)!.trackingLabel),
|
||||||
value: trackingEnabled,
|
value: trackingEnabled,
|
||||||
onChanged: (bool value) {
|
onChanged: (bool value) async {
|
||||||
if (value) {
|
if (await PasswordService.authenticate(context) && mounted) {
|
||||||
bg.BackgroundGeolocation.start();
|
if (value) {
|
||||||
_checkBatteryOptimizations(context);
|
bg.BackgroundGeolocation.start();
|
||||||
} else {
|
_checkBatteryOptimizations(context);
|
||||||
bg.BackgroundGeolocation.stop();
|
} else {
|
||||||
|
bg.BackgroundGeolocation.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -166,10 +169,12 @@ class _MainScreenState extends State<MainScreen> {
|
||||||
children: [
|
children: [
|
||||||
FilledButton.tonal(
|
FilledButton.tonal(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await Navigator.push(context, MaterialPageRoute(builder: (_) => const SettingsScreen()));
|
if (await PasswordService.authenticate(context) && mounted) {
|
||||||
setState(() {
|
await Navigator.push(context, MaterialPageRoute(builder: (_) => const SettingsScreen()));
|
||||||
stopDetection = Preferences.instance.getBool(Preferences.stopDetection);
|
setState(() {
|
||||||
});
|
stopDetection = Preferences.instance.getBool(Preferences.stopDetection);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Text(AppLocalizations.of(context)!.settingsButton),
|
child: Text(AppLocalizations.of(context)!.settingsButton),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
57
lib/password_service.dart
Normal file
57
lib/password_service.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
|
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:traccar_client/qr_code_screen.dart';
|
||||||
import 'package:wakelock_partial_android/wakelock_partial_android.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) {
|
Widget _buildListTile(String title, String key, bool isInt) {
|
||||||
String? value;
|
String? value;
|
||||||
if (isInt) {
|
if (isInt) {
|
||||||
|
|
@ -201,6 +229,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (advanced)
|
||||||
|
ListTile(
|
||||||
|
title: Text(AppLocalizations.of(context)!.passwordLabel),
|
||||||
|
onTap: _changePassword,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
88
pubspec.lock
88
pubspec.lock
|
|
@ -203,6 +203,54 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.1"
|
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:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
@ -221,6 +269,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.20.2"
|
version: "0.20.2"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.7"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -293,6 +349,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
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:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -514,6 +594,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.14.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ dependencies:
|
||||||
shared_preferences_android: ^2.4.10
|
shared_preferences_android: ^2.4.10
|
||||||
wakelock_partial_android: ^1.0.0
|
wakelock_partial_android: ^1.0.0
|
||||||
mobile_scanner: ^7.0.1
|
mobile_scanner: ^7.0.1
|
||||||
|
flutter_secure_storage: ^9.2.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue