diff --git a/mobile/lib/core/configuration.dart b/mobile/lib/core/configuration.dart index 571a9d0f19..f9ccd55868 100644 --- a/mobile/lib/core/configuration.dart +++ b/mobile/lib/core/configuration.dart @@ -34,7 +34,7 @@ import 'package:photos/services/search_service.dart'; import 'package:photos/services/sync_service.dart'; import 'package:photos/utils/crypto_util.dart'; import 'package:photos/utils/file_uploader.dart'; -import "package:photos/utils/lockscreen_setting.dart"; +import "package:photos/utils/lock_screen_settings.dart"; import 'package:photos/utils/validator_util.dart'; import "package:photos/utils/wakelock_util.dart"; import 'package:shared_preferences/shared_preferences.dart'; @@ -73,9 +73,6 @@ class Configuration { "has_selected_all_folders_for_backup"; static const anonymousUserIDKey = "anonymous_user_id"; static const endPointKey = "endpoint"; - static const password = "user_pass"; - static const pin = "user_pin"; - static const saltKey = "user_salt"; static final _logger = Logger("Configuration"); String? _cachedToken; @@ -86,7 +83,6 @@ class Configuration { late FlutterSecureStorage _secureStorage; late String _tempDocumentsDirPath; late String _thumbnailCacheDirectory; - final LockscreenSetting _lockscreenSetting = LockscreenSetting.instance; // 6th July 22: Remove this after 3 months. Hopefully, active users // will migrate to newer version of the app, where shared media is stored // on appSupport directory which OS won't clean up automatically @@ -624,8 +620,8 @@ class Configuration { } Future shouldShowLockScreen() async { - final bool isPin = await _lockscreenSetting.isPinSet(); - final bool isPass = await _lockscreenSetting.isPasswordSet(); + final bool isPin = await LockScreenSettings.instance.isPinSet(); + final bool isPass = await LockScreenSettings.instance.isPasswordSet(); return isPin || isPass || shouldShowSystemLockScreen(); } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index ec49a844c5..7775b00aaa 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -9,7 +9,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import "package:flutter/rendering.dart"; import "package:flutter_displaymode/flutter_displaymode.dart"; -import "package:flutter_secure_storage/flutter_secure_storage.dart"; import 'package:logging/logging.dart'; import "package:media_kit/media_kit.dart"; import 'package:path_provider/path_provider.dart'; @@ -54,7 +53,7 @@ import 'package:photos/utils/crypto_util.dart'; import "package:photos/utils/email_util.dart"; import 'package:photos/utils/file_uploader.dart'; import 'package:photos/utils/local_settings.dart'; -import "package:photos/utils/lockscreen_setting.dart"; +import "package:photos/utils/lock_screen_settings.dart"; import 'package:shared_preferences/shared_preferences.dart'; final _logger = Logger("main"); @@ -197,7 +196,6 @@ Future _init(bool isBackground, {String via = ''}) async { _isProcessRunning = true; _logger.info("Initializing... inBG =$isBackground via: $via"); final SharedPreferences preferences = await SharedPreferences.getInstance(); - const secureStorage = FlutterSecureStorage(); await _logFGHeartBeatInfo(); _logger.info("_logFGHeartBeatInfo done"); unawaited(_scheduleHeartBeat(preferences, isBackground)); @@ -212,7 +210,7 @@ Future _init(bool isBackground, {String via = ''}) async { CryptoUtil.init(); _logger.info("Lockscreen init"); - LockscreenSetting.instance.init(secureStorage, preferences); + LockScreenSettings.instance.init(preferences); _logger.info("Configuration init"); await Configuration.instance.init(); diff --git a/mobile/lib/services/local_authentication_service.dart b/mobile/lib/services/local_authentication_service.dart index 73cc0e3701..a989da5124 100644 --- a/mobile/lib/services/local_authentication_service.dart +++ b/mobile/lib/services/local_authentication_service.dart @@ -3,8 +3,8 @@ import "dart:async"; import 'package:flutter/material.dart'; import 'package:local_auth/local_auth.dart'; import 'package:photos/core/configuration.dart'; -import "package:photos/ui/settings/lockscreen/lockscreen_password.dart"; -import "package:photos/ui/settings/lockscreen/lockscreen_pin.dart"; +import "package:photos/ui/settings/lock_screen/lock_screen_password.dart"; +import "package:photos/ui/settings/lock_screen/lock_screen_pin.dart"; import 'package:photos/ui/tools/app_lock.dart'; import 'package:photos/utils/auth_util.dart'; import 'package:photos/utils/dialog_util.dart'; @@ -39,7 +39,7 @@ class LocalAuthenticationService { BuildContext context, String? savedPin, String? savedPassword, { - bool isLockscreenAuth = false, + bool isOnOpeningApp = false, }) async { if (savedPassword != null) { final result = await Navigator.of(context).push( @@ -47,7 +47,7 @@ class LocalAuthenticationService { builder: (BuildContext context) { return LockScreenPassword( isAuthenticating: true, - isLockscreenAuth: isLockscreenAuth, + isOnOpeningApp: isOnOpeningApp, authPass: savedPassword, ); }, @@ -63,7 +63,7 @@ class LocalAuthenticationService { builder: (BuildContext context) { return LockScreenPin( isAuthenticating: true, - isLockscreenAuth: isLockscreenAuth, + isOnOpeningApp: isOnOpeningApp, authPin: savedPin, ); }, diff --git a/mobile/lib/ui/settings/lock_screen/custom_pin_keypad.dart b/mobile/lib/ui/settings/lock_screen/custom_pin_keypad.dart new file mode 100644 index 0000000000..d388a2f906 --- /dev/null +++ b/mobile/lib/ui/settings/lock_screen/custom_pin_keypad.dart @@ -0,0 +1,196 @@ +import "package:flutter/material.dart"; +import "package:photos/theme/ente_theme.dart"; + +class CustomPinKeypad extends StatelessWidget { + final TextEditingController controller; + const CustomPinKeypad({required this.controller, super.key}); + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Container( + padding: const EdgeInsets.all(2), + color: getEnteColorScheme(context).strokeFainter, + child: Column( + children: [ + Row( + children: [ + _Button( + text: '', + number: '1', + onTap: () { + _onKeyTap('1'); + }, + ), + _Button( + text: "ABC", + number: '2', + onTap: () { + _onKeyTap('2'); + }, + ), + _Button( + text: "DEF", + number: '3', + onTap: () { + _onKeyTap('3'); + }, + ), + ], + ), + Row( + children: [ + _Button( + number: '4', + text: "GHI", + onTap: () { + _onKeyTap('4'); + }, + ), + _Button( + number: '5', + text: 'JKL', + onTap: () { + _onKeyTap('5'); + }, + ), + _Button( + number: '6', + text: 'MNO', + onTap: () { + _onKeyTap('6'); + }, + ), + ], + ), + Row( + children: [ + _Button( + number: '7', + text: 'PQRS', + onTap: () { + _onKeyTap('7'); + }, + ), + _Button( + number: '8', + text: 'TUV', + onTap: () { + _onKeyTap('8'); + }, + ), + _Button( + number: '9', + text: 'WXYZ', + onTap: () { + _onKeyTap('9'); + }, + ), + ], + ), + Row( + children: [ + const _Button( + number: '', + text: '', + muteButton: true, + onTap: null, + ), + _Button( + number: '0', + text: '', + onTap: () { + _onKeyTap('0'); + }, + ), + _Button( + number: '', + text: '', + icon: const Icon(Icons.backspace_outlined), + onTap: () { + _onBackspace(); + }, + ), + ], + ), + ], + ), + ), + ); + } + + void _onKeyTap(String number) { + controller.text += number; + return; + } + + void _onBackspace() { + if (controller.text.isNotEmpty) { + controller.text = + controller.text.substring(0, controller.text.length - 1); + } + return; + } +} + +class _Button extends StatelessWidget { + final String number; + final String text; + final VoidCallback? onTap; + final bool muteButton; + final Widget? icon; + const _Button({ + required this.number, + required this.text, + this.muteButton = false, + required this.onTap, + this.icon, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + return Expanded( + child: GestureDetector( + onTap: onTap, + child: Container( + margin: const EdgeInsets.all(4), + decoration: BoxDecoration( + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(6), + color: muteButton + ? colorScheme.fillFaintPressed + : icon == null + ? colorScheme.backgroundElevated2 + : null, + ), + child: Center( + child: muteButton + ? const SizedBox.shrink() + : icon != null + ? Container( + child: icon, + ) + : Container( + padding: const EdgeInsets.all(4), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + number, + style: textTheme.h3, + ), + Text( + text, + style: textTheme.tinyBold, + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/mobile/lib/ui/settings/lockscreen/lockscreen_confirm_password.dart b/mobile/lib/ui/settings/lock_screen/lock_screen_confirm_password.dart similarity index 97% rename from mobile/lib/ui/settings/lockscreen/lockscreen_confirm_password.dart rename to mobile/lib/ui/settings/lock_screen/lock_screen_confirm_password.dart index dbf07f5cf6..e83446ba87 100644 --- a/mobile/lib/ui/settings/lockscreen/lockscreen_confirm_password.dart +++ b/mobile/lib/ui/settings/lock_screen/lock_screen_confirm_password.dart @@ -5,7 +5,7 @@ import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/common/dynamic_fab.dart"; import "package:photos/ui/components/buttons/icon_button_widget.dart"; import "package:photos/ui/components/text_input_widget.dart"; -import "package:photos/utils/lockscreen_setting.dart"; +import "package:photos/utils/lock_screen_settings.dart"; class LockScreenConfirmPassword extends StatefulWidget { const LockScreenConfirmPassword({ @@ -23,7 +23,7 @@ class _LockScreenConfirmPasswordState extends State { /// _confirmPasswordController is disposed by the [TextInputWidget] final _confirmPasswordController = TextEditingController(text: null); - final LockscreenSetting _lockscreenSetting = LockscreenSetting.instance; + final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; final _focusNode = FocusNode(); final _isFormValid = ValueNotifier(false); final _submitNotifier = ValueNotifier(false); diff --git a/mobile/lib/ui/settings/lockscreen/lockscreen_confirm_pin.dart b/mobile/lib/ui/settings/lock_screen/lock_screen_confirm_pin.dart similarity index 53% rename from mobile/lib/ui/settings/lockscreen/lockscreen_confirm_pin.dart rename to mobile/lib/ui/settings/lock_screen/lock_screen_confirm_pin.dart index 412cfcce84..4681273996 100644 --- a/mobile/lib/ui/settings/lockscreen/lockscreen_confirm_pin.dart +++ b/mobile/lib/ui/settings/lock_screen/lock_screen_confirm_pin.dart @@ -1,10 +1,9 @@ import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:photos/theme/colors.dart"; import "package:photos/theme/ente_theme.dart"; -import "package:photos/theme/text_style.dart"; import "package:photos/ui/components/buttons/icon_button_widget.dart"; -import "package:photos/utils/lockscreen_setting.dart"; +import "package:photos/ui/settings/lock_screen/custom_pin_keypad.dart"; +import "package:photos/utils/lock_screen_settings.dart"; import "package:pinput/pinput.dart"; class LockScreenConfirmPin extends StatefulWidget { @@ -17,7 +16,8 @@ class LockScreenConfirmPin extends StatefulWidget { class _LockScreenConfirmPinState extends State { final _confirmPinController = TextEditingController(text: null); bool isConfirmPinValid = false; - final LockscreenSetting _lockscreenSetting = LockscreenSetting.instance; + + final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; final _pinPutDecoration = PinTheme( height: 48, width: 48, @@ -34,19 +34,6 @@ class _LockScreenConfirmPinState extends State { _confirmPinController.dispose(); } - void _onKeyTap(String number) { - _confirmPinController.text += number; - return; - } - - void _onBackspace() { - if (_confirmPinController.text.isNotEmpty) { - _confirmPinController.text = _confirmPinController.text - .substring(0, _confirmPinController.text.length - 1); - } - return; - } - Future _confirmPinMatch() async { if (widget.pin == _confirmPinController.text) { await _lockscreenSetting.setPin(_confirmPinController.text); @@ -214,197 +201,9 @@ class _LockScreenConfirmPinState extends State { isPortrait ? const Spacer() : const Padding(padding: EdgeInsets.all(12)), - customKeyPad(colorTheme, textTheme), + CustomPinKeypad(controller: _confirmPinController), ], ), ); } - - Widget customKeyPad(EnteColorScheme colorTheme, EnteTextTheme textTheme) { - return SafeArea( - child: Container( - padding: const EdgeInsets.all(2), - color: colorTheme.strokeFainter, - child: Column( - children: [ - Row( - children: [ - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - text: '', - number: '1', - onTap: () { - _onKeyTap('1'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - text: "ABC", - number: '2', - onTap: () { - _onKeyTap('2'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - text: "DEF", - number: '3', - onTap: () { - _onKeyTap('3'); - }, - ), - ], - ), - Row( - children: [ - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '4', - text: "GHI", - onTap: () { - _onKeyTap('4'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '5', - text: 'JKL', - onTap: () { - _onKeyTap('5'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '6', - text: 'MNO', - onTap: () { - _onKeyTap('6'); - }, - ), - ], - ), - Row( - children: [ - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '7', - text: 'PQRS', - onTap: () { - _onKeyTap('7'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '8', - text: 'TUV', - onTap: () { - _onKeyTap('8'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '9', - text: 'WXYZ', - onTap: () { - _onKeyTap('9'); - }, - ), - ], - ), - Row( - children: [ - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '', - text: '', - muteButton: true, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '0', - text: '', - onTap: () { - _onKeyTap('0'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '', - text: '', - icons: const Icon(Icons.backspace_outlined), - onTap: () { - _onBackspace(); - }, - ), - ], - ), - ], - ), - ), - ); - } - - Widget buttonWidget({ - colorTheme, - textTheme, - text, - number, - muteButton = false, - icons, - onTap, - }) { - return Expanded( - child: GestureDetector( - onTap: onTap, - child: Container( - margin: const EdgeInsets.all(4), - decoration: BoxDecoration( - shape: BoxShape.rectangle, - borderRadius: BorderRadius.circular(6), - color: muteButton - ? colorTheme.fillFaintPressed - : icons == null - ? colorTheme.backgroundElevated2 - : null, - ), - child: Center( - child: muteButton - ? Container() - : icons != null - ? Container( - child: icons, - ) - : Container( - padding: const EdgeInsets.all(4), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - number, - style: textTheme.h3, - ), - Text( - text, - style: textTheme.small, - ), - ], - ), - ), - ), - ), - ), - ); - } } diff --git a/mobile/lib/ui/settings/lockscreen/lock_screen_option.dart b/mobile/lib/ui/settings/lock_screen/lock_screen_options.dart similarity index 94% rename from mobile/lib/ui/settings/lockscreen/lock_screen_option.dart rename to mobile/lib/ui/settings/lock_screen/lock_screen_options.dart index 57e5529297..a274944996 100644 --- a/mobile/lib/ui/settings/lockscreen/lock_screen_option.dart +++ b/mobile/lib/ui/settings/lock_screen/lock_screen_options.dart @@ -7,21 +7,21 @@ import "package:photos/ui/components/menu_item_widget/menu_item_widget.dart"; import "package:photos/ui/components/title_bar_title_widget.dart"; import "package:photos/ui/components/title_bar_widget.dart"; import "package:photos/ui/components/toggle_switch_widget.dart"; -import "package:photos/ui/settings/lockscreen/lockscreen_password.dart"; -import "package:photos/ui/settings/lockscreen/lockscreen_pin.dart"; +import "package:photos/ui/settings/lock_screen/lock_screen_password.dart"; +import "package:photos/ui/settings/lock_screen/lock_screen_pin.dart"; import "package:photos/ui/tools/app_lock.dart"; -import "package:photos/utils/lockscreen_setting.dart"; +import "package:photos/utils/lock_screen_settings.dart"; -class LockScreenOption extends StatefulWidget { - const LockScreenOption({super.key}); +class LockScreenOptions extends StatefulWidget { + const LockScreenOptions({super.key}); @override - State createState() => _LockScreenOptionState(); + State createState() => _LockScreenOptionsState(); } -class _LockScreenOptionState extends State { +class _LockScreenOptionsState extends State { final Configuration _configuration = Configuration.instance; - final LockscreenSetting _lockscreenSetting = LockscreenSetting.instance; + final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; late bool appLock; bool isPinEnabled = false; bool isPasswordEnabled = false; diff --git a/mobile/lib/ui/settings/lockscreen/lockscreen_password.dart b/mobile/lib/ui/settings/lock_screen/lock_screen_password.dart similarity index 88% rename from mobile/lib/ui/settings/lockscreen/lockscreen_password.dart rename to mobile/lib/ui/settings/lock_screen/lock_screen_password.dart index b3a60d32f5..21fdf2fc71 100644 --- a/mobile/lib/ui/settings/lockscreen/lockscreen_password.dart +++ b/mobile/lib/ui/settings/lock_screen/lock_screen_password.dart @@ -8,23 +8,22 @@ import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/common/dynamic_fab.dart"; import "package:photos/ui/components/buttons/icon_button_widget.dart"; import "package:photos/ui/components/text_input_widget.dart"; -import "package:photos/ui/settings/lockscreen/lock_screen_option.dart"; -import "package:photos/ui/settings/lockscreen/lockscreen_confirm_password.dart"; +import "package:photos/ui/settings/lock_screen/lock_screen_confirm_password.dart"; +import "package:photos/ui/settings/lock_screen/lock_screen_options.dart"; import "package:photos/utils/crypto_util.dart"; -import "package:photos/utils/lockscreen_setting.dart"; +import "package:photos/utils/lock_screen_settings.dart"; class LockScreenPassword extends StatefulWidget { const LockScreenPassword({ super.key, this.isAuthenticating = false, - this.isLockscreenAuth = false, + this.isOnOpeningApp = false, this.authPass, }); - /// If [isLockscreenAuth] is true then we are authenticating the user at Lock screen - /// If [isAuthenticating] is true then we are authenticating the user at Setting screen + //Is false when setting a new password final bool isAuthenticating; - final bool isLockscreenAuth; + final bool isOnOpeningApp; final String? authPass; @override State createState() => _LockScreenPasswordState(); @@ -38,8 +37,7 @@ class _LockScreenPasswordState extends State { final _submitNotifier = ValueNotifier(false); int invalidAttemptsCount = 0; - final LockscreenSetting _lockscreenSetting = LockscreenSetting.instance; - late String enteredHashedPassword; + final _lockscreenSetting = LockScreenSettings.instance; @override void initState() { super.initState(); @@ -57,57 +55,6 @@ class _LockScreenPasswordState extends State { _isFormValid.dispose(); } - Future confirmPasswordAuth(String code) async { - final Uint8List? salt = await _lockscreenSetting.getSalt(); - final hash = cryptoPwHash({ - "password": utf8.encode(code), - "salt": salt, - "opsLimit": Sodium.cryptoPwhashOpslimitInteractive, - "memLimit": Sodium.cryptoPwhashMemlimitInteractive, - }); - - enteredHashedPassword = base64Encode(hash); - if (widget.authPass == enteredHashedPassword) { - await _lockscreenSetting.setInvalidAttemptCount(0); - - widget.isLockscreenAuth - ? Navigator.of(context).pop(true) - : Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => const LockScreenOption(), - ), - ); - return true; - } else { - if (widget.isLockscreenAuth) { - invalidAttemptsCount++; - if (invalidAttemptsCount > 4) { - await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount); - Navigator.of(context).pop(false); - } - } - - await HapticFeedback.vibrate(); - throw Exception("Incorrect password"); - } - } - - Future _confirmPassword() async { - if (widget.isAuthenticating) { - await confirmPasswordAuth(_passwordController.text); - return; - } else { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) => LockScreenConfirmPassword( - password: _passwordController.text, - ), - ), - ); - _passwordController.clear(); - } - } - @override Widget build(BuildContext context) { final colorTheme = getEnteColorScheme(context); @@ -239,4 +186,54 @@ class _LockScreenPasswordState extends State { ), ); } + + Future _confirmPasswordAuth(String inputtedPassword) async { + final Uint8List? salt = await _lockscreenSetting.getSalt(); + final hash = cryptoPwHash({ + "password": utf8.encode(inputtedPassword), + "salt": salt, + "opsLimit": Sodium.cryptoPwhashOpslimitInteractive, + "memLimit": Sodium.cryptoPwhashMemlimitInteractive, + }); + + if (widget.authPass == base64Encode(hash)) { + await _lockscreenSetting.setInvalidAttemptCount(0); + + widget.isOnOpeningApp + ? Navigator.of(context).pop(true) + : Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => const LockScreenOptions(), + ), + ); + return true; + } else { + if (widget.isOnOpeningApp) { + invalidAttemptsCount++; + if (invalidAttemptsCount > 4) { + await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount); + Navigator.of(context).pop(false); + } + } + + await HapticFeedback.vibrate(); + throw Exception("Incorrect password"); + } + } + + Future _confirmPassword() async { + if (widget.isAuthenticating) { + await _confirmPasswordAuth(_passwordController.text); + return; + } else { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => LockScreenConfirmPassword( + password: _passwordController.text, + ), + ), + ); + _passwordController.clear(); + } + } } diff --git a/mobile/lib/ui/settings/lockscreen/lockscreen_pin.dart b/mobile/lib/ui/settings/lock_screen/lock_screen_pin.dart similarity index 55% rename from mobile/lib/ui/settings/lockscreen/lockscreen_pin.dart rename to mobile/lib/ui/settings/lock_screen/lock_screen_pin.dart index ee5dc24cd8..899d3f1e6a 100644 --- a/mobile/lib/ui/settings/lockscreen/lockscreen_pin.dart +++ b/mobile/lib/ui/settings/lock_screen/lock_screen_pin.dart @@ -7,24 +7,24 @@ import "package:photos/theme/colors.dart"; import "package:photos/theme/ente_theme.dart"; import "package:photos/theme/text_style.dart"; import "package:photos/ui/components/buttons/icon_button_widget.dart"; -import "package:photos/ui/settings/lockscreen/lock_screen_option.dart"; -import "package:photos/ui/settings/lockscreen/lockscreen_confirm_pin.dart"; +import "package:photos/ui/settings/lock_screen/custom_pin_keypad.dart"; +import "package:photos/ui/settings/lock_screen/lock_screen_confirm_pin.dart"; +import "package:photos/ui/settings/lock_screen/lock_screen_options.dart"; import "package:photos/utils/crypto_util.dart"; -import "package:photos/utils/lockscreen_setting.dart"; +import "package:photos/utils/lock_screen_settings.dart"; import 'package:pinput/pinput.dart'; class LockScreenPin extends StatefulWidget { const LockScreenPin({ super.key, this.isAuthenticating = false, - this.isLockscreenAuth = false, + this.isOnOpeningApp = false, this.authPin, }); - /// If [isLockscreenAuth] is true then we are authenticating the user at the Lock screen - /// If [isAuthenticating] is true then we are authenticating the user at the Setting screen + //Is false when setting a new password final bool isAuthenticating; - final bool isLockscreenAuth; + final bool isOnOpeningApp; final String? authPin; @override State createState() => _LockScreenPinState(); @@ -33,8 +33,7 @@ class LockScreenPin extends StatefulWidget { class _LockScreenPinState extends State { final _pinController = TextEditingController(text: null); - final LockscreenSetting _lockscreenSetting = LockscreenSetting.instance; - late String enteredHashedPin; + final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; bool isPinValid = false; int invalidAttemptsCount = 0; @@ -50,19 +49,6 @@ class _LockScreenPinState extends State { _pinController.dispose(); } - void _onKeyTap(String number) { - _pinController.text += number; - return; - } - - void _onBackspace() { - if (_pinController.text.isNotEmpty) { - _pinController.text = - _pinController.text.substring(0, _pinController.text.length - 1); - } - return; - } - Future confirmPinAuth(String code) async { final Uint8List? salt = await _lockscreenSetting.getSalt(); final hash = cryptoPwHash({ @@ -72,15 +58,14 @@ class _LockScreenPinState extends State { "memLimit": Sodium.cryptoPwhashMemlimitInteractive, }); - enteredHashedPin = base64Encode(hash); - if (widget.authPin == enteredHashedPin) { + if (widget.authPin == base64Encode(hash)) { invalidAttemptsCount = 0; await _lockscreenSetting.setInvalidAttemptCount(0); - widget.isLockscreenAuth + widget.isOnOpeningApp ? Navigator.of(context).pop(true) : Navigator.of(context).pushReplacement( MaterialPageRoute( - builder: (context) => const LockScreenOption(), + builder: (context) => const LockScreenOptions(), ), ); return true; @@ -95,7 +80,7 @@ class _LockScreenPinState extends State { isPinValid = false; }); - if (widget.isLockscreenAuth) { + if (widget.isOnOpeningApp) { invalidAttemptsCount++; if (invalidAttemptsCount > 4) { await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount); @@ -282,197 +267,9 @@ class _LockScreenPinState extends State { isPortrait ? const Spacer() : const Padding(padding: EdgeInsets.all(12)), - customKeyPad(colorTheme, textTheme), + CustomPinKeypad(controller: _pinController), ], ), ); } - - Widget customKeyPad(EnteColorScheme colorTheme, EnteTextTheme textTheme) { - return SafeArea( - child: Container( - padding: const EdgeInsets.all(2), - color: colorTheme.strokeFainter, - child: Column( - children: [ - Row( - children: [ - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - text: '', - number: '1', - onTap: () { - _onKeyTap('1'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - text: "ABC", - number: '2', - onTap: () { - _onKeyTap('2'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - text: "DEF", - number: '3', - onTap: () { - _onKeyTap('3'); - }, - ), - ], - ), - Row( - children: [ - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '4', - text: "GHI", - onTap: () { - _onKeyTap('4'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '5', - text: 'JKL', - onTap: () { - _onKeyTap('5'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '6', - text: 'MNO', - onTap: () { - _onKeyTap('6'); - }, - ), - ], - ), - Row( - children: [ - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '7', - text: 'PQRS', - onTap: () { - _onKeyTap('7'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '8', - text: 'TUV', - onTap: () { - _onKeyTap('8'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '9', - text: 'WXYZ', - onTap: () { - _onKeyTap('9'); - }, - ), - ], - ), - Row( - children: [ - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '', - text: '', - muteButton: true, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '0', - text: '', - onTap: () { - _onKeyTap('0'); - }, - ), - buttonWidget( - colorTheme: colorTheme, - textTheme: textTheme, - number: '', - text: '', - icons: const Icon(Icons.backspace_outlined), - onTap: () { - _onBackspace(); - }, - ), - ], - ), - ], - ), - ), - ); - } - - Widget buttonWidget({ - colorTheme, - textTheme, - text, - number, - muteButton = false, - icons, - onTap, - }) { - return Expanded( - child: GestureDetector( - onTap: onTap, - child: Container( - margin: const EdgeInsets.all(4), - decoration: BoxDecoration( - shape: BoxShape.rectangle, - borderRadius: BorderRadius.circular(6), - color: muteButton - ? colorTheme.fillFaintPressed - : icons == null - ? colorTheme.backgroundElevated2 - : null, - ), - child: Center( - child: muteButton - ? Container() - : icons != null - ? Container( - child: icons, - ) - : Container( - padding: const EdgeInsets.all(4), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - number, - style: textTheme.h3, - ), - Text( - text, - style: textTheme.miniBold, - ), - ], - ), - ), - ), - ), - ), - ); - } } diff --git a/mobile/lib/ui/settings/security_section_widget.dart b/mobile/lib/ui/settings/security_section_widget.dart index a9e89c10c7..490dacb3fc 100644 --- a/mobile/lib/ui/settings/security_section_widget.dart +++ b/mobile/lib/ui/settings/security_section_widget.dart @@ -21,7 +21,7 @@ import 'package:photos/ui/components/expandable_menu_item_widget.dart'; import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/toggle_switch_widget.dart'; import 'package:photos/ui/settings/common_settings.dart'; -import "package:photos/ui/settings/lockscreen/lock_screen_option.dart"; +import "package:photos/ui/settings/lock_screen/lock_screen_options.dart"; import "package:photos/utils/auth_util.dart"; import "package:photos/utils/crypto_util.dart"; import "package:photos/utils/dialog_util.dart"; @@ -154,7 +154,7 @@ class _SecuritySectionWidgetState extends State { await Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { - return const LockScreenOption(); + return const LockScreenOptions(); }, ), ); diff --git a/mobile/lib/ui/tools/lock_screen.dart b/mobile/lib/ui/tools/lock_screen.dart index 046abc3fa7..44e4835eda 100644 --- a/mobile/lib/ui/tools/lock_screen.dart +++ b/mobile/lib/ui/tools/lock_screen.dart @@ -13,7 +13,6 @@ import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/components/buttons/icon_button_widget.dart"; import 'package:photos/ui/tools/app_lock.dart'; import 'package:photos/utils/auth_util.dart'; -import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/lockscreen_setting.dart"; class LockScreen extends StatefulWidget { @@ -34,7 +33,16 @@ class _LockScreenState extends State int lockedTime = 0; int invalidAttemptCount = 0; int remainingTime = 0; + bool showErrorMessage = true; final _lockscreenSetting = LockscreenSetting.instance; + late final AnimationController _controller = AnimationController( + duration: const Duration(milliseconds: 500), + vsync: this, + ); + late final animation = CurvedAnimation( + parent: _controller, + curve: Curves.easeInOut, + ); @override void initState() { @@ -169,7 +177,7 @@ class _LockScreenState extends State if (Platform.isAndroid) { return false; } - final shortestSide = MediaQuery.of(context).size.shortestSide; + final shortestSide = MediaQuery.sizeOf(context).shortestSide; return shortestSide > 600 ? true : false; } @@ -311,7 +319,7 @@ class _LockScreenState extends State : await requestAuthentication( context, context.l10n.authToViewYourMemories, - isLockscreenAuth: true, + isOpeningApp: true, ); _logger.finest("LockScreen Result $result $id"); _isShowingLockScreen = false; diff --git a/mobile/lib/utils/auth_util.dart b/mobile/lib/utils/auth_util.dart index 0fa14db811..9c4e161fbf 100644 --- a/mobile/lib/utils/auth_util.dart +++ b/mobile/lib/utils/auth_util.dart @@ -5,26 +5,25 @@ import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:logging/logging.dart'; import "package:photos/generated/l10n.dart"; import "package:photos/services/local_authentication_service.dart"; -import "package:photos/utils/lockscreen_setting.dart"; +import "package:photos/utils/lock_screen_settings.dart"; Future requestAuthentication( BuildContext context, String reason, { - bool isLockscreenAuth = false, + bool isOpeningApp = false, }) async { Logger("AuthUtil").info("Requesting authentication"); await LocalAuthentication().stopAuthentication(); - final LockscreenSetting lockscreenSetting = LockscreenSetting.instance; - final String? savedPin = await lockscreenSetting.getPin(); - final String? savedPassword = await lockscreenSetting.getPassword(); + final String? savedPin = await LockScreenSettings.instance.getPin(); + final String? savedPassword = await LockScreenSettings.instance.getPassword(); if (savedPassword != null || savedPin != null) { return await LocalAuthenticationService.instance .requestEnteAuthForLockScreen( context, savedPin, savedPassword, - isLockscreenAuth: isLockscreenAuth, + isOnOpeningApp: isOpeningApp, ); } else { return await LocalAuthentication().authenticate( diff --git a/mobile/lib/utils/lockscreen_setting.dart b/mobile/lib/utils/lock_screen_settings.dart similarity index 82% rename from mobile/lib/utils/lockscreen_setting.dart rename to mobile/lib/utils/lock_screen_settings.dart index 988ac3624a..13772d5224 100644 --- a/mobile/lib/utils/lockscreen_setting.dart +++ b/mobile/lib/utils/lock_screen_settings.dart @@ -6,21 +6,21 @@ import "package:flutter_sodium/flutter_sodium.dart"; import "package:photos/utils/crypto_util.dart"; import "package:shared_preferences/shared_preferences.dart"; -class LockscreenSetting { - LockscreenSetting._privateConstructor(); +class LockScreenSettings { + LockScreenSettings._privateConstructor(); - static final LockscreenSetting instance = - LockscreenSetting._privateConstructor(); - static const password = "user_pass"; - static const pin = "user_pin"; - static const saltKey = "user_salt"; - static const keyInvalidAttempts = "invalid_attempts"; - static const lastInvalidAttemptTime = "last_invalid_attempt_time"; + static final LockScreenSettings instance = + LockScreenSettings._privateConstructor(); + static const password = "ls_password"; + static const pin = "ls_pin"; + static const saltKey = "ls_salt"; + static const keyInvalidAttempts = "ls_invalid_attempts"; + static const lastInvalidAttemptTime = "ls_last_invalid_attempt_time"; late FlutterSecureStorage _secureStorage; late SharedPreferences _preferences; - void init(FlutterSecureStorage secureStorage, SharedPreferences prefs) async { - _secureStorage = secureStorage; + void init(SharedPreferences prefs) async { + _secureStorage = const FlutterSecureStorage(); _preferences = prefs; } @@ -40,14 +40,14 @@ class LockscreenSetting { await _preferences.setInt(keyInvalidAttempts, count); } - static Uint8List generateSalt() { + static Uint8List _generateSalt() { return Sodium.randombytesBuf(Sodium.cryptoPwhashSaltbytes); } Future setPin(String userPin) async { await _secureStorage.delete(key: saltKey); - final salt = generateSalt(); + final salt = _generateSalt(); final hash = cryptoPwHash({ "password": utf8.encode(userPin), "salt": salt, @@ -78,7 +78,7 @@ class LockscreenSetting { Future setPassword(String pass) async { await _secureStorage.delete(key: saltKey); - final salt = generateSalt(); + final salt = _generateSalt(); final hash = cryptoPwHash({ "password": utf8.encode(pass), "salt": salt,