From 2183ff7cd7e1b8e2a866df603b9068eaece26329 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Fri, 3 Mar 2023 20:27:12 +0100 Subject: [PATCH] #543 vaults: custom pattern lock --- CHANGELOG.md | 4 + lib/l10n/app_en.arb | 10 +- lib/model/vaults/enums.dart | 4 +- lib/model/vaults/vaults.dart | 21 ++++ lib/utils/dependencies.dart | 5 + .../filter_editors/edit_vault_dialog.dart | 1 + .../filter_editors/password_dialog.dart | 52 +++++---- .../filter_editors/pattern_dialog.dart | 75 ++++++++++++ .../dialogs/filter_editors/pin_dialog.dart | 50 ++++---- pubspec.lock | 8 ++ pubspec.yaml | 1 + untranslated.json | 108 ++++++++++++++++++ 12 files changed, 286 insertions(+), 53 deletions(-) create mode 100644 lib/widgets/dialogs/filter_editors/pattern_dialog.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index ede6aa10c..cd566af0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Vaults: custom pattern lock + ### Changed - upgraded Flutter to stable v3.7.6 diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index a2bcaa31a..63827cd32 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -226,7 +226,8 @@ "unitSystemMetric": "Metric", "unitSystemImperial": "Imperial", - "vaultLockTypePin": "Pin", + "vaultLockTypePattern": "Pattern", + "vaultLockTypePin": "PIN", "vaultLockTypePassword": "Password", "videoControlsPlay": "Play", @@ -384,8 +385,11 @@ "vaultDialogLockModeWhenScreenOff": "Lock when screen turns off", "vaultDialogLockTypeLabel": "Lock type", - "pinDialogEnter": "Enter pin", - "pinDialogConfirm": "Confirm pin", + "patternDialogEnter": "Enter pattern", + "patternDialogConfirm": "Confirm pattern", + + "pinDialogEnter": "Enter PIN", + "pinDialogConfirm": "Confirm PIN", "passwordDialogEnter": "Enter password", "passwordDialogConfirm": "Confirm password", diff --git a/lib/model/vaults/enums.dart b/lib/model/vaults/enums.dart index 339f4c9f7..27752a438 100644 --- a/lib/model/vaults/enums.dart +++ b/lib/model/vaults/enums.dart @@ -1,13 +1,15 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/widgets.dart'; -enum VaultLockType { system, pin, password } +enum VaultLockType { system, pattern, pin, password } extension ExtraVaultLockType on VaultLockType { String getText(BuildContext context) { switch (this) { case VaultLockType.system: return context.l10n.settingsSystemDefault; + case VaultLockType.pattern: + return context.l10n.vaultLockTypePattern; case VaultLockType.pin: return context.l10n.vaultLockTypePin; case VaultLockType.password: diff --git a/lib/model/vaults/vaults.dart b/lib/model/vaults/vaults.dart index 015a08ac2..902a9aa0d 100644 --- a/lib/model/vaults/vaults.dart +++ b/lib/model/vaults/vaults.dart @@ -7,6 +7,7 @@ import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart'; import 'package:aves/widgets/dialogs/filter_editors/password_dialog.dart'; +import 'package:aves/widgets/dialogs/filter_editors/pattern_dialog.dart'; import 'package:aves/widgets/dialogs/filter_editors/pin_dialog.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -160,6 +161,16 @@ class Vaults extends ChangeNotifier { } } break; + case VaultLockType.pattern: + final pattern = await showDialog( + context: context, + builder: (context) => const PatternDialog(needConfirmation: false), + routeSettings: const RouteSettings(name: PatternDialog.routeName), + ); + if (pattern != null) { + confirmed = pattern == await securityService.readValue(details.passKey); + } + break; case VaultLockType.pin: final pin = await showDialog( context: context, @@ -211,6 +222,16 @@ class Vaults extends ChangeNotifier { } } break; + case VaultLockType.pattern: + final pattern = await showDialog( + context: context, + builder: (context) => const PatternDialog(needConfirmation: true), + routeSettings: const RouteSettings(name: PatternDialog.routeName), + ); + if (pattern != null) { + return await securityService.writeValue(details.passKey, pattern); + } + break; case VaultLockType.pin: final pin = await showDialog( context: context, diff --git a/lib/utils/dependencies.dart b/lib/utils/dependencies.dart index 9219ac084..564d43c57 100644 --- a/lib/utils/dependencies.dart +++ b/lib/utils/dependencies.dart @@ -260,6 +260,11 @@ class Dependencies { license: apache2, sourceUrl: 'https://github.com/zesage/panorama', ), + Dependency( + name: 'Pattern Lock', + license: apache2, + sourceUrl: 'https://github.com/qwert2603/pattern_lock', + ), Dependency( name: 'Percent Indicator', license: bsd2, diff --git a/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart b/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart index 52c81df08..165a8c055 100644 --- a/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart @@ -39,6 +39,7 @@ class _EditVaultDialogState extends State { final List _lockTypeOptions = [ if (device.canAuthenticateUser) VaultLockType.system, if (device.canUseCrypto) ...[ + VaultLockType.pattern, VaultLockType.pin, VaultLockType.password, ], diff --git a/lib/widgets/dialogs/filter_editors/password_dialog.dart b/lib/widgets/dialogs/filter_editors/password_dialog.dart index 80b075e6b..08f4864e8 100644 --- a/lib/widgets/dialogs/filter_editors/password_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/password_dialog.dart @@ -41,31 +41,7 @@ class _PasswordDialogState extends State { controller: _controller, focusNode: _focusNode, obscureText: true, - onSubmitted: (password) { - if (widget.needConfirmation) { - if (_confirming) { - final match = _firstPassword == password; - Navigator.maybeOf(context)?.pop(match ? password : null); - if (!match) { - showDialog( - context: context, - builder: (context) => AvesDialog( - content: Text(context.l10n.genericFailureFeedback), - actions: const [OkButton()], - ), - routeSettings: const RouteSettings(name: AvesDialog.warningRouteName), - ); - } - } else { - _firstPassword = password; - _controller.clear(); - setState(() => _confirming = true); - WidgetsBinding.instance.addPostFrameCallback((_) => _focusNode.requestFocus()); - } - } else { - Navigator.maybeOf(context)?.pop(password); - } - }, + onSubmitted: _submit, autofillHints: const [AutofillHints.password], ), ), @@ -73,4 +49,30 @@ class _PasswordDialogState extends State { ), ); } + + void _submit(String password) { + if (widget.needConfirmation) { + if (_confirming) { + final match = _firstPassword == password; + Navigator.maybeOf(context)?.pop(match ? password : null); + if (!match) { + showDialog( + context: context, + builder: (context) => AvesDialog( + content: Text(context.l10n.genericFailureFeedback), + actions: const [OkButton()], + ), + routeSettings: const RouteSettings(name: AvesDialog.warningRouteName), + ); + } + } else { + _firstPassword = password; + _controller.clear(); + setState(() => _confirming = true); + WidgetsBinding.instance.addPostFrameCallback((_) => _focusNode.requestFocus()); + } + } else { + Navigator.maybeOf(context)?.pop(password); + } + } } diff --git a/lib/widgets/dialogs/filter_editors/pattern_dialog.dart b/lib/widgets/dialogs/filter_editors/pattern_dialog.dart new file mode 100644 index 000000000..f77241c9d --- /dev/null +++ b/lib/widgets/dialogs/filter_editors/pattern_dialog.dart @@ -0,0 +1,75 @@ +import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/dialogs/aves_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:pattern_lock/pattern_lock.dart'; +import 'package:provider/provider.dart'; + +class PatternDialog extends StatefulWidget { + static const routeName = '/dialog/pattern'; + + final bool needConfirmation; + + const PatternDialog({ + super.key, + required this.needConfirmation, + }); + + @override + State createState() => _PatternDialogState(); +} + +class _PatternDialogState extends State { + bool _confirming = false; + String? _firstPattern; + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return AvesDialog( + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(_confirming ? context.l10n.patternDialogConfirm : context.l10n.patternDialogEnter), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: SizedBox.square( + dimension: context.select((mq) => mq.size.shortestSide / 2), + child: PatternLock( + relativePadding: .4, + selectedColor: colorScheme.secondary, + notSelectedColor: colorScheme.onBackground, + pointRadius: 8, + fillPoints: true, + onInputComplete: (input) => _submit(input.join()), + ), + ), + ), + ], + ), + ); + } + + void _submit(String pattern) { + if (widget.needConfirmation) { + if (_confirming) { + final match = _firstPattern == pattern; + Navigator.maybeOf(context)?.pop(match ? pattern : null); + if (!match) { + showDialog( + context: context, + builder: (context) => AvesDialog( + content: Text(context.l10n.genericFailureFeedback), + actions: const [OkButton()], + ), + routeSettings: const RouteSettings(name: AvesDialog.warningRouteName), + ); + } + } else { + _firstPattern = pattern; + setState(() => _confirming = true); + } + } else { + Navigator.maybeOf(context)?.pop(pattern); + } + } +} diff --git a/lib/widgets/dialogs/filter_editors/pin_dialog.dart b/lib/widgets/dialogs/filter_editors/pin_dialog.dart index 7394061c0..52098c5b7 100644 --- a/lib/widgets/dialogs/filter_editors/pin_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/pin_dialog.dart @@ -38,30 +38,7 @@ class _PinDialogState extends State { controller: _controller, obscureText: true, onChanged: (v) {}, - onCompleted: (pin) { - if (widget.needConfirmation) { - if (_confirming) { - final match = _firstPin == pin; - Navigator.maybeOf(context)?.pop(match ? pin : null); - if (!match) { - showDialog( - context: context, - builder: (context) => AvesDialog( - content: Text(context.l10n.genericFailureFeedback), - actions: const [OkButton()], - ), - routeSettings: const RouteSettings(name: AvesDialog.warningRouteName), - ); - } - } else { - _firstPin = pin; - _controller.clear(); - setState(() => _confirming = true); - } - } else { - Navigator.maybeOf(context)?.pop(pin); - } - }, + onCompleted: _submit, animationType: AnimationType.scale, keyboardType: TextInputType.number, autoFocus: true, @@ -80,4 +57,29 @@ class _PinDialogState extends State { ), ); } + + void _submit(String pin) { + if (widget.needConfirmation) { + if (_confirming) { + final match = _firstPin == pin; + Navigator.maybeOf(context)?.pop(match ? pin : null); + if (!match) { + showDialog( + context: context, + builder: (context) => AvesDialog( + content: Text(context.l10n.genericFailureFeedback), + actions: const [OkButton()], + ), + routeSettings: const RouteSettings(name: AvesDialog.warningRouteName), + ); + } + } else { + _firstPin = pin; + _controller.clear(); + setState(() => _confirming = true); + } + } else { + Navigator.maybeOf(context)?.pop(pin); + } + } } diff --git a/pubspec.lock b/pubspec.lock index 149ce2fd8..4583f1972 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -853,6 +853,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + pattern_lock: + dependency: "direct main" + description: + name: pattern_lock + sha256: "7929f877bcc82744ba2c0d55aaad1ff7cf09744f516b4853363fd29a1d7f539f" + url: "https://pub.dev" + source: hosted + version: "2.0.0" pdf: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index eface67c8..e4c188594 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -80,6 +80,7 @@ dependencies: url: https://github.com/deckerst/aves_panorama.git ref: aves path: + pattern_lock: pdf: percent_indicator: permission_handler: diff --git a/untranslated.json b/untranslated.json index 385066706..ea94407bc 100644 --- a/untranslated.json +++ b/untranslated.json @@ -136,6 +136,7 @@ "themeBrightnessBlack", "unitSystemMetric", "unitSystemImperial", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "videoControlsPlay", @@ -195,6 +196,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -688,6 +691,7 @@ "themeBrightnessBlack", "unitSystemMetric", "unitSystemImperial", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "videoControlsPlay", @@ -747,6 +751,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -1170,6 +1176,7 @@ "albumTierVaults", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "newVaultWarningDialogMessage", @@ -1177,6 +1184,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -1200,6 +1209,7 @@ "albumTierVaults", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "newVaultWarningDialogMessage", @@ -1207,6 +1217,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -1224,12 +1236,27 @@ "el": [ "chipActionGoToPlacePage", + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm", "exportEntryDialogWriteMetadata", "drawerPlacePage", "placePageTitle", "placeEmpty" ], + "es": [ + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm" + ], + + "eu": [ + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm" + ], + "fa": [ "clearTooltip", "chipActionGoToPlacePage", @@ -1269,6 +1296,7 @@ "lengthUnitPixel", "lengthUnitPercent", "nameConflictStrategySkip", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "videoControlsNone", @@ -1315,6 +1343,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -1698,6 +1728,12 @@ "filePickerUseThisFolder" ], + "fr": [ + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm" + ], + "gl": [ "columnCount", "chipActionGoToPlacePage", @@ -1731,6 +1767,7 @@ "themeBrightnessLight", "themeBrightnessDark", "themeBrightnessBlack", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "videoPlaybackSkip", @@ -1783,6 +1820,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -2353,6 +2392,7 @@ "themeBrightnessBlack", "unitSystemMetric", "unitSystemImperial", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "videoControlsPlay", @@ -2412,6 +2452,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -2827,10 +2869,19 @@ "filePickerUseThisFolder" ], + "id": [ + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm" + ], + "it": [ "chipActionGoToPlacePage", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm", "exportEntryDialogWriteMetadata", "drawerPlacePage", "placePageTitle", @@ -2855,6 +2906,7 @@ "lengthUnitPercent", "subtitlePositionTop", "subtitlePositionBottom", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "newVaultWarningDialogMessage", @@ -2862,6 +2914,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -2884,6 +2938,12 @@ "settingsWidgetDisplayedItem" ], + "ko": [ + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm" + ], + "lt": [ "columnCount", "chipActionGoToPlacePage", @@ -2896,6 +2956,7 @@ "keepScreenOnVideoPlayback", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "newVaultWarningDialogMessage", @@ -2903,6 +2964,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -2932,6 +2995,7 @@ "albumTierVaults", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "newVaultWarningDialogMessage", @@ -2939,6 +3003,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -2975,6 +3041,7 @@ "lengthUnitPercent", "subtitlePositionTop", "subtitlePositionBottom", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "widgetDisplayedItemRandom", @@ -2984,6 +3051,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -3026,6 +3095,7 @@ "displayRefreshRatePreferLowest", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "wallpaperTargetHome", @@ -3036,6 +3106,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -3323,6 +3395,12 @@ "wallpaperUseScrollEffect" ], + "pl": [ + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm" + ], + "pt": [ "columnCount", "chipActionGoToPlacePage", @@ -3331,8 +3409,11 @@ "albumTierVaults", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "newVaultWarningDialogMessage", + "patternDialogEnter", + "patternDialogConfirm", "passwordDialogConfirm", "authenticateToConfigureVault", "authenticateToUnlockVault", @@ -3351,6 +3432,9 @@ "chipActionGoToPlacePage", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm", "exportEntryDialogWriteMetadata", "drawerPlacePage", "placePageTitle", @@ -3363,6 +3447,9 @@ "filterLocatedLabel", "filterTaggedLabel", "lengthUnitPixel", + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm", "authenticateToConfigureVault", "authenticateToUnlockVault", "exportEntryDialogWriteMetadata", @@ -3390,6 +3477,7 @@ "coordinateDms", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "otherDirectoryDescription", @@ -3404,6 +3492,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -3808,6 +3898,7 @@ "albumTierVaults", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "newVaultWarningDialogMessage", @@ -3815,6 +3906,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -4153,6 +4246,7 @@ "albumTierVaults", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "newVaultWarningDialogMessage", @@ -4160,6 +4254,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -4175,6 +4271,12 @@ "settingsDisablingBinWarningDialogMessage" ], + "uk": [ + "vaultLockTypePattern", + "patternDialogEnter", + "patternDialogConfirm" + ], + "zh": [ "chipActionGoToPlacePage", "chipActionLock", @@ -4185,6 +4287,7 @@ "albumTierVaults", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "newVaultWarningDialogMessage", @@ -4192,6 +4295,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter", @@ -4224,6 +4329,7 @@ "albumTierVaults", "lengthUnitPixel", "lengthUnitPercent", + "vaultLockTypePattern", "vaultLockTypePin", "vaultLockTypePassword", "newVaultWarningDialogMessage", @@ -4231,6 +4337,8 @@ "configureVaultDialogTitle", "vaultDialogLockModeWhenScreenOff", "vaultDialogLockTypeLabel", + "patternDialogEnter", + "patternDialogConfirm", "pinDialogEnter", "pinDialogConfirm", "passwordDialogEnter",