LCOV - code coverage report
Current view: top level - lib/pages/home/widgets - security_bio_tab.dart Coverage Total Hit
Test: lcov.info Lines: 0.0 % 61 0
Test Date: 2026-11-14 15:08:11 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2026 The terCAD team. All rights reserved.
       2              : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
       3              : 
       4              : import 'package:app_finance/_classes/controller/encryption_handler.dart';
       5              : import 'package:app_finance/_classes/controller/focus_controller.dart';
       6              : import 'package:app_finance/_classes/herald/app_design.dart';
       7              : import 'package:app_finance/_classes/herald/app_locale.dart';
       8              : import 'package:app_finance/_classes/storage/app_preferences.dart';
       9              : import 'package:app_finance/_configs/theme_helper.dart';
      10              : import 'package:app_finance/design/button/full_sized_button_widget.dart';
      11              : import 'package:app_finance/design/generic/notification_bar.dart';
      12              : import 'package:app_finance/design/wrapper/input_wrapper.dart';
      13              : import 'package:app_finance/design/wrapper/single_scroll_wrapper.dart';
      14              : import 'package:app_finance/pages/_interfaces/abstract_page_state.dart';
      15              : import 'package:material_ui/material_ui.dart';
      16              : import 'package:local_auth/local_auth.dart';
      17              : 
      18              : class SecurityBioTab extends StatefulWidget {
      19              :   final VoidCallback onSuccess;
      20              : 
      21            0 :   const SecurityBioTab({super.key, required this.onSuccess});
      22              : 
      23            0 :   @override
      24            0 :   SecurityBioTabState createState() => SecurityBioTabState();
      25              : }
      26              : 
      27              : class SecurityBioTabState extends AbstractPageState<SecurityBioTab> {
      28              :   late FocusController focus;
      29              :   late TextEditingController password;
      30              :   final bio = LocalAuthentication();
      31              :   bool _isAuthenticating = false;
      32              : 
      33            0 :   @override
      34              :   void initState() {
      35            0 :     focus = FocusController();
      36            0 :     password = TextEditingController();
      37            0 :     super.initState();
      38            0 :     WidgetsBinding.instance.addPostFrameCallback((_) {
      39            0 :       if (mounted) {
      40            0 :         checkBio(context);
      41              :       }
      42              :     });
      43              :   }
      44              : 
      45            0 :   @override
      46              :   void dispose() {
      47            0 :     focus.dispose();
      48            0 :     password.dispose();
      49            0 :     super.dispose();
      50              :   }
      51              : 
      52            0 :   @override
      53              :   Drawer? buildDrawer() => null;
      54              : 
      55            0 :   @override
      56            0 :   List<Widget> getBarActions(NavigatorState nav) => [];
      57              : 
      58            0 :   @override
      59              :   Widget? getBarLeading(NavigatorState nav) => null;
      60              : 
      61            0 :   @override
      62            0 :   String getTitle() => AppLocale.labels.securityHeadline;
      63              : 
      64            0 :   @override
      65            0 :   String getButtonName() => AppLocale.labels.btnConfirm;
      66              : 
      67            0 :   @override
      68            0 :   Widget buildButton(BuildContext context, BoxConstraints constraints) => FullSizedButtonWidget(
      69              :         constraints: constraints,
      70            0 :         controller: focus,
      71            0 :         onPressed: () => triggerActionButton(),
      72            0 :         title: getButtonName(),
      73              :         icon: Icons.password,
      74              :       );
      75              : 
      76            0 :   void triggerActionButton() {
      77            0 :     if (password.text.isEmpty) {
      78            0 :       NotificationBar.showSnackBar(context, AppLocale.labels.securePasswordEmpty, true);
      79              :       return;
      80              :     }
      81            0 :     final key = AppPreferences.get(AppPreferences.prefRecoveryKey) ?? '';
      82            0 :     if (EncryptionHandler.getHashString(password.text) != key) {
      83            0 :       NotificationBar.showSnackBar(context, AppLocale.labels.securePasswordNotMatch, true);
      84              :       return;
      85              :     }
      86            0 :     widget.onSuccess();
      87              :   }
      88              : 
      89            0 :   Future<void> checkBio(BuildContext context) async {
      90              :     bool canAuthenticate = false;
      91              :     try {
      92            0 :       final canCheckBiometrics = await bio.canCheckBiometrics;
      93            0 :       final isDeviceSupported = await bio.isDeviceSupported();
      94              :       canAuthenticate = canCheckBiometrics && isDeviceSupported;
      95            0 :     } on Exception catch (_) {
      96              :       canAuthenticate = false;
      97              :     }
      98            0 :     if (canAuthenticate && !_isAuthenticating) {
      99            0 :       _isAuthenticating = true;
     100            0 :       bio
     101            0 :           .authenticate(
     102            0 :         localizedReason: AppLocale.labels.secureBioPromptReason,
     103              :         options: const AuthenticationOptions(
     104              :           biometricOnly: true,
     105              :           stickyAuth: true,
     106              :         ),
     107              :       )
     108            0 :           .then((authenticated) {
     109              :         if (authenticated) {
     110            0 :           widget.onSuccess();
     111              :         }
     112            0 :       }).catchError((error) {
     113            0 :         if (context.mounted) {
     114            0 :           NotificationBar.showSnackBar(context, AppLocale.labels.secureBioNotMatch, true);
     115              :         }
     116            0 :       }).whenComplete(() => _isAuthenticating = false);
     117              :     }
     118              :   }
     119              : 
     120            0 :   @override
     121              :   Widget buildContent(BuildContext context, BoxConstraints constraints) {
     122            0 :     final indent = ThemeHelper.getIndent();
     123            0 :     return SingleScrollWrapper(
     124            0 :       controller: focus,
     125            0 :       child: Padding(
     126            0 :         padding: EdgeInsets.all(indent),
     127            0 :         child: Column(
     128            0 :           crossAxisAlignment: AppDesign.getAlignment(),
     129            0 :           children: [
     130            0 :             InputWrapper.text(
     131            0 :               title: AppLocale.labels.securePassword,
     132            0 :               controller: password,
     133              :             ),
     134              :           ],
     135              :         ),
     136              :       ),
     137              :     );
     138              :   }
     139              : }
        

Generated by: LCOV version 2.0-1