import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:login_vault_app/auth_service.dart'; import 'package:login_vault_app/auth_screen.dart'; import 'package:login_vault_app/theme_provider.dart'; import 'package:login_vault_app/backup_service.dart'; /// Settings: change app password, switch theme (dark/system), session timeout, /// fingerprint toggle, and Exit. class SettingsScreen extends StatefulWidget { final AuthService auth; final ThemeProvider theme; const SettingsScreen({super.key, required this.auth, required this.theme}); @override State createState() => _SettingsScreenState(); } class _SettingsScreenState extends State { final _oldCtrl = TextEditingController(); final _newCtrl = TextEditingController(); String? _msg; Color _msgColor = Colors.grey; bool _biometricOn = false; bool _biometricAvailable = false; int _timeout = 3; final List _timeoutOptions = [3, 5, 10, 15, 30, 60, 0]; @override void initState() { super.initState(); _loadSettings(); } Future _loadSettings() async { final on = await widget.auth.isBiometricEnabled(); final avail = await widget.auth.canUseBiometrics(); final t = await widget.auth.getTimeoutMinutes(); if (mounted) { setState(() { _biometricOn = on && avail; _biometricAvailable = avail; _timeout = t; }); } } Future _changePassword() async { final oldP = _oldCtrl.text; final newP = _newCtrl.text; if (newP.isEmpty) { setState(() { _msg = 'Password baru wajib diisi'; _msgColor = Colors.red; }); return; } final stored = await widget.auth.getAppPassword(); if (stored != null && stored != '__google__' && stored != oldP) { setState(() { _msg = 'Password lama salah'; _msgColor = Colors.red; }); return; } await widget.auth.changePassword(newP); setState(() { _msg = 'Password aplikasi berhasil diubah'; _msgColor = Colors.green; }); _oldCtrl.clear(); _newCtrl.clear(); } Future _backup() async { try { final path = await BackupService.exportEncrypted(widget.auth); if (mounted) { setState(() { _msg = 'Backup tersimpan: $path'; _msgColor = Colors.green; }); } } catch (e) { if (mounted) { setState(() { _msg = 'Backup gagal: $e'; _msgColor = Colors.red; }); } } } Future _restore() async { try { final backups = await BackupService.listBackups(); if (backups.isEmpty) { if (mounted) { setState(() { _msg = 'Tidak ada file backup di folder Download/loginvault'; _msgColor = Colors.red; }); } return; } if (!mounted) return; final chosen = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Pilih file backup'), content: SizedBox( width: double.maxFinite, child: ListView.builder( shrinkWrap: true, itemCount: backups.length, itemBuilder: (_, i) => ListTile( title: Text(backups[i].path.split('/').last), onTap: () => Navigator.of(ctx).pop(backups[i].path), ), ), ), ), ); if (chosen == null) return; final count = await BackupService.importEncrypted(widget.auth, chosen); if (mounted) { setState(() { _msg = 'Restore berhasil: $count entri'; _msgColor = Colors.green; }); } } catch (e) { if (mounted) { setState(() { _msg = 'Restore gagal: $e'; _msgColor = Colors.red; }); } } } Future _toggleBiometric(bool value) async { if (value && !_biometricAvailable) { setState(() { _msg = 'Perangkat tidak mendukung biometrik'; _msgColor = Colors.red; }); return; } // When turning ON, ask for confirmation first. If cancelled, keep OFF. if (value) { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Aktifkan Sidik Jari?'), content: const Text( 'Saat login, Anda akan diminta verifikasi sidik jari ' 'untuk masuk tanpa mengisi email & password.'), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(false), child: const Text('Batal'), ), TextButton( onPressed: () => Navigator.of(ctx).pop(true), child: const Text('Aktifkan'), ), ], ), ); if (confirmed != true) { // cancelled -> revert switch to OFF if (mounted) setState(() => _biometricOn = false); return; } } // Settings only sets the flag; actual biometric prompt happens at login. await widget.auth.setBiometricEnabled(value); if (mounted) { setState(() { _biometricOn = value; _msg = value ? 'Sidik jari aktif. Akan diminta saat login.' : 'Sidik jari nonaktif.'; _msgColor = value ? Colors.green : Colors.grey; }); } } Future _setTimeout(int? value) async { if (value == null) return; await widget.auth.setTimeoutMinutes(value); if (mounted) setState(() => _timeout = value); widget.auth.openSession(); // activity } String _timeoutLabel(int m) => m == 0 ? 'Selamanya' : '$m menit'; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Pengaturan')), body: ListView( padding: const EdgeInsets.all(16), children: [ const Text('Tema', style: TextStyle(fontWeight: FontWeight.bold)), ListTile( title: const Text('Gelap (Dark)'), leading: const Icon(Icons.dark_mode), trailing: Radio( value: ThemeMode.dark, groupValue: widget.theme.mode, onChanged: (m) => widget.theme.setMode(m!), ), onTap: () => widget.theme.setMode(ThemeMode.dark), ), ListTile( title: const Text('Sistem (System)'), leading: const Icon(Icons.brightness_auto), trailing: Radio( value: ThemeMode.system, groupValue: widget.theme.mode, onChanged: (m) => widget.theme.setMode(m!), ), onTap: () => widget.theme.setMode(ThemeMode.system), ), const Divider(), const Text('Session', style: TextStyle(fontWeight: FontWeight.bold)), ListTile( title: const Text('Timeout (tanpa aktivitas)'), subtitle: Text('Logout otomatis setelah: ${_timeoutLabel(_timeout)}'), trailing: PopupMenuButton( initialValue: _timeoutOptions.contains(_timeout) ? _timeout : _timeoutOptions.first, onSelected: _setTimeout, itemBuilder: (ctx) => _timeoutOptions .map((m) => PopupMenuItem(value: m, child: Text(_timeoutLabel(m)))) .toList(), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(_timeoutLabel(_timeout)), const Icon(Icons.arrow_drop_down), ], ), ), ), SwitchListTile( title: const Text('Login dengan sidik jari'), subtitle: Text(_biometricAvailable ? 'Gunakan fingerprint untuk membuka app' : 'Perangkat tidak mendukung biometrik'), value: _biometricOn, onChanged: _toggleBiometric, ), const Divider(), const Text('Ubah Password Aplikasi', style: TextStyle(fontWeight: FontWeight.bold)), TextField( controller: _oldCtrl, obscureText: true, decoration: const InputDecoration(labelText: 'Password lama'), ), TextField( controller: _newCtrl, obscureText: true, decoration: const InputDecoration(labelText: 'Password baru'), ), const SizedBox(height: 8), ElevatedButton( onPressed: _changePassword, child: const Text('Simpan Password'), ), if (_msg != null) Padding( padding: const EdgeInsets.only(top: 8), child: Text(_msg!, style: TextStyle(color: _msgColor)), ), const Divider(), const Text('Backup & Restore', style: TextStyle(fontWeight: FontWeight.bold)), ListTile( leading: const Icon(Icons.backup), title: const Text('Backup ke file (lokal)'), subtitle: const Text( 'Simpan vault terenkripsi ke folder Download. Bisa dipindah ke Drive manual.'), onTap: _backup, ), ListTile( leading: const Icon(Icons.restore), title: const Text('Restore dari file'), subtitle: const Text('Pilih file backup .enc untuk dikembalikan.'), onTap: _restore, ), const Divider(), ListTile( leading: const Icon(Icons.exit_to_app), title: const Text('Keluar (Exit)'), onTap: () async { await widget.auth.closeSession(); // restart to a fresh AuthScreen (re-evaluates registered + // session state) instead of just popping back to Dashboard. if (!mounted) return; Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (_) => AuthScreen(auth: widget.auth), ), (r) => false, ); }, ), ], ), ); } }