| 1 | import 'dart:io'; |
| 2 | |
| 3 | import 'package:cake_wallet/core/auth_service.dart'; |
| 4 | import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; |
| 5 | import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_selector.dart'; |
| 6 | import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart'; |
| 7 | import 'package:cake_wallet/entities/pin_code_required_duration.dart'; |
| 8 | import 'package:cake_wallet/new-ui/widgets/modal_header.dart'; |
| 9 | import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; |
| 10 | import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; |
| 11 | import 'package:cake_wallet/routes.dart'; |
| 12 | import 'package:cake_wallet/src/screens/base_page.dart'; |
| 13 | import 'package:cake_wallet/generated/i18n.dart'; |
| 14 | import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; |
| 15 | import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; |
| 16 | import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; |
| 17 | import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; |
| 18 | import 'package:cake_wallet/src/widgets/picker.dart'; |
| 19 | import 'package:cake_wallet/utils/device_info.dart'; |
| 20 | import 'package:cake_wallet/utils/feature_flag.dart'; |
| 21 | import 'package:cake_wallet/utils/show_pop_up.dart'; |
| 22 | import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; |
| 23 | import 'package:flutter/material.dart'; |
| 24 | import 'package:flutter_mobx/flutter_mobx.dart'; |
| 25 | |
| 26 | class SecurityBackupPage extends BasePage { |
| 27 | SecurityBackupPage(this._securitySettingsViewModel, this._authService, |
| 28 | [this._isHardwareWallet = false]); |
| 29 | |
| 30 | final AuthService _authService; |
| 31 | |
| 32 | @override |
| 33 | bool get hideAppBar => true; |
| 34 | |
| 35 | @override |
| 36 | String get title => S.current.security_and_backup; |
| 37 | |
| 38 | final SecuritySettingsViewModel _securitySettingsViewModel; |
| 39 | |
| 40 | final bool _isHardwareWallet; |
| 41 | |
| 42 | @override |
| 43 | Widget body(BuildContext context) { |
| 44 | return ModalPageWrapper( |
| 45 | topBar: ModalTopBar( |
| 46 | title: "", |
| 47 | leadingIcon: Icon(Icons.arrow_back_ios_new), |
| 48 | leadingSemanticLabel: S.of(context).seed_alert_back, |
| 49 | onLeadingPressed: () => Navigator.of(context).pop()), |
| 50 | header: ModalHeader( |
| 51 | iconPath: "assets/new-ui/settings_row_icons/security.svg", |
| 52 | message: S.of(context).privacy_and_security_desc, |
| 53 | title: S.of(context).security), |
| 54 | content: Column( |
| 55 | spacing: 16, |
| 56 | mainAxisSize: MainAxisSize.min, |
| 57 | children: [ |
| 58 | Observer( |
| 59 | builder: (_) => NewListSections(sections: { |
| 60 | "": [ |
| 61 | if (DeviceInfo.instance.isMobile || Platform.isMacOS || Platform.isLinux) |
| 62 | ListItemToggle( |
| 63 | keyValue: "security_backup_page_allow_biometrics_button_key", |
| 64 | label: S.current.settings_allow_biometrical_authentication, |
| 65 | value: _securitySettingsViewModel.allowBiometricalAuthentication, |
| 66 | onChanged: (bool value) { |
| 67 | if (value) { |
| 68 | _authService.authenticateAction( |
| 69 | context, |
| 70 | onAuthSuccess: (isAuthenticatedSuccessfully) async { |
| 71 | if (isAuthenticatedSuccessfully) { |
| 72 | if (await _securitySettingsViewModel.biometricAuthenticated()) { |
| 73 | _securitySettingsViewModel.setAllowBiometricalAuthentication( |
| 74 | isAuthenticatedSuccessfully); |
| 75 | } |
| 76 | } else { |
| 77 | _securitySettingsViewModel |
| 78 | .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); |
| 79 | } |
| 80 | }, |
| 81 | conditionToDetermineIfToUse2FA: _securitySettingsViewModel |
| 82 | .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, |
| 83 | ); |
| 84 | } else { |
| 85 | _securitySettingsViewModel.setAllowBiometricalAuthentication(value); |
| 86 | } |
| 87 | }), |
| 88 | if (DeviceInfo.instance.isMobile) |
| 89 | ListItemToggle( |
| 90 | keyValue: "display_settings_prevent_screen_capture", |
| 91 | label: S.of(context).prevent_screenshots, |
| 92 | value: _securitySettingsViewModel.isAppSecure, |
| 93 | onChanged: (val) { |
| 94 | _securitySettingsViewModel.setIsAppSecure(val); |
| 95 | }), |
| 96 | if (FeatureFlag.duressPinEnabled) |
| 97 | ListItemToggle( |
| 98 | keyValue: "security_backup_page_duress_pin_button_key", |
| 99 | label: "Duress PIN", |
| 100 | value: _securitySettingsViewModel.enableDuressPin, |
| 101 | onChanged: (bool value) { |
| 102 | _authService |
| 103 | .authenticateAction(context, route: Routes.securityBackupDuressPin, |
| 104 | onAuthSuccess: (isAuthenticatedSuccessfully) async { |
| 105 | if (isAuthenticatedSuccessfully) { |
| 106 | if (!value) { |
| 107 | _securitySettingsViewModel.setEnableDuressPin(value); |
| 108 | _securitySettingsViewModel.clearDuressPin(); |
| 109 | return; |
| 110 | } |
| 111 | final res = await _showDuressPinDescription(context); |
| 112 | if (res) { |
| 113 | final confirmation = await _showDuressPinConfirmation(context); |
| 114 | |
| 115 | if (confirmation) { |
| 116 | Navigator.of(context).pushNamed( |
| 117 | Routes.setupDuressPin, |
| 118 | arguments: (PinCodeState<PinCodeWidget> pinCtx, String _) async { |
| 119 | pinCtx.close(); |
| 120 | _securitySettingsViewModel.setEnableDuressPin(true); |
| 121 | }, |
| 122 | ); |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | }, |
| 127 | conditionToDetermineIfToUse2FA: _securitySettingsViewModel |
| 128 | .shouldRequireTOTP2FAForAllSecurityAndBackupSettings); |
| 129 | }), |
| 130 | ListItemSelector( |
| 131 | keyValue: "security_backup_page_require_pin_after_button_key", |
| 132 | label: S.current.require_pin_after, |
| 133 | options: [_securitySettingsViewModel.pinCodeRequiredDuration.toString()], |
| 134 | onTap: () async { |
| 135 | final items = PinCodeRequiredDuration.values; |
| 136 | |
| 137 | final selectedAtIndex = |
| 138 | items.indexOf(_securitySettingsViewModel.pinCodeRequiredDuration); |
| 139 | |
| 140 | await showPopUp<void>( |
| 141 | context: context, |
| 142 | builder: (_) => Picker( |
| 143 | items: items, |
| 144 | selectedAtIndex: selectedAtIndex, |
| 145 | mainAxisAlignment: MainAxisAlignment.start, |
| 146 | onItemSelected: (PinCodeRequiredDuration item) { |
| 147 | _securitySettingsViewModel.setPinCodeRequiredDuration(item); |
| 148 | }, |
| 149 | isSeparated: false, |
| 150 | ), |
| 151 | ); |
| 152 | }), |
| 153 | // if (!_isHardwareWallet) |
| 154 | // ListItemRegularRow( |
| 155 | // keyValue: "security_backup_page_show_keys_button_key", |
| 156 | // label: S.current.show_keys, |
| 157 | // onTap: () { |
| 158 | // _authService.authenticateAction( |
| 159 | // context, |
| 160 | // route: Routes.showKeys, |
| 161 | // conditionToDetermineIfToUse2FA: _securitySettingsViewModel |
| 162 | // .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, |
| 163 | // ); |
| 164 | // }), |
| 165 | ListItemRegularRow( |
| 166 | keyValue: "security_backup_page_change_pin_button_key", |
| 167 | label: S.current.settings_change_pin, |
| 168 | onTap: () { |
| 169 | _authService.authenticateAction( |
| 170 | context, |
| 171 | route: Routes.setupPin, |
| 172 | arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) { |
| 173 | setupPinContext.close(); |
| 174 | }, |
| 175 | conditionToDetermineIfToUse2FA: _securitySettingsViewModel |
| 176 | .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, |
| 177 | ); |
| 178 | }), |
| 179 | ListItemRegularRow( |
| 180 | keyValue: "security_backup_page_totp_2fa_button_key", |
| 181 | label: _securitySettingsViewModel.useTotp2FA |
| 182 | ? S.current.modify_2fa |
| 183 | : S.current.setup_2fa, |
| 184 | onTap: () { |
| 185 | _authService.authenticateAction( |
| 186 | context, |
| 187 | route: _securitySettingsViewModel.useTotp2FA |
| 188 | ? Routes.modify2FAPage |
| 189 | : Routes.setup2faInfoPage, |
| 190 | conditionToDetermineIfToUse2FA: _securitySettingsViewModel |
| 191 | .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, |
| 192 | ); |
| 193 | }) |
| 194 | ] |
| 195 | }), |
| 196 | ), |
| 197 | ], |
| 198 | ), |
| 199 | ); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | Future<bool> _showDuressPinDescription(BuildContext context) async { |
| 204 | final ok = await showPopUp<bool>( |
| 205 | context: context, |
| 206 | builder: (BuildContext context) => AlertWithOneAction( |
| 207 | alertTitle: S.of(context).alert_notice, |
| 208 | alertContent: S.current.duress_pin_description, |
| 209 | buttonText: S.of(context).ok, |
| 210 | buttonAction: () => Navigator.of(context).pop(true))); |
| 211 | return ok ?? false; |
| 212 | } |
| 213 | |
| 214 | Future<bool> _showDuressPinConfirmation(BuildContext context) async { |
| 215 | final ok = await showPopUp<bool>( |
| 216 | context: context, |
| 217 | builder: (BuildContext context) => AlertWithTwoActions( |
| 218 | alertTitle: S.of(context).confirm, |
| 219 | alertContent: S.current.did_you_back_up_seeds, |
| 220 | leftButtonText: S.current.no, |
| 221 | rightButtonText: S.current.yes, |
| 222 | actionLeftButton: () => Navigator.of(context).pop(false), |
| 223 | actionRightButton: () => Navigator.of(context).pop(true))); |
| 224 | return ok ?? false; |
| 225 | } |