Compare commits

..

125 Commits
4.1.2 ... 4.1.4

Author SHA1 Message Date
J-Jamet
536e038306 Merge branch 'release/4.1.4' 2025-08-14 13:04:44 +02:00
J-Jamet
9e1f6d29a5 fix: update Gemfile.lock 2025-08-14 12:28:22 +02:00
J-Jamet
7a9469e59d fix: code improvement #2105 2025-08-13 19:21:42 +02:00
J-Jamet
c12eb3d643 fix: error code 28 #2105 2025-08-13 19:03:15 +02:00
J-Jamet
da0f02e536 fix: better prompt variable management #2105 2025-08-13 18:27:51 +02:00
J-Jamet
04bcc6631c fix: open auto prompt too often #2105 2025-08-13 17:59:56 +02:00
J-Jamet
698e3b7fb1 fix: auto open prompt #2105 2025-08-13 11:49:54 +02:00
J-Jamet
6de02384c1 fix: close prompt #2105 2025-08-13 11:25:36 +02:00
J-Jamet
df3bd7e0a1 fix: cipher call #2105 2025-08-13 11:03:50 +02:00
J-Jamet
c8c232639f fix: better cipher and prompt workflow #2105 2025-08-12 19:49:31 +02:00
J-Jamet
192d6eedd0 fix: autoprompt thread #2105 2025-08-12 15:51:41 +02:00
J-Jamet
9cae3f0794 fix: initDecryptData #2105 2025-08-12 15:33:35 +02:00
J-Jamet
a680db9707 fix: initDecryptData #2105 2025-08-12 15:20:32 +02:00
J-Jamet
fe526089d7 fix: auto prompt #2105 2025-08-12 14:54:52 +02:00
J-Jamet
dfd7ade416 fix: Regression #2105 2025-08-12 13:42:57 +02:00
J-Jamet
3cd65345c5 fix: Small biometric fixes 2025-08-12 13:04:52 +02:00
J-Jamet
2d398908de fix: refresh when activate setting 2025-08-12 11:11:12 +02:00
J-Jamet
756454abc3 fix: update CHANGELOG 2025-08-12 10:52:42 +02:00
J-Jamet
b7619b45b1 fix: Transition deprecation 2025-08-12 10:51:15 +02:00
J-Jamet
1369a3cad9 fix: test dependency and gradle version 2025-08-12 10:26:32 +02:00
J-Jamet
f46c062c4e fix: Auto biometric prompt #2105 2025-08-10 22:34:48 +02:00
J-Jamet
0a0abef4d4 fix: Keystore exception 2025-08-10 21:37:41 +02:00
J-Jamet
3a8245ee74 fix: Exception as Snackbar 2025-08-10 21:32:12 +02:00
J-Jamet
7be554a378 fix: unlock manager #2098 #2101 2025-08-10 12:24:23 +02:00
J-Jamet
6c37f7b12c fix: Update to 4.1.4 2025-08-03 18:17:56 +02:00
J-Jamet
3a67ec09d5 fix: Update CHANGELOG 2025-07-25 16:04:02 +02:00
J-Jamet
dca800b1bb Merge tag '4.1.3' into develop
4.1.3
2025-07-25 15:56:49 +02:00
J-Jamet
70665f110d Merge branch 'release/4.1.3' 2025-07-25 15:56:17 +02:00
J-Jamet
3b39cafb99 fix: Warnings 2025-07-25 14:30:47 +02:00
J-Jamet
2b5ecb2f84 fix: Simplify query #2096 2025-07-24 22:19:39 +02:00
J-Jamet
e397b92c36 fix: Search and allow empty query #2096 2025-07-24 21:29:43 +02:00
J-Jamet
e273eb6e03 fix: Replace strong tag 2025-07-24 20:36:01 +02:00
J-Jamet
28b624afa3 fix: Descriptions 2025-07-24 20:30:14 +02:00
J-Jamet
fb1e6cdc3f Merge branch 'develop' of https://hosted.weblate.org/projects/keepass-dx/strings into translations 2025-07-24 20:23:50 +02:00
jonnysemon
8b6499d040 Translated using Weblate (Arabic)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/
2025-07-24 20:16:45 +02:00
Kunzisoft
054af507ad Translated using Weblate (French)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2025-07-24 20:16:44 +02:00
J-Jamet
ac9bb9b666 fix: Biometric error #2081 2025-07-24 19:41:28 +02:00
J-Jamet
809e1929e5 fix: Biometric error #2081 2025-07-24 19:21:29 +02:00
J-Jamet
a1b1338d67 fix: Biometric after orientation change #2081 2025-07-24 19:12:25 +02:00
J-Jamet
bd4cacfab1 Merge branch 'IzzySoft-fastlane' into develop 2025-07-24 18:26:32 +02:00
J-Jamet
e0343bdc55 Merge branch 'IzzySoft-iod' into develop 2025-07-24 18:23:40 +02:00
J-Jamet
b743d004e2 fix: Template EMAIL #1986 2025-07-24 18:22:11 +02:00
J-Jamet
4b20e035b2 fix: Upgrade CHANGELOG 2025-07-24 17:12:09 +02:00
J-Jamet
afe5fddc50 Merge branch 'shauser88-fix-save-copy-localtime' into develop 2025-07-24 17:09:48 +02:00
J-Jamet
d68ca1b51f Merge branch 'fix-save-copy-localtime' of github.com:shauser88/KeePassDX into shauser88-fix-save-copy-localtime 2025-07-24 17:08:55 +02:00
Jérémy JAMET
061b087229 Merge pull request #2095 from d20n/fix-en_GB-strings
Fix several incomplete en_GB strings
2025-07-24 16:59:23 +02:00
J-Jamet
bb3a379965 fix: Upgrade CHANGELOG 2025-07-24 16:42:35 +02:00
J-Jamet
593b5c6338 fix: Biometric error prompts #2081 2025-07-24 16:39:42 +02:00
J-Jamet
56f8a1bf9f fix: Upgrade version and CHANGELOG 2025-07-22 18:45:44 +02:00
J-Jamet
962b547b36 fix: Registration #2089 2025-07-22 18:40:36 +02:00
Izzy
6df8ff4310 fastlane: slight formatting adjustments to full descriptions 2025-07-20 00:31:38 +02:00
Martin Milchevski
52f17140b8 Translated using Weblate (Macedonian)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/mk/
2025-07-20 00:03:07 +02:00
ginger-co
75c2bb4a87 Translated using Weblate (Hebrew)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/he/
2025-07-20 00:03:05 +02:00
Martin Milchevski
f36f6c3155 Translated using Weblate (Macedonian)
Currently translated at 4.8% (32 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/mk/
2025-07-18 23:16:13 +02:00
Martin Milchevski
b88b92c5b0 Added translation using Weblate (Macedonian) 2025-07-18 23:00:51 +02:00
pigmentblue15
d2c569c4f0 Translated using Weblate (Japanese)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ja/
2025-07-16 17:01:50 +02:00
Izzy
cb1316564e IzzyOnDroid provides both, the Free and the Libre variants 2025-07-15 09:51:50 +02:00
VfBFan
245d3f7df2 Translated using Weblate (German)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/de/
2025-07-14 19:02:00 +02:00
Random
3729b3c5a0 Translated using Weblate (Italian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2025-07-14 19:01:52 +02:00
Random
7ce5eb3c27 Translated using Weblate (Italian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2025-07-14 19:01:50 +02:00
VfBFan
43defea85e Translated using Weblate (German)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2025-07-14 19:01:48 +02:00
d20n
8470c4e39b Fix several incomplete en_GB strings 2025-07-14 17:20:42 +02:00
Stephan Paternotte
a41afb7f1e Translated using Weblate (Dutch)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/nl/
2025-07-07 15:02:17 +02:00
Stephan Paternotte
32d9cfbe29 Translated using Weblate (Dutch)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/nl/
2025-07-07 15:02:15 +02:00
Masowick
7210652567 Translated using Weblate (German)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/de/
2025-07-07 15:02:13 +02:00
VfBFan
ab15967ad7 Translated using Weblate (German)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/de/
2025-07-07 15:02:11 +02:00
Stephan Paternotte
44df4ec181 Added translation using Weblate (Dutch) 2025-07-06 14:29:32 +02:00
Fjuro
7afe356082 Translated using Weblate (Czech)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/cs/
2025-07-06 14:29:32 +02:00
Fjuro
87597553b8 Translated using Weblate (Czech)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/cs/
2025-07-06 14:29:32 +02:00
Fjuro
27e5f58d5e Added translation using Weblate (Czech) 2025-07-05 23:12:59 +02:00
jonnysemon
762c946d35 Translated using Weblate (Arabic)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ar/
2025-07-05 23:12:58 +02:00
jonnysemon
21a927e3e9 Translated using Weblate (Arabic)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ar/
2025-07-05 23:12:58 +02:00
jonnysemon
f93bb7436a Translated using Weblate (Arabic)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ar/
2025-07-05 23:12:58 +02:00
jonnysemon
6294fddbba Translated using Weblate (Arabic)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/
2025-07-05 23:12:58 +02:00
jonnysemon
c5719dfaf2 Translated using Weblate (Arabic)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/
2025-07-05 23:12:58 +02:00
Matthaiks
673fd67f15 Translated using Weblate (Polish)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2025-07-05 23:12:58 +02:00
Fjuro
25524c48e9 Translated using Weblate (Czech)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2025-07-05 23:12:58 +02:00
Fjuro
631b924c33 Translated using Weblate (Czech)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2025-07-05 23:12:58 +02:00
J-Jamet
fba12bc278 Merge tag '4.1.2' into develop
4.1.2
2025-07-05 17:30:44 +02:00
Tomás
bf7e014f8c Translated using Weblate (Spanish)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/es/
2025-07-05 11:40:35 +02:00
Tomás
40e1607698 Translated using Weblate (Spanish)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/es/
2025-07-05 11:40:35 +02:00
Matthaiks
4a132f06fe Translated using Weblate (Polish)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2025-07-05 11:40:35 +02:00
Tomás
0396dd975d Translated using Weblate (Spanish)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/es/
2025-07-05 02:47:45 +02:00
Yurt Page
a6b20455ef Translated using Weblate (Russian)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ru/
2025-07-04 16:00:48 +02:00
Bora Atıcı
ca148ef546 Translated using Weblate (Turkish)
Currently translated at 66.6% (2 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/tr/
2025-07-02 19:01:48 +02:00
Priit Jõerüüt
25df86606c Translated using Weblate (Estonian)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/et/
2025-07-01 18:31:43 +02:00
Priit Jõerüüt
811f33eb3f Translated using Weblate (Estonian)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/et/
2025-07-01 18:31:43 +02:00
solokot
ca7e2ed89d Translated using Weblate (Russian)
Currently translated at 66.6% (2 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ru/
2025-07-01 18:31:43 +02:00
solokot
6f4cd79e2c Translated using Weblate (Russian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2025-07-01 18:31:43 +02:00
solokot
da1caf4b8b Translated using Weblate (Russian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2025-07-01 18:31:43 +02:00
Stephan Paternotte
4a0a8e44ca Translated using Weblate (Dutch)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nl/
2025-07-01 18:31:43 +02:00
Stephan Paternotte
6bc2c3481b Translated using Weblate (Dutch)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nl/
2025-07-01 18:31:43 +02:00
Liner Seven
dc75837ac7 Translated using Weblate (Japanese)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ja/
2025-07-01 09:22:50 +02:00
Liner Seven
9849b0a1da Translated using Weblate (Japanese)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ja/
2025-07-01 09:22:50 +02:00
Priit Jõerüüt
2c15a1ddd6 Translated using Weblate (Estonian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/et/
2025-07-01 09:22:50 +02:00
Priit Jõerüüt
98eb9976cf Translated using Weblate (Estonian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/et/
2025-07-01 09:22:50 +02:00
Keterion
0d9a5810b1 Translated using Weblate (Filipino)
Currently translated at 46.2% (308 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fil/
2025-07-01 09:22:50 +02:00
Besnik Bleta
1adaa137a5 Translated using Weblate (Albanian)
Currently translated at 96.5% (643 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sq/
2025-07-01 09:22:50 +02:00
bowornsin
44a428d15a Translated using Weblate (Thai)
Currently translated at 89.3% (595 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/th/
2025-07-01 09:22:50 +02:00
Suman Garai
5416a7942a Translated using Weblate (Bengali)
Currently translated at 52.5% (350 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/bn/
2025-07-01 09:22:49 +02:00
Keterion
9e0024baf5 Translated using Weblate (English (United Kingdom))
Currently translated at 33.9% (226 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en_GB/
2025-07-01 09:22:49 +02:00
Keterion
8d47ce38c2 Translated using Weblate (Vietnamese)
Currently translated at 98.6% (657 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/vi/
2025-07-01 09:22:49 +02:00
109247019824
80af43c0ca Translated using Weblate (Bulgarian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/bg/
2025-07-01 09:22:49 +02:00
109247019824
225f8243c2 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/bg/
2025-07-01 09:22:49 +02:00
தமிழ்நேரம்
68bc118add Translated using Weblate (Tamil)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ta/
2025-07-01 09:22:49 +02:00
தமிழ்நேரம்
abbc584402 Translated using Weblate (Tamil)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ta/
2025-07-01 09:22:49 +02:00
தமிழ்நேரம்
6635594639 Translated using Weblate (Tamil)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ta/
2025-07-01 09:22:49 +02:00
தமிழ்நேரம்
10db77d402 Translated using Weblate (Tamil)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ta/
2025-07-01 09:22:49 +02:00
Allan Nordhøy
000fd7e520 Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.4% (656 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2025-07-01 09:22:48 +02:00
Keterion
c8ced4ae59 Translated using Weblate (Galician)
Currently translated at 87.0% (580 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/gl/
2025-07-01 09:22:48 +02:00
hugoalh
9209ca9af7 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.8% (665 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hant/
2025-07-01 09:22:48 +02:00
大王叫我来巡山
c7bd90c610 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2025-07-01 09:22:47 +02:00
aasami
fceb9c3547 Translated using Weblate (Slovak)
Currently translated at 99.8% (665 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sk/
2025-07-01 09:22:47 +02:00
Ash Ed
030c49b571 Translated using Weblate (Russian)
Currently translated at 99.8% (665 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2025-07-01 09:22:47 +02:00
Stephan Paternotte
f2d6a6a536 Translated using Weblate (Dutch)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nl/
2025-07-01 09:22:47 +02:00
Liner Seven
8f61521f05 Translated using Weblate (Japanese)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ja/
2025-07-01 09:22:47 +02:00
cc5efd7b0
89af7ec5d0 Translated using Weblate (Japanese)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ja/
2025-07-01 09:22:47 +02:00
Ghost of Sparta
362f1aebed Translated using Weblate (Hungarian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/
2025-07-01 09:22:46 +02:00
summoner001
5226527cec Translated using Weblate (Hungarian)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/
2025-07-01 09:22:46 +02:00
Aitor Elorza
b8464cd0e5 Translated using Weblate (Basque)
Currently translated at 95.6% (637 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/eu/
2025-07-01 09:22:46 +02:00
Óscar Fernández Díaz
46e7b04d66 Translated using Weblate (Spanish)
Currently translated at 99.6% (664 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/es/
2025-07-01 09:22:46 +02:00
VfBFan
73111b770f Translated using Weblate (German)
Currently translated at 100.0% (666 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2025-07-01 09:22:46 +02:00
Keterion
995d485700 Translated using Weblate (Danish)
Currently translated at 98.6% (657 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2025-07-01 09:22:46 +02:00
pitroig
5ebbbef667 Translated using Weblate (Catalan)
Currently translated at 99.8% (665 of 666 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ca/
2025-07-01 09:22:45 +02:00
Stefan Hauser
a7a93fa2a2 Use local date/time in filename for saving a copy of the database 2025-01-16 09:15:32 +01:00
110 changed files with 2140 additions and 1792 deletions

View File

@@ -1,3 +1,15 @@
KeePassDX(4.1.4)
* Fix UnlockManager #2098 #2101
* Auto device unlock prompt #2105
* Small fixes ##2066
KeePassDX(4.1.3)
* Fix Autofill Registration #2089
* Fix Biometric errors #2081
* Fixed timestamp in copy file #1981 #1983
* Fix Template Email #1986
* Fix Search #2096
KeePassDX(4.1.2)
* Fix URL search #1940 #1946 #2003 #2040 #2044
* Fix Autofill popup #2054

View File

@@ -9,31 +9,35 @@ GEM
public_suffix (>= 2.0.2, < 7.0)
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.0)
aws-partitions (1.1009.0)
aws-sdk-core (3.213.0)
aws-eventstream (1.4.0)
aws-partitions (1.1146.0)
aws-sdk-core (3.229.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
bigdecimal
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.95.0)
aws-sdk-core (~> 3, >= 3.210.0)
logger
aws-sdk-kms (1.110.0)
aws-sdk-core (~> 3, >= 3.228.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.171.0)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sdk-s3 (1.196.1)
aws-sdk-core (~> 3, >= 3.228.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.10.1)
aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.2.0)
base64 (0.3.0)
bigdecimal (3.2.2)
claide (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
declarative (0.0.20)
digest-crc (0.6.5)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20240107)
dotenv (2.8.1)
@@ -55,11 +59,11 @@ GEM
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-em_synchrony (1.0.1)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-multipart (1.1.1)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
@@ -67,8 +71,8 @@ GEM
faraday-retry (1.0.3)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.3.1)
fastlane (2.225.0)
fastimage (2.4.0)
fastlane (2.228.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -108,7 +112,7 @@ GEM
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty (~> 0.4.1)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-versioning_android (0.1.1)
fastlane-sirp (1.0.0)
@@ -130,12 +134,12 @@ GEM
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a)
google-cloud-core (1.7.1)
google-cloud-core (1.8.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.4.0)
google-cloud-errors (1.5.0)
google-cloud-storage (1.47.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
@@ -151,36 +155,39 @@ GEM
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.7)
http-cookie (1.0.8)
domain_name (~> 0.5)
httpclient (2.8.3)
httpclient (2.9.0)
mutex_m
jmespath (1.6.2)
json (2.8.2)
jwt (2.9.3)
json (2.13.2)
jwt (2.10.2)
base64
logger (1.7.0)
mini_magick (4.13.2)
mini_mime (1.1.5)
multi_json (1.15.0)
multi_json (1.17.0)
multipart-post (2.4.1)
mutex_m (0.3.0)
nanaimo (0.4.0)
naturally (2.2.1)
naturally (2.3.0)
nkf (0.2.0)
optparse (0.6.0)
os (1.1.4)
plist (3.7.1)
public_suffix (6.0.1)
rake (13.2.1)
plist (3.7.2)
public_suffix (6.0.2)
rake (13.3.0)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.3.9)
rouge (2.0.7)
rexml (3.4.1)
rouge (3.28.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
rubyzip (2.4.1)
security (0.1.5)
signet (0.19.0)
signet (0.20.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
@@ -207,8 +214,8 @@ GEM
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty (0.4.1)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
@@ -220,4 +227,4 @@ DEPENDENCIES
fastlane-plugin-versioning_android
BUNDLED WITH
2.5.10
2.6.9

View File

@@ -54,7 +54,7 @@ Optional visual styles are accessible after a contribution (and a congratulatory
|--------|--------|---------|
| [Google Play](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.free) | ![Google Play Release](https://img.shields.io/endpoint?color=blue&logo=google-play&logoColor=green&url=https%3A%2F%2Fplay.cuzi.workers.dev%2Fplay%3Fi%3Dcom.kunzisoft.keepass.free%26gl%3DUS%26hl%3Den%26l%3DGoogle%2520Play%26m%3D%24version) | Free + [Pro](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro) |
| [F-Droid](https://f-droid.org/en/packages/com.kunzisoft.keepass.libre/) | ![F-Droid Version](https://img.shields.io/f-droid/v/com.kunzisoft.keepass.libre?logo=F-Droid&label=F-Droid) | Libre |
| [IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.kunzisoft.keepass.free) | ![IzzyOnDroid Version](https://img.shields.io/endpoint?&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAADAFBMVEUA0////wAA0v8A0v8A0////wD//wAFz/QA0/8A0/8A0/8A0/8A0v///wAA0/8A0/8A0/8A0/8A0//8/gEA0/8A0/8B0/4A0/8A0/8A0/+j5QGAwwIA0//C9yEA0/8A0/8A0/8A0/8A0/8A0/+n4SAA0/8A0/8A0/+o6gCw3lKt7QCv5SC+422b3wC19AC36zAA0/+d1yMA0/8A0/+W2gEA0/+w8ACz8gCKzgG7+QC+9CFLfwkA0/8A0////wAA0/8A0/8A0/8A0/+f2xym3iuHxCGq5BoA1P+m2joI0vONyiCz3mLO7oYA0/8M1Piq3Ei78CbB8EPe8LLj9Ly751G77zWQ1AC96UYC0fi37CL//wAA0/8A0////wD//wCp3jcA0/+j3SGj2i/I72Sx4zHE8FLB8zak1kYeycDI6nRl3qEA0/7V7psA0v6WzTa95mGi2RvB5XkPy9zH5YJ3uwGV1yxVihRLiwdxtQ1ZkAf//wD//wD//wD//wD//wCn5gf//wD//wD//wD//wD//wAA0/+h4A3R6p8A0/+X1w565OD6/ARg237n9csz2vPz+gNt37V/vifO8HW68B/L6ZOCwxXY8KRQsWRzhExAtG/E612a1Rd/pTBpmR9qjysduKVhmxF9mTY51aUozK+CsDSA52T//wD//wAA0////wD//wBJ1JRRxFWjzlxDyXRc0pGT1wCG0CWB3VGUzSTh8h6c0TSr5CCJ5FFxvl6s4H3m8xML0/DA5CvK51EX1N+Y2gSt4Dag3ChE3fax2ki68yO57NF10FRZnUPl88eJxhuCxgCz5EOLwEGf1DFutmahzGW98x0W1PGk3R154MHE6bOn69qv3gy92oG90o+Hn07B7rhCmiyMwECv1nO+0pQfwrCo57xF2daXsVhKrEdenQAduaee1Bsjr42z5D9RoCXy+QNovXpy2Z5MtWDO/TiSukaF3UtE1K6j3B4YwLc5wXlzpyIK0u5zy3uJqg4pu5RTpkZmpVKyAP8A0wBHcExHcEyBUSeEAAABAHRSTlP///9F9wjAAxD7FCEGzBjd08QyEL39abMd6///8P/ZWAnipIv/cC6B//7////////L/1Dz/0D///////86/vYnquY3/v///5T//v///17///////////////84S3QNB/8L/////////////7r/////NP////9l/////wPD4yis/x7Ym2lWSP+em////0n////////v///////////////////7//7pdGN3Urr6/+v/6aT////+//H/o2P/1v+7r7jp4PM/3p4g////g///K///481LxO///v////9w////8v/////9/p3J///a+P9v/5KR/+n///+p/xf//8P//wAAe7FyaAAABCZJREFUSMdj+E8iYKBUgwIHnwQ3N7cEHxcH+///VayoAE0Dh41qR7aBnCIQ8MsJKHH9/99czYYMWlA0cIkJGjMgAKfq//9RNYzIgLcBWYOTiCgDMhDn+B9bh6LebiWyH6L5UZQzONoAHWSHoqEpDkkDsyKqelv1//9rG1HUN9YihZK9AKp6BkG+/6xNqA5ajhSsCkrIipmYGGRa//9vQXVQXSySBnkWJOUMfn5Myuz/G3hR1NdEIUUchwiy+bkTsg4dbW/fu6W/e1c3XMMy5JiOZkFxUFZo74mgKTqaKXu0+2HqVwkja3BH9kFu361JwcHTfPJD4mdfe8ULAdVRyGlJAcVFfg+CQOozZ4XrJ85+JgwBsVXIGriQw5Tp4ZScezd8JiWnBupru30qwJZa+ZAjmWlC8fUZM4qB6kPnLNSPLMWqQQ5ZQ5aOzs1HmamBaQHzFs6y+qAmJCTE8f9/QgKSBg4DJPWc6zVDQkIC09JkZSPD38kukpExFpT4z67uYI/QwCOOCCK/izvu5CWl6AcEWMnKWml7LWbKZfH9/99UkknQHhGsynDz+65eWXv3/JmJrq5eXienVlRUfH/z8VvCf45soKQIH1yDEQsszrp6gwq9C73T87xcXadKl5TkFev4A/2tygmSBqYXqAYJmK+ZuoJydDR1vP09DA0NOy2kpdML81+U/heCpH1JU3jig7lJ5nKOT4i/t6ZHkqGzs4lJmIVHfrj+JR4HqLQSD0yDkCNEpGNn5ix9D03/eJdElTZdKV2TpNOhkwt8YUlNUgimgV0dLMBvf1gz1MolPd5FRcVNSkpDQ8owJeBCDyIhrIDnOD5QcuIU+3/2QKSs9laQ+noNLS0zLWdtqyP7mBAFAw88TwsJgMuJYweBGjYngtWbmeuZOW+bvNQToUFOAlFqOBk4Ov3/L7Z60/aN0p1tUhpa5nqWlub7C3p2I9QzyAghlUvczOz/1fhzPT3XSIfpSmmYAdVbmm1gV0dSz8DSilpUQsqCddIWIA3meuZaJqdMJZEzl6gRqgZIWZAxUdoizERXN8yi5MltcZTChzMaRQM3JNUWHS8rL/+yaPGvMmvr5ywoGoxtkDWwQ+Pb89ycBeWfGSJeL/la+RS1eOPnRtbQKgMRjZg+t8x6PkP273nWQAoFOPAgaeAThKXAmXMrK39Kmr5fsuBlBqoXfJGLe3VbmHjG9Mczi9T//3h7vygXtcDlQtJg44iQiIjIBRbGPO7gghPJy0ZIxT2HOLIUgwxQzsgYrUR350HSIMaJLidhgKY+mw+pflBDrX8E7OGBjPCAPc76gQFSTqAIiYrb/8dRP4CyosJ/rmwU5XIxHMilt4QBJwsSkBMClxOQULBlkRRwEONmR2kJcDGjADX2/+xO8r5iqjExqmLyrWpcPFRta1BfAwCtyN3XpuJ4RgAAAABJRU5ErkJggg==&url=https://apt.izzysoft.de/fdroid/api/v1/shield/com.kunzisoft.keepass.free&label=IzzyOnDroid) | Free |
| [IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.kunzisoft.keepass.free) | ![IzzyOnDroid Version](https://img.shields.io/endpoint?&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAADAFBMVEUA0////wAA0v8A0v8A0////wD//wAFz/QA0/8A0/8A0/8A0/8A0v///wAA0/8A0/8A0/8A0/8A0//8/gEA0/8A0/8B0/4A0/8A0/8A0/+j5QGAwwIA0//C9yEA0/8A0/8A0/8A0/8A0/8A0/+n4SAA0/8A0/8A0/+o6gCw3lKt7QCv5SC+422b3wC19AC36zAA0/+d1yMA0/8A0/+W2gEA0/+w8ACz8gCKzgG7+QC+9CFLfwkA0/8A0////wAA0/8A0/8A0/8A0/+f2xym3iuHxCGq5BoA1P+m2joI0vONyiCz3mLO7oYA0/8M1Piq3Ei78CbB8EPe8LLj9Ly751G77zWQ1AC96UYC0fi37CL//wAA0/8A0////wD//wCp3jcA0/+j3SGj2i/I72Sx4zHE8FLB8zak1kYeycDI6nRl3qEA0/7V7psA0v6WzTa95mGi2RvB5XkPy9zH5YJ3uwGV1yxVihRLiwdxtQ1ZkAf//wD//wD//wD//wD//wCn5gf//wD//wD//wD//wD//wAA0/+h4A3R6p8A0/+X1w565OD6/ARg237n9csz2vPz+gNt37V/vifO8HW68B/L6ZOCwxXY8KRQsWRzhExAtG/E612a1Rd/pTBpmR9qjysduKVhmxF9mTY51aUozK+CsDSA52T//wD//wAA0////wD//wBJ1JRRxFWjzlxDyXRc0pGT1wCG0CWB3VGUzSTh8h6c0TSr5CCJ5FFxvl6s4H3m8xML0/DA5CvK51EX1N+Y2gSt4Dag3ChE3fax2ki68yO57NF10FRZnUPl88eJxhuCxgCz5EOLwEGf1DFutmahzGW98x0W1PGk3R154MHE6bOn69qv3gy92oG90o+Hn07B7rhCmiyMwECv1nO+0pQfwrCo57xF2daXsVhKrEdenQAduaee1Bsjr42z5D9RoCXy+QNovXpy2Z5MtWDO/TiSukaF3UtE1K6j3B4YwLc5wXlzpyIK0u5zy3uJqg4pu5RTpkZmpVKyAP8A0wBHcExHcEyBUSeEAAABAHRSTlP///9F9wjAAxD7FCEGzBjd08QyEL39abMd6///8P/ZWAnipIv/cC6B//7////////L/1Dz/0D///////86/vYnquY3/v///5T//v///17///////////////84S3QNB/8L/////////////7r/////NP////9l/////wPD4yis/x7Ym2lWSP+em////0n////////v///////////////////7//7pdGN3Urr6/+v/6aT////+//H/o2P/1v+7r7jp4PM/3p4g////g///K///481LxO///v////9w////8v/////9/p3J///a+P9v/5KR/+n///+p/xf//8P//wAAe7FyaAAABCZJREFUSMdj+E8iYKBUgwIHnwQ3N7cEHxcH+///VayoAE0Dh41qR7aBnCIQ8MsJKHH9/99czYYMWlA0cIkJGjMgAKfq//9RNYzIgLcBWYOTiCgDMhDn+B9bh6LebiWyH6L5UZQzONoAHWSHoqEpDkkDsyKqelv1//9rG1HUN9YihZK9AKp6BkG+/6xNqA5ajhSsCkrIipmYGGRa//9vQXVQXSySBnkWJOUMfn5Myuz/G3hR1NdEIUUchwiy+bkTsg4dbW/fu6W/e1c3XMMy5JiOZkFxUFZo74mgKTqaKXu0+2HqVwkja3BH9kFu361JwcHTfPJD4mdfe8ULAdVRyGlJAcVFfg+CQOozZ4XrJ85+JgwBsVXIGriQw5Tp4ZScezd8JiWnBupru30qwJZa+ZAjmWlC8fUZM4qB6kPnLNSPLMWqQQ5ZQ5aOzs1HmamBaQHzFs6y+qAmJCTE8f9/QgKSBg4DJPWc6zVDQkIC09JkZSPD38kukpExFpT4z67uYI/QwCOOCCK/izvu5CWl6AcEWMnKWml7LWbKZfH9/99UkknQHhGsynDz+65eWXv3/JmJrq5eXienVlRUfH/z8VvCf45soKQIH1yDEQsszrp6gwq9C73T87xcXadKl5TkFev4A/2tygmSBqYXqAYJmK+ZuoJydDR1vP09DA0NOy2kpdML81+U/heCpH1JU3jig7lJ5nKOT4i/t6ZHkqGzs4lJmIVHfrj+JR4HqLQSD0yDkCNEpGNn5ix9D03/eJdElTZdKV2TpNOhkwt8YUlNUgimgV0dLMBvf1gz1MolPd5FRcVNSkpDQ8owJeBCDyIhrIDnOD5QcuIU+3/2QKSs9laQ+noNLS0zLWdtqyP7mBAFAw88TwsJgMuJYweBGjYngtWbmeuZOW+bvNQToUFOAlFqOBk4Ov3/L7Z60/aN0p1tUhpa5nqWlub7C3p2I9QzyAghlUvczOz/1fhzPT3XSIfpSmmYAdVbmm1gV0dSz8DSilpUQsqCddIWIA3meuZaJqdMJZEzl6gRqgZIWZAxUdoizERXN8yi5MltcZTChzMaRQM3JNUWHS8rL/+yaPGvMmvr5ywoGoxtkDWwQ+Pb89ycBeWfGSJeL/la+RS1eOPnRtbQKgMRjZg+t8x6PkP273nWQAoFOPAgaeAThKXAmXMrK39Kmr5fsuBlBqoXfJGLe3VbmHjG9Mczi9T//3h7vygXtcDlQtJg44iQiIjIBRbGPO7gghPJy0ZIxT2HOLIUgwxQzsgYrUR350HSIMaJLidhgKY+mw+pflBDrX8E7OGBjPCAPc76gQFSTqAIiYrb/8dRP4CyosJ/rmwU5XIxHMilt4QBJwsSkBMClxOQULBlkRRwEONmR2kJcDGjADX2/+xO8r5iqjExqmLyrWpcPFRta1BfAwCtyN3XpuJ4RgAAAABJRU5ErkJggg==&url=https://apt.izzysoft.de/fdroid/api/v1/shield/com.kunzisoft.keepass.free&label=IzzyOnDroid) | Free & [Libre](https://apt.izzysoft.de/fdroid/index/apk/com.kunzisoft.keepass.libre) |
| [GitHub](https://github.com/Kunzisoft/KeePassDX/releases) / [Obtainium](https://github.com/ImranR98/Obtainium) | ![GitHub Release](https://img.shields.io/github/v/release/Kunzisoft/KeePassDX?include_prereleases&logo=GitHub&label=GitHub) | Free & Libre |
## Package authenticity from GitHub

View File

@@ -11,8 +11,8 @@ android {
applicationId "com.kunzisoft.keepass"
minSdkVersion 15
targetSdkVersion 34
versionCode = 134
versionName = "4.1.2"
versionCode = 136
versionName = "4.1.4"
multiDexEnabled true
testApplicationId = "com.kunzisoft.keepass.tests"

View File

@@ -124,41 +124,41 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
if (autofillComponent == null) {
setResult(Activity.RESULT_CANCELED)
finish()
} else if (!KeeAutofillService.autofillAllowedFor(
} else if (KeeAutofillService.autofillAllowedFor(
applicationId = searchInfo.applicationId,
webDomain = searchInfo.webDomain,
context = this
)) {
// If database is open
SearchHelper.checkAutoSearchInfo(this,
database,
searchInfo,
{ openedDatabase, items ->
// Items found
AutofillHelper.buildResponseAndSetResult(this, openedDatabase, items)
finish()
},
{ openedDatabase ->
// Show the database UI to select the entry
GroupActivity.launchForAutofillResult(this,
openedDatabase,
mAutofillActivityResultLauncher,
autofillComponent,
searchInfo,
false)
},
{
// If database not open
FileDatabaseSelectActivity.launchForAutofillResult(this,
mAutofillActivityResultLauncher,
autofillComponent,
searchInfo)
}
)
} else {
showBlockRestartMessage()
setResult(Activity.RESULT_CANCELED)
finish()
} else {
// If database is open
SearchHelper.checkAutoSearchInfo(this,
database,
searchInfo,
{ openedDatabase, items ->
// Items found
AutofillHelper.buildResponseAndSetResult(this, openedDatabase, items)
finish()
},
{ openedDatabase ->
// Show the database UI to select the entry
GroupActivity.launchForAutofillResult(this,
openedDatabase,
mAutofillActivityResultLauncher,
autofillComponent,
searchInfo,
false)
},
{
// If database not open
FileDatabaseSelectActivity.launchForAutofillResult(this,
mAutofillActivityResultLauncher,
autofillComponent,
searchInfo)
}
)
}
}
@@ -169,40 +169,40 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
applicationId = searchInfo.applicationId,
webDomain = searchInfo.webDomain,
context = this
)) {
showBlockRestartMessage()
setResult(Activity.RESULT_CANCELED)
} else {
)) {
val readOnly = database?.isReadOnly != false
SearchHelper.checkAutoSearchInfo(this,
database,
searchInfo,
{ openedDatabase, _ ->
if (!readOnly) {
// Show the database UI to select the entry
GroupActivity.launchForRegistration(this,
openedDatabase,
registerInfo)
} else {
showReadOnlySaveMessage()
}
},
{ openedDatabase ->
if (!readOnly) {
// Show the database UI to select the entry
GroupActivity.launchForRegistration(this,
openedDatabase,
registerInfo)
} else {
showReadOnlySaveMessage()
}
},
{
// If database not open
FileDatabaseSelectActivity.launchForRegistration(this,
registerInfo)
database,
searchInfo,
{ openedDatabase, _ ->
if (!readOnly) {
// Show the database UI to select the entry
GroupActivity.launchForRegistration(this,
openedDatabase,
registerInfo)
} else {
showReadOnlySaveMessage()
}
},
{ openedDatabase ->
if (!readOnly) {
// Show the database UI to select the entry
GroupActivity.launchForRegistration(this,
openedDatabase,
registerInfo)
} else {
showReadOnlySaveMessage()
}
},
{
// If database not open
FileDatabaseSelectActivity.launchForRegistration(this,
registerInfo)
}
)
} else {
showBlockRestartMessage()
setResult(Activity.RESULT_CANCELED)
}
finish()
}

View File

@@ -316,6 +316,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
private fun launchPasswordActivityWithPath(databaseUri: Uri) {
launchPasswordActivity(databaseUri, null, null)
// Delete flickering for kitkat <=
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
overridePendingTransition(0, 0)
}

View File

@@ -116,7 +116,7 @@ import com.kunzisoft.keepass.view.showActionErrorIfNeeded
import com.kunzisoft.keepass.view.updateLockPaddingStart
import com.kunzisoft.keepass.viewmodels.GroupEditViewModel
import com.kunzisoft.keepass.viewmodels.GroupViewModel
import org.joda.time.Instant
import org.joda.time.LocalDateTime
class GroupActivity : DatabaseLockActivity(),
@@ -343,7 +343,7 @@ class GroupActivity : DatabaseLockActivity(),
mExternalFileHelper?.createDocument(
getString(R.string.database_file_name_default) +
"_" +
Instant.now().toString() +
LocalDateTime.now().toString() +
mDatabase?.defaultFileExtension)
}
R.id.menu_lock_all -> {

View File

@@ -32,7 +32,6 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.CompoundButton
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
@@ -43,25 +42,33 @@ import androidx.appcompat.widget.Toolbar
import androidx.biometric.BiometricManager
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.commit
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.snackbar.Snackbar
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.DuplicateUuidDialog
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.SpecialMode
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity.Companion.UI_VISIBLE_DURING_LOCK
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
import com.kunzisoft.keepass.autofill.AutofillComponent
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.biometric.AdvancedUnlockFragment
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
import com.kunzisoft.keepass.biometric.DeviceUnlockFragment
import com.kunzisoft.keepass.biometric.DeviceUnlockManager
import com.kunzisoft.keepass.biometric.deviceUnlockError
import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.MainCredential
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException
import com.kunzisoft.keepass.education.PasswordActivityEducation
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.*
import com.kunzisoft.keepass.model.CipherDecryptDatabase
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.CredentialStorage
import com.kunzisoft.keepass.model.RegisterInfo
import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.CIPHER_DATABASE_KEY
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.DATABASE_URI_KEY
@@ -79,12 +86,14 @@ import com.kunzisoft.keepass.utils.getParcelableExtraCompat
import com.kunzisoft.keepass.view.MainCredentialView
import com.kunzisoft.keepass.view.asError
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
import com.kunzisoft.keepass.viewmodels.AdvancedUnlockViewModel
import com.kunzisoft.keepass.viewmodels.DatabaseFileViewModel
import com.kunzisoft.keepass.viewmodels.DeviceUnlockState
import com.kunzisoft.keepass.viewmodels.DeviceUnlockViewModel
import kotlinx.coroutines.launch
import java.io.FileNotFoundException
class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderListener {
class MainCredentialActivity : DatabaseModeActivity() {
// Views
private var toolbar: Toolbar? = null
@@ -95,10 +104,10 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
private var confirmButtonView: Button? = null
private var infoContainerView: ViewGroup? = null
private lateinit var coordinatorLayout: CoordinatorLayout
private var advancedUnlockFragment: AdvancedUnlockFragment? = null
private var deviceUnlockFragment: DeviceUnlockFragment? = null
private val mDatabaseFileViewModel: DatabaseFileViewModel by viewModels()
private val mAdvancedUnlockViewModel: AdvancedUnlockViewModel by viewModels()
private val mDeviceUnlockViewModel: DeviceUnlockViewModel by viewModels()
private val mPasswordActivityEducation = PasswordActivityEducation(this)
@@ -166,21 +175,14 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
}
// Listen password checkbox to init advanced unlock and confirmation button
mainCredentialView?.onPasswordChecked =
CompoundButton.OnCheckedChangeListener { _, _ ->
mAdvancedUnlockViewModel.checkUnlockAvailability()
enableConfirmationButton()
}
mainCredentialView?.onKeyFileChecked =
CompoundButton.OnCheckedChangeListener { _, _ ->
// TODO mAdvancedUnlockViewModel.checkUnlockAvailability()
enableConfirmationButton()
}
mainCredentialView?.onHardwareKeyChecked =
CompoundButton.OnCheckedChangeListener { _, _ ->
// TODO mAdvancedUnlockViewModel.checkUnlockAvailability()
enableConfirmationButton()
}
mainCredentialView?.onConditionToStoreCredentialChanged = { _, verified ->
mDeviceUnlockViewModel.checkConditionToStoreCredential(
condition = verified,
databaseFileUri = mDatabaseFileUri
)
// TODO Async by ViewModel
enableConfirmationButton()
}
// Observe if default database
mDatabaseFileViewModel.isDefaultDatabase.observe(this) { isDefaultDatabase ->
@@ -228,17 +230,50 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
onDatabaseFileLoaded(databaseFile?.databaseUri, keyFileUri, hardwareKey)
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
mDeviceUnlockViewModel.uiState.collect { uiState ->
// New value received
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
uiState.credentialRequiredCipher?.let { cipher ->
mDeviceUnlockViewModel.encryptCredential(
credential = getCredentialForEncryption(),
cipher = cipher
)
}
}
uiState.cipherEncryptDatabase?.let { cipherEncryptDatabase ->
onCredentialEncrypted(cipherEncryptDatabase)
mDeviceUnlockViewModel.consumeCredentialEncrypted()
}
uiState.cipherDecryptDatabase?.let { cipherDecryptDatabase ->
onCredentialDecrypted(cipherDecryptDatabase)
mDeviceUnlockViewModel.consumeCredentialDecrypted()
}
uiState.exception?.let { error ->
Snackbar.make(
coordinatorLayout,
deviceUnlockError(error, this@MainCredentialActivity),
Snackbar.LENGTH_LONG
).asError().show()
mDeviceUnlockViewModel.exceptionShown()
}
}
}
}
}
override fun onResume() {
super.onResume()
// Init Biometric elements only if allowed
if (PreferencesUtil.isAdvancedUnlockEnable(this)) {
advancedUnlockFragment = supportFragmentManager
.findFragmentByTag(UNLOCK_FRAGMENT_TAG) as? AdvancedUnlockFragment?
if (advancedUnlockFragment == null) {
advancedUnlockFragment = AdvancedUnlockFragment().also {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& PreferencesUtil.isAdvancedUnlockEnable(this)) {
deviceUnlockFragment = supportFragmentManager
.findFragmentByTag(UNLOCK_FRAGMENT_TAG) as? DeviceUnlockFragment?
if (deviceUnlockFragment == null) {
deviceUnlockFragment = DeviceUnlockFragment().also {
supportFragmentManager.commit {
replace(
R.id.fragment_advanced_unlock_container_view,
@@ -258,11 +293,6 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
sendBroadcast(Intent(BACK_PREVIOUS_KEYBOARD_ACTION))
}
// Don't allow auto open prompt if lock become when UI visible
if (DatabaseLockActivity.LOCKING_ACTIVITY_UI_VISIBLE_DURING_LOCK == true) {
mAdvancedUnlockViewModel.allowAutoOpenBiometricPrompt = false
}
mDatabaseFileUri?.let { databaseFileUri ->
mDatabaseFileViewModel.loadDatabaseFile(databaseFileUri)
}
@@ -296,9 +326,6 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
super.onDatabaseActionFinished(database, actionTask, result)
when (actionTask) {
ACTION_DATABASE_LOAD_TASK -> {
// Recheck advanced unlock if error
mAdvancedUnlockViewModel.initAdvancedUnlockMode()
if (result.isSuccess) {
launchGroupActivityIfLoaded(database)
} else {
@@ -389,6 +416,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
private fun launchGroupActivityIfLoaded(database: ContextualDatabase) {
// Check if database really loaded
if (database.loaded) {
mDeviceUnlockViewModel.allowAutoOpenBiometricPrompt = true
clearCredentialsViews(clearKeyFile = true, clearHardwareKey = true)
GroupActivity.launch(this,
database,
@@ -400,23 +428,6 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
}
}
override fun retrieveCredentialForEncryption(): ByteArray {
return mainCredentialView?.retrieveCredentialForStorage(credentialStorageListener)
?: byteArrayOf()
}
override fun conditionToStoreCredential(): Boolean {
return mainCredentialView?.conditionToStoreCredential() == true
}
override fun onCredentialEncrypted(cipherEncryptDatabase: CipherEncryptDatabase) {
// Load the database if password is registered with biometric
loadDatabase(mDatabaseFileUri,
mainCredentialView?.getMainCredential(),
cipherEncryptDatabase
)
}
private val credentialStorageListener = object: MainCredentialView.CredentialStorageListener {
override fun passwordToStore(password: String?): ByteArray? {
return password?.toByteArray()
@@ -433,7 +444,20 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
}
}
override fun onCredentialDecrypted(cipherDecryptDatabase: CipherDecryptDatabase) {
private fun getCredentialForEncryption(): ByteArray {
return mainCredentialView?.retrieveCredentialForStorage(credentialStorageListener)
?: byteArrayOf()
}
private fun onCredentialEncrypted(cipherEncryptDatabase: CipherEncryptDatabase) {
// Load the database if password is registered with biometric
loadDatabase(mDatabaseFileUri,
mainCredentialView?.getMainCredential(),
cipherEncryptDatabase
)
}
private fun onCredentialDecrypted(cipherDecryptDatabase: CipherDecryptDatabase) {
// Load the database if password is retrieve from biometric
// Retrieve from biometric
val mainCredential = mainCredentialView?.getMainCredential() ?: MainCredential()
@@ -485,7 +509,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
loadDatabase()
} else {
// Init Biometric elements
mAdvancedUnlockViewModel.databaseFileLoaded(databaseFileUri)
mDeviceUnlockViewModel.databaseFileLoaded(databaseFileUri)
}
enableConfirmationButton()
@@ -515,8 +539,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
override fun onPause() {
// Reinit locking activity UI variable
DatabaseLockActivity.LOCKING_ACTIVITY_UI_VISIBLE_DURING_LOCK = null
UI_VISIBLE_DURING_LOCK = false
super.onPause()
}
@@ -645,7 +668,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !readOnlyEducationPerformed) {
val biometricCanAuthenticate = AdvancedUnlockManager.canAuthenticate(this)
val biometricCanAuthenticate = DeviceUnlockManager.canAuthenticate(this)
if ((biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS)
&& advancedUnlockButton != null) {

View File

@@ -47,10 +47,14 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.*
import com.kunzisoft.keepass.utils.LOCK_ACTION
import com.kunzisoft.keepass.utils.LockReceiver
import com.kunzisoft.keepass.utils.closeDatabase
import com.kunzisoft.keepass.utils.registerLockReceiver
import com.kunzisoft.keepass.utils.unregisterLockReceiver
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
import com.kunzisoft.keepass.viewmodels.NodesViewModel
import java.util.*
import java.util.UUID
abstract class DatabaseLockActivity : DatabaseModeActivity(),
PasswordEncodingDialogFragment.Listener {
@@ -184,8 +188,7 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
mLockReceiver = LockReceiver {
mDatabase = null
closeDatabase(database)
if (LOCKING_ACTIVITY_UI_VISIBLE_DURING_LOCK == null)
LOCKING_ACTIVITY_UI_VISIBLE_DURING_LOCK = LOCKING_ACTIVITY_UI_VISIBLE
UI_VISIBLE_DURING_LOCK = UI_VISIBLE
mExitLock = true
closeOptionsMenu()
finish()
@@ -414,7 +417,7 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
invalidateOptionsMenu()
LOCKING_ACTIVITY_UI_VISIBLE = true
UI_VISIBLE = true
}
protected fun checkTimeAndLockIfTimeoutOrResetTimeout(action: (() -> Unit)? = null) {
@@ -429,7 +432,7 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
}
override fun onPause() {
LOCKING_ACTIVITY_UI_VISIBLE = false
UI_VISIBLE = false
super.onPause()
@@ -481,8 +484,8 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
const val TIMEOUT_ENABLE_KEY = "TIMEOUT_ENABLE_KEY"
const val TIMEOUT_ENABLE_KEY_DEFAULT = true
private var LOCKING_ACTIVITY_UI_VISIBLE = false
var LOCKING_ACTIVITY_UI_VISIBLE_DURING_LOCK: Boolean? = null
var UI_VISIBLE: Boolean = false
var UI_VISIBLE_DURING_LOCK: Boolean = false
}
}

View File

@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.activities.stylish
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -77,7 +78,18 @@ abstract class StylishActivity : AppCompatActivity() {
startActivity(intent)
}
finish()
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
overrideActivityTransition(
OVERRIDE_TRANSITION_OPEN,
android.R.anim.fade_in,
android.R.anim.fade_out
)
else
overridePendingTransition(
android.R.anim.fade_in,
android.R.anim.fade_out
)
}
override fun onCreate(savedInstanceState: Bundle?) {

View File

@@ -177,14 +177,18 @@ class CipherDatabaseAction(context: Context) {
}
}
fun containsCipherDatabase(databaseUri: Uri,
fun containsCipherDatabase(databaseUri: Uri?,
contains: (Boolean) -> Unit) {
getCipherDatabase(databaseUri) {
contains.invoke(it != null)
if (databaseUri == null) {
contains.invoke(false)
} else {
getCipherDatabase(databaseUri) {
contains.invoke(it != null)
}
}
}
fun resetCipherParameters(databaseUri: Uri) {
fun resetCipherParameters(databaseUri: Uri?) {
containsCipherDatabase(databaseUri) { contains ->
if (contains) {
mBinder?.resetTimer()

View File

@@ -1,10 +0,0 @@
package com.kunzisoft.keepass.biometric
import androidx.annotation.StringRes
import javax.crypto.Cipher
data class AdvancedUnlockCryptoPrompt(var cipher: Cipher,
@StringRes var promptTitleId: Int,
@StringRes var promptDescriptionId: Int? = null,
var isDeviceCredentialOperation: Boolean,
var isBiometricOperation: Boolean)

View File

@@ -1,678 +0,0 @@
/*
* Copyright 2020 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.biometric
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.view.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.core.view.MenuProvider
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.database.exception.UnknownDatabaseLocationException
import com.kunzisoft.keepass.model.CipherDecryptDatabase
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.CredentialStorage
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
import com.kunzisoft.keepass.view.hideByFading
import com.kunzisoft.keepass.view.showByFading
import com.kunzisoft.keepass.viewmodels.AdvancedUnlockViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class AdvancedUnlockFragment: Fragment(), AdvancedUnlockManager.AdvancedUnlockCallback {
private var mBuilderListener: BuilderListener? = null
private var mAdvancedUnlockEnabled = false
private var mAutoOpenPromptEnabled = false
private var advancedUnlockManager: AdvancedUnlockManager? = null
private var biometricMode: Mode = Mode.BIOMETRIC_UNAVAILABLE
private var mAdvancedUnlockInfoView: AdvancedUnlockInfoView? = null
var databaseFileUri: Uri? = null
private set
// TODO Retrieve credential storage from app database
var credentialDatabaseStorage: CredentialStorage = CredentialStorage.DEFAULT
// Variable to check if the prompt can be open (if the right activity is currently shown)
// checkBiometricAvailability() allows open biometric prompt and onDestroy() removes the authorization
private var allowOpenBiometricPrompt = false
private lateinit var cipherDatabaseAction : CipherDatabaseAction
private var cipherDatabaseListener: CipherDatabaseAction.CipherDatabaseListener? = null
private val mAdvancedUnlockViewModel: AdvancedUnlockViewModel by activityViewModels()
// Only to fix multiple fingerprint menu #332
private var mAllowAdvancedUnlockMenu = false
private var mAddBiometricMenuInProgress = false
// Only keep connection when we request a device credential activity
private var keepConnection = false
private var mDeviceCredentialResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
mAdvancedUnlockViewModel.allowAutoOpenBiometricPrompt = false
// To wait resume
if (keepConnection) {
mAdvancedUnlockViewModel.deviceCredentialAuthSucceeded =
result.resultCode == Activity.RESULT_OK
}
keepConnection = false
}
private val menuProvider: MenuProvider = object: MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// biometric menu
if (mAllowAdvancedUnlockMenu)
menuInflater.inflate(R.menu.advanced_unlock, menu)
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
when (menuItem.itemId) {
R.id.menu_keystore_remove_key -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
deleteEncryptedDatabaseKey()
}
}
return false
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
mAdvancedUnlockEnabled = PreferencesUtil.isAdvancedUnlockEnable(context)
mAutoOpenPromptEnabled = PreferencesUtil.isAdvancedUnlockPromptAutoOpenEnable(context)
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mBuilderListener = context as BuilderListener
}
} catch (e: ClassCastException) {
throw ClassCastException(context.toString()
+ " must implement " + BuilderListener::class.java.name)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
cipherDatabaseAction = CipherDatabaseAction.getInstance(requireContext().applicationContext)
mAdvancedUnlockViewModel.onInitAdvancedUnlockModeRequested.observe(this) {
initAdvancedUnlockMode()
}
mAdvancedUnlockViewModel.onUnlockAvailabilityCheckRequested.observe(this) {
checkUnlockAvailability()
}
mAdvancedUnlockViewModel.onDatabaseFileLoaded.observe(this) {
onDatabaseLoaded(it)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
val rootView = inflater.inflate(R.layout.fragment_advanced_unlock, container, false)
mAdvancedUnlockInfoView = rootView.findViewById(R.id.advanced_unlock_view)
return rootView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity?.addMenuProvider(menuProvider, viewLifecycleOwner)
}
override fun onResume() {
super.onResume()
context?.let {
mAdvancedUnlockEnabled = PreferencesUtil.isAdvancedUnlockEnable(it)
mAutoOpenPromptEnabled = PreferencesUtil.isAdvancedUnlockPromptAutoOpenEnable(it)
}
keepConnection = false
}
private fun onDatabaseLoaded(databaseUri: Uri?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// To get device credential unlock result, only if same database uri
if (databaseUri != null
&& mAdvancedUnlockEnabled) {
val deviceCredentialAuthSucceeded = mAdvancedUnlockViewModel.deviceCredentialAuthSucceeded
deviceCredentialAuthSucceeded?.let {
if (databaseUri == databaseFileUri) {
if (deviceCredentialAuthSucceeded == true) {
advancedUnlockManager?.advancedUnlockCallback?.onAuthenticationSucceeded()
} else {
advancedUnlockManager?.advancedUnlockCallback?.onAuthenticationFailed()
}
} else {
disconnect()
}
} ?: run {
if (databaseUri != databaseFileUri) {
connect(databaseUri)
}
}
} else {
disconnect()
}
mAdvancedUnlockViewModel.deviceCredentialAuthSucceeded = null
}
}
/**
* Check unlock availability and change the current mode depending of device's state
*/
private fun checkUnlockAvailability() {
context?.let { context ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
allowOpenBiometricPrompt = true
if (PreferencesUtil.isBiometricUnlockEnable(context)) {
// biometric not supported (by API level or hardware) so keep option hidden
// or manually disable
val biometricCanAuthenticate = AdvancedUnlockManager.canAuthenticate(context)
if (!PreferencesUtil.isAdvancedUnlockEnable(context)
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
toggleMode(Mode.BIOMETRIC_UNAVAILABLE)
} else if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED) {
toggleMode(Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED)
} else {
// biometric is available but not configured, show icon but in disabled state with some information
if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
toggleMode(Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED)
} else {
selectMode()
}
}
} else if (PreferencesUtil.isDeviceCredentialUnlockEnable(context)) {
if (AdvancedUnlockManager.isDeviceSecure(context)) {
selectMode()
} else {
toggleMode(Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED)
}
}
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun selectMode() {
// Check if fingerprint well init (be called the first time the fingerprint is configured
// and the activity still active)
if (advancedUnlockManager?.isKeyManagerInitialized != true) {
advancedUnlockManager = AdvancedUnlockManager { requireActivity() }
// callback for fingerprint findings
advancedUnlockManager?.advancedUnlockCallback = this
}
// Recheck to change the mode
if (advancedUnlockManager?.isKeyManagerInitialized != true) {
toggleMode(Mode.KEY_MANAGER_UNAVAILABLE)
} else {
if (mBuilderListener?.conditionToStoreCredential() == true) {
// listen for encryption
toggleMode(Mode.STORE_CREDENTIAL)
} else {
databaseFileUri?.let { databaseUri ->
cipherDatabaseAction.containsCipherDatabase(databaseUri) { containsCipher ->
// biometric available but no stored password found yet for this DB so show info don't listen
toggleMode(if (containsCipher) {
// listen for decryption
Mode.EXTRACT_CREDENTIAL
} else {
// wait for typing
Mode.WAIT_CREDENTIAL
})
}
}
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun toggleMode(newBiometricMode: Mode) {
if (newBiometricMode != biometricMode) {
biometricMode = newBiometricMode
initAdvancedUnlockMode()
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun initNotAvailable() {
showViews(false)
mAdvancedUnlockInfoView?.setIconViewClickListener(null)
}
@RequiresApi(Build.VERSION_CODES.M)
private fun openBiometricSetting() {
mAdvancedUnlockInfoView?.setIconViewClickListener {
try {
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
context?.startActivity(Intent(Settings.ACTION_BIOMETRIC_ENROLL))
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> {
@Suppress("DEPRECATION") context
?.startActivity(Intent(Settings.ACTION_FINGERPRINT_ENROLL))
}
else -> {
context?.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
}
}
} catch (e: Exception) {
// ACTION_SECURITY_SETTINGS does not contain fingerprint enrollment on some devices...
context?.startActivity(Intent(Settings.ACTION_SETTINGS))
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun initSecurityUpdateRequired() {
showViews(true)
setAdvancedUnlockedTitleView(R.string.biometric_security_update_required)
openBiometricSetting()
}
@RequiresApi(Build.VERSION_CODES.M)
private fun initNotConfigured() {
showViews(true)
setAdvancedUnlockedTitleView(R.string.configure_biometric)
setAdvancedUnlockedMessageView("")
openBiometricSetting()
}
@RequiresApi(Build.VERSION_CODES.M)
private fun initKeyManagerNotAvailable() {
showViews(true)
setAdvancedUnlockedTitleView(R.string.keystore_not_accessible)
openBiometricSetting()
}
@RequiresApi(Build.VERSION_CODES.M)
private fun initWaitData() {
showViews(true)
setAdvancedUnlockedTitleView(R.string.unavailable)
setAdvancedUnlockedMessageView("")
context?.let { context ->
mAdvancedUnlockInfoView?.setIconViewClickListener {
onAuthenticationError(BiometricPrompt.ERROR_UNABLE_TO_PROCESS,
context.getString(R.string.credential_before_click_advanced_unlock_button))
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun openAdvancedUnlockPrompt(cryptoPrompt: AdvancedUnlockCryptoPrompt) {
lifecycleScope.launch(Dispatchers.Main) {
if (allowOpenBiometricPrompt) {
if (cryptoPrompt.isDeviceCredentialOperation)
keepConnection = true
try {
advancedUnlockManager?.openAdvancedUnlockPrompt(cryptoPrompt,
mDeviceCredentialResultLauncher)
} catch (e: Exception) {
Log.e(TAG, "Unable to open advanced unlock prompt", e)
setAdvancedUnlockedTitleView(R.string.advanced_unlock_prompt_not_initialized)
}
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun initEncryptData() {
showViews(true)
setAdvancedUnlockedTitleView(R.string.unlock_and_link_biometric)
setAdvancedUnlockedMessageView("")
advancedUnlockManager?.initEncryptData { cryptoPrompt ->
// Set listener to open the biometric dialog and save credential
mAdvancedUnlockInfoView?.setIconViewClickListener { _ ->
openAdvancedUnlockPrompt(cryptoPrompt)
}
} ?: throw Exception("AdvancedUnlockManager not initialized")
}
@RequiresApi(Build.VERSION_CODES.M)
private fun initDecryptData() {
showViews(true)
setAdvancedUnlockedTitleView(R.string.unlock)
setAdvancedUnlockedMessageView("")
advancedUnlockManager?.let { unlockHelper ->
databaseFileUri?.let { databaseUri ->
cipherDatabaseAction.getCipherDatabase(databaseUri) { cipherDatabase ->
cipherDatabase?.let {
unlockHelper.initDecryptData(it.specParameters) { cryptoPrompt ->
// Set listener to open the biometric dialog and check credential
mAdvancedUnlockInfoView?.setIconViewClickListener { _ ->
openAdvancedUnlockPrompt(cryptoPrompt)
}
// Auto open the biometric prompt
if (mAdvancedUnlockViewModel.allowAutoOpenBiometricPrompt
&& mAutoOpenPromptEnabled) {
mAdvancedUnlockViewModel.allowAutoOpenBiometricPrompt = false
openAdvancedUnlockPrompt(cryptoPrompt)
}
}
} ?: deleteEncryptedDatabaseKey()
}
} ?: throw UnknownDatabaseLocationException()
} ?: throw Exception("AdvancedUnlockManager not initialized")
}
private fun initAdvancedUnlockMode() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mAllowAdvancedUnlockMenu = false
try {
when (biometricMode) {
Mode.BIOMETRIC_UNAVAILABLE -> initNotAvailable()
Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED -> initSecurityUpdateRequired()
Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED -> initNotConfigured()
Mode.KEY_MANAGER_UNAVAILABLE -> initKeyManagerNotAvailable()
Mode.WAIT_CREDENTIAL -> initWaitData()
Mode.STORE_CREDENTIAL -> initEncryptData()
Mode.EXTRACT_CREDENTIAL -> initDecryptData()
}
} catch (e: Exception) {
onGenericException(e)
}
invalidateBiometricMenu()
}
}
private fun invalidateBiometricMenu() {
// Show fingerprint key deletion
if (!mAddBiometricMenuInProgress) {
mAddBiometricMenuInProgress = true
databaseFileUri?.let { databaseUri ->
cipherDatabaseAction.containsCipherDatabase(databaseUri) { containsCipher ->
mAllowAdvancedUnlockMenu = containsCipher
&& (biometricMode != Mode.BIOMETRIC_UNAVAILABLE
&& biometricMode != Mode.KEY_MANAGER_UNAVAILABLE)
mAddBiometricMenuInProgress = false
activity?.invalidateOptionsMenu()
}
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
fun connect(databaseUri: Uri) {
showViews(true)
this.databaseFileUri = databaseUri
cipherDatabaseListener = object: CipherDatabaseAction.CipherDatabaseListener {
override fun onCipherDatabaseCleared() {
advancedUnlockManager?.closeBiometricPrompt()
checkUnlockAvailability()
}
}
cipherDatabaseAction.apply {
reloadPreferences()
cipherDatabaseListener?.let {
registerDatabaseListener(it)
}
}
checkUnlockAvailability()
}
@RequiresApi(Build.VERSION_CODES.M)
fun disconnect(hideViews: Boolean = true,
closePrompt: Boolean = true) {
this.databaseFileUri = null
// Close the biometric prompt
allowOpenBiometricPrompt = false
if (closePrompt)
advancedUnlockManager?.closeBiometricPrompt()
cipherDatabaseListener?.let {
cipherDatabaseAction.unregisterDatabaseListener(it)
}
biometricMode = Mode.BIOMETRIC_UNAVAILABLE
if (hideViews) {
showViews(false)
}
}
@RequiresApi(Build.VERSION_CODES.M)
fun deleteEncryptedDatabaseKey() {
mAllowAdvancedUnlockMenu = false
advancedUnlockManager?.closeBiometricPrompt()
databaseFileUri?.let { databaseUri ->
cipherDatabaseAction.deleteByDatabaseUri(databaseUri) {
checkUnlockAvailability()
}
} ?: checkUnlockAvailability()
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
lifecycleScope.launch(Dispatchers.Main) {
Log.e(TAG, "Biometric authentication error. Code : $errorCode Error : $errString")
setAdvancedUnlockedMessageView(errString.toString())
}
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onAuthenticationFailed() {
lifecycleScope.launch(Dispatchers.Main) {
Log.e(TAG, "Biometric authentication failed, biometric not recognized")
setAdvancedUnlockedMessageView(R.string.advanced_unlock_not_recognized)
}
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onAuthenticationSucceeded() {
lifecycleScope.launch(Dispatchers.Main) {
when (biometricMode) {
Mode.BIOMETRIC_UNAVAILABLE -> {
}
Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED -> {
}
Mode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED -> {
}
Mode.KEY_MANAGER_UNAVAILABLE -> {
}
Mode.WAIT_CREDENTIAL -> {
}
Mode.STORE_CREDENTIAL -> {
// newly store the entered password in encrypted way
mBuilderListener?.retrieveCredentialForEncryption()?.let { credential ->
advancedUnlockManager?.encryptData(credential)
}
}
Mode.EXTRACT_CREDENTIAL -> {
// retrieve the encrypted value from preferences
databaseFileUri?.let { databaseUri ->
cipherDatabaseAction.getCipherDatabase(databaseUri) { cipherDatabase ->
cipherDatabase?.encryptedValue?.let { value ->
advancedUnlockManager?.decryptData(value)
} ?: deleteEncryptedDatabaseKey()
}
} ?: run {
onAuthenticationError(-1, getString(R.string.error_database_uri_null))
}
}
}
}
}
override fun handleEncryptedResult(encryptedValue: ByteArray, ivSpec: ByteArray) {
databaseFileUri?.let { databaseUri ->
mBuilderListener?.onCredentialEncrypted(
CipherEncryptDatabase().apply {
this.databaseUri = databaseUri
this.credentialStorage = credentialDatabaseStorage
this.encryptedValue = encryptedValue
this.specParameters = ivSpec
}
)
}
}
override fun handleDecryptedResult(decryptedValue: ByteArray) {
// Load database directly with password retrieve
databaseFileUri?.let { databaseUri ->
mBuilderListener?.onCredentialDecrypted(
CipherDecryptDatabase().apply {
this.databaseUri = databaseUri
this.credentialStorage = credentialDatabaseStorage
this.decryptedValue = decryptedValue
}
)
cipherDatabaseAction.resetCipherParameters(databaseUri)
}
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onUnrecoverableKeyException(e: Exception) {
setAdvancedUnlockedMessageView(R.string.advanced_unlock_invalid_key)
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onInvalidKeyException(e: Exception) {
setAdvancedUnlockedMessageView(R.string.advanced_unlock_invalid_key)
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onGenericException(e: Exception) {
val errorMessage = e.cause?.localizedMessage ?: e.localizedMessage ?: ""
setAdvancedUnlockedMessageView(errorMessage)
}
private fun showViews(show: Boolean) {
lifecycleScope.launch(Dispatchers.Main) {
if (show) {
if (mAdvancedUnlockInfoView?.visibility != View.VISIBLE)
mAdvancedUnlockInfoView?.showByFading()
}
else {
if (mAdvancedUnlockInfoView?.visibility == View.VISIBLE)
mAdvancedUnlockInfoView?.hideByFading()
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun setAdvancedUnlockedTitleView(textId: Int) {
lifecycleScope.launch(Dispatchers.Main) {
mAdvancedUnlockInfoView?.setTitle(textId)
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun setAdvancedUnlockedMessageView(textId: Int) {
lifecycleScope.launch(Dispatchers.Main) {
mAdvancedUnlockInfoView?.setMessage(textId)
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun setAdvancedUnlockedMessageView(text: CharSequence) {
lifecycleScope.launch(Dispatchers.Main) {
mAdvancedUnlockInfoView?.setMessage(text)
}
}
enum class Mode {
BIOMETRIC_UNAVAILABLE,
BIOMETRIC_SECURITY_UPDATE_REQUIRED,
DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED,
KEY_MANAGER_UNAVAILABLE,
WAIT_CREDENTIAL,
STORE_CREDENTIAL,
EXTRACT_CREDENTIAL
}
interface BuilderListener {
fun retrieveCredentialForEncryption(): ByteArray
fun conditionToStoreCredential(): Boolean
fun onCredentialEncrypted(cipherEncryptDatabase: CipherEncryptDatabase)
fun onCredentialDecrypted(cipherDecryptDatabase: CipherDecryptDatabase)
}
override fun onPause() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!keepConnection) {
// If close prompt, bug "user not authenticated in Android R"
disconnect(false)
advancedUnlockManager = null
}
}
super.onPause()
}
override fun onDestroyView() {
mAdvancedUnlockInfoView = null
super.onDestroyView()
}
override fun onDestroy() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
disconnect()
advancedUnlockManager = null
mBuilderListener = null
}
super.onDestroy()
}
override fun onDetach() {
mBuilderListener = null
super.onDetach()
}
companion object {
private val TAG = AdvancedUnlockFragment::class.java.name
}
}

View File

@@ -1,506 +0,0 @@
/*
* Copyright 2020 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.biometric
import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyPermanentlyInvalidatedException
import android.security.keystore.KeyProperties
import android.util.Log
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.RequiresApi
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.*
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.settings.PreferencesUtil
import java.security.KeyStore
import java.security.UnrecoverableKeyException
import java.util.concurrent.Executors
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec
@RequiresApi(api = Build.VERSION_CODES.M)
class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity) {
private var keyStore: KeyStore? = null
private var keyGenerator: KeyGenerator? = null
private var cipher: Cipher? = null
private var biometricPrompt: BiometricPrompt? = null
private var authenticationCallback = object: BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
advancedUnlockCallback?.onAuthenticationSucceeded()
}
override fun onAuthenticationFailed() {
advancedUnlockCallback?.onAuthenticationFailed()
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
advancedUnlockCallback?.onAuthenticationError(errorCode, errString)
}
}
var advancedUnlockCallback: AdvancedUnlockCallback? = null
private var isKeyManagerInit = false
private val biometricUnlockEnable = PreferencesUtil.isBiometricUnlockEnable(retrieveContext())
private val deviceCredentialUnlockEnable = PreferencesUtil.isDeviceCredentialUnlockEnable(retrieveContext())
val isKeyManagerInitialized: Boolean
get() {
if (!isKeyManagerInit) {
advancedUnlockCallback?.onGenericException(Exception("Biometric not initialized"))
}
return isKeyManagerInit
}
private fun isBiometricOperation(): Boolean {
return biometricUnlockEnable || isDeviceCredentialBiometricOperation()
}
// Since Android 30, device credential is also a biometric operation
private fun isDeviceCredentialOperation(): Boolean {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.R
&& deviceCredentialUnlockEnable
}
private fun isDeviceCredentialBiometricOperation(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
&& deviceCredentialUnlockEnable
}
init {
if (isDeviceSecure(retrieveContext())
&& (biometricUnlockEnable || deviceCredentialUnlockEnable)) {
try {
this.keyStore = KeyStore.getInstance(ADVANCED_UNLOCK_KEYSTORE)
this.keyGenerator = KeyGenerator.getInstance(ADVANCED_UNLOCK_KEY_ALGORITHM, ADVANCED_UNLOCK_KEYSTORE)
this.cipher = Cipher.getInstance(
ADVANCED_UNLOCK_KEY_ALGORITHM + "/"
+ ADVANCED_UNLOCK_BLOCKS_MODES + "/"
+ ADVANCED_UNLOCK_ENCRYPTION_PADDING)
isKeyManagerInit = (keyStore != null
&& keyGenerator != null
&& cipher != null)
} catch (e: Exception) {
Log.e(TAG, "Unable to initialize the keystore", e)
isKeyManagerInit = false
advancedUnlockCallback?.onGenericException(e)
}
} else {
// really not much to do when no fingerprint support found
isKeyManagerInit = false
}
}
@Synchronized private fun getSecretKey(): SecretKey? {
if (!isKeyManagerInitialized) {
return null
}
try {
// Create new key if needed
keyStore?.let { keyStore ->
keyStore.load(null)
try {
if (!keyStore.containsAlias(ADVANCED_UNLOCK_KEYSTORE_KEY)) {
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
keyGenerator?.init(
KeyGenParameterSpec.Builder(
ADVANCED_UNLOCK_KEYSTORE_KEY,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(ADVANCED_UNLOCK_BLOCKS_MODES)
.setEncryptionPaddings(ADVANCED_UNLOCK_ENCRYPTION_PADDING)
.apply {
// Require the user to authenticate with a fingerprint to authorize every use
// of the key, don't use it for device credential because it's the user authentication
if (biometricUnlockEnable) {
setUserAuthenticationRequired(true)
}
// To store in the security chip
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& retrieveContext().packageManager.hasSystemFeature(
PackageManager.FEATURE_STRONGBOX_KEYSTORE)) {
setIsStrongBoxBacked(true)
}
}
.build())
keyGenerator?.generateKey()
}
} catch (e: Exception) {
Log.e(TAG, "Unable to create a key in keystore", e)
advancedUnlockCallback?.onGenericException(e)
}
return keyStore.getKey(ADVANCED_UNLOCK_KEYSTORE_KEY, null) as SecretKey?
}
} catch (e: Exception) {
Log.e(TAG, "Unable to retrieve the key in keystore", e)
advancedUnlockCallback?.onGenericException(e)
}
return null
}
@Synchronized fun initEncryptData(actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit,) {
initEncryptData(actionIfCypherInit, true)
}
@Synchronized private fun initEncryptData(actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit,
firstLaunch: Boolean) {
if (!isKeyManagerInitialized) {
return
}
try {
getSecretKey()?.let { secretKey ->
cipher?.let { cipher ->
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
actionIfCypherInit.invoke(
AdvancedUnlockCryptoPrompt(
cipher,
R.string.advanced_unlock_prompt_store_credential_title,
R.string.advanced_unlock_prompt_store_credential_message,
isDeviceCredentialOperation(), isBiometricOperation())
)
}
}
} catch (unrecoverableKeyException: UnrecoverableKeyException) {
Log.e(TAG, "Unable to initialize encrypt data", unrecoverableKeyException)
advancedUnlockCallback?.onUnrecoverableKeyException(unrecoverableKeyException)
} catch (invalidKeyException: KeyPermanentlyInvalidatedException) {
Log.e(TAG, "Unable to initialize encrypt data", invalidKeyException)
if (firstLaunch) {
deleteAllEntryKeysInKeystoreForBiometric(retrieveContext())
initEncryptData(actionIfCypherInit, false)
} else {
advancedUnlockCallback?.onInvalidKeyException(invalidKeyException)
}
} catch (e: Exception) {
Log.e(TAG, "Unable to initialize encrypt data", e)
advancedUnlockCallback?.onGenericException(e)
}
}
@Synchronized fun encryptData(value: ByteArray) {
if (!isKeyManagerInitialized) {
return
}
try {
val encrypted = cipher?.doFinal(value) ?: byteArrayOf()
// passes updated iv spec on to callback so this can be stored for decryption
cipher?.parameters?.getParameterSpec(IvParameterSpec::class.java)?.let{ spec ->
advancedUnlockCallback?.handleEncryptedResult(encrypted, spec.iv)
}
} catch (e: Exception) {
Log.e(TAG, "Unable to encrypt data", e)
advancedUnlockCallback?.onGenericException(e)
}
}
@Synchronized fun initDecryptData(ivSpecValue: ByteArray,
actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit) {
initDecryptData(ivSpecValue, actionIfCypherInit, true)
}
@Synchronized private fun initDecryptData(ivSpecValue: ByteArray,
actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit,
firstLaunch: Boolean = true) {
if (!isKeyManagerInitialized) {
return
}
try {
// important to restore spec here that was used for decryption
val spec = IvParameterSpec(ivSpecValue)
getSecretKey()?.let { secretKey ->
cipher?.let { cipher ->
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
actionIfCypherInit.invoke(
AdvancedUnlockCryptoPrompt(
cipher,
R.string.advanced_unlock_prompt_extract_credential_title,
null,
isDeviceCredentialOperation(), isBiometricOperation())
)
}
}
} catch (unrecoverableKeyException: UnrecoverableKeyException) {
Log.e(TAG, "Unable to initialize decrypt data", unrecoverableKeyException)
if (firstLaunch) {
deleteKeystoreKey()
initDecryptData(ivSpecValue, actionIfCypherInit, firstLaunch)
} else {
advancedUnlockCallback?.onUnrecoverableKeyException(unrecoverableKeyException)
}
} catch (invalidKeyException: KeyPermanentlyInvalidatedException) {
Log.e(TAG, "Unable to initialize decrypt data", invalidKeyException)
if (firstLaunch) {
deleteAllEntryKeysInKeystoreForBiometric(retrieveContext())
initDecryptData(ivSpecValue, actionIfCypherInit, firstLaunch)
} else {
advancedUnlockCallback?.onInvalidKeyException(invalidKeyException)
}
} catch (e: Exception) {
Log.e(TAG, "Unable to initialize decrypt data", e)
advancedUnlockCallback?.onGenericException(e)
}
}
@Synchronized fun decryptData(encryptedValue: ByteArray) {
if (!isKeyManagerInitialized) {
return
}
try {
// actual decryption here
cipher?.doFinal(encryptedValue)?.let { decrypted ->
advancedUnlockCallback?.handleDecryptedResult(decrypted)
}
} catch (badPaddingException: BadPaddingException) {
Log.e(TAG, "Unable to decrypt data", badPaddingException)
advancedUnlockCallback?.onInvalidKeyException(badPaddingException)
} catch (e: Exception) {
Log.e(TAG, "Unable to decrypt data", e)
advancedUnlockCallback?.onGenericException(e)
}
}
@Synchronized fun deleteKeystoreKey() {
try {
keyStore?.load(null)
keyStore?.deleteEntry(ADVANCED_UNLOCK_KEYSTORE_KEY)
} catch (e: Exception) {
Log.e(TAG, "Unable to delete entry key in keystore", e)
advancedUnlockCallback?.onGenericException(e)
}
}
@Synchronized fun openAdvancedUnlockPrompt(cryptoPrompt: AdvancedUnlockCryptoPrompt,
deviceCredentialResultLauncher: ActivityResultLauncher<Intent>
) {
// Init advanced unlock prompt
if (biometricPrompt == null) {
biometricPrompt = BiometricPrompt(retrieveContext(),
Executors.newSingleThreadExecutor(),
authenticationCallback)
}
val promptTitle = retrieveContext().getString(cryptoPrompt.promptTitleId)
val promptDescription = cryptoPrompt.promptDescriptionId?.let { descriptionId ->
retrieveContext().getString(descriptionId)
} ?: ""
if (cryptoPrompt.isBiometricOperation) {
val promptInfoExtractCredential = BiometricPrompt.PromptInfo.Builder().apply {
setTitle(promptTitle)
if (promptDescription.isNotEmpty())
setDescription(promptDescription)
setConfirmationRequired(false)
if (isDeviceCredentialBiometricOperation()) {
setAllowedAuthenticators(DEVICE_CREDENTIAL)
} else {
setNegativeButtonText(retrieveContext().getString(android.R.string.cancel))
}
}.build()
biometricPrompt?.authenticate(
promptInfoExtractCredential,
BiometricPrompt.CryptoObject(cryptoPrompt.cipher))
}
else if (cryptoPrompt.isDeviceCredentialOperation) {
val keyGuardManager = ContextCompat.getSystemService(retrieveContext(), KeyguardManager::class.java)
@Suppress("DEPRECATION")
deviceCredentialResultLauncher.launch(
keyGuardManager?.createConfirmDeviceCredentialIntent(promptTitle, promptDescription)
)
}
}
@Synchronized fun closeBiometricPrompt() {
biometricPrompt?.cancelAuthentication()
}
interface AdvancedUnlockErrorCallback {
fun onUnrecoverableKeyException(e: Exception)
fun onInvalidKeyException(e: Exception)
fun onGenericException(e: Exception)
}
interface AdvancedUnlockCallback : AdvancedUnlockErrorCallback {
fun onAuthenticationSucceeded()
fun onAuthenticationFailed()
fun onAuthenticationError(errorCode: Int, errString: CharSequence)
fun handleEncryptedResult(encryptedValue: ByteArray, ivSpec: ByteArray)
fun handleDecryptedResult(decryptedValue: ByteArray)
}
companion object {
private val TAG = AdvancedUnlockManager::class.java.name
private const val ADVANCED_UNLOCK_KEYSTORE = "AndroidKeyStore"
private const val ADVANCED_UNLOCK_KEYSTORE_KEY = "com.kunzisoft.keepass.biometric.key"
private const val ADVANCED_UNLOCK_KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
private const val ADVANCED_UNLOCK_BLOCKS_MODES = KeyProperties.BLOCK_MODE_CBC
private const val ADVANCED_UNLOCK_ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
@RequiresApi(api = Build.VERSION_CODES.M)
fun canAuthenticate(context: Context): Int {
return try {
BiometricManager.from(context).canAuthenticate(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
&& PreferencesUtil.isDeviceCredentialUnlockEnable(context)) {
BIOMETRIC_STRONG or DEVICE_CREDENTIAL
} else {
BIOMETRIC_STRONG
}
)
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with strong biometric.", e)
try {
BiometricManager.from(context).canAuthenticate(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
&& PreferencesUtil.isDeviceCredentialUnlockEnable(context)) {
BIOMETRIC_WEAK or DEVICE_CREDENTIAL
} else {
BIOMETRIC_WEAK
}
)
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with weak biometric.", e)
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
}
}
}
fun isDeviceSecure(context: Context): Boolean {
return ContextCompat.getSystemService(context, KeyguardManager::class.java)
?.isDeviceSecure ?: false
}
fun biometricUnlockSupported(context: Context): Boolean {
val biometricCanAuthenticate = try {
BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG)
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with strong biometric.", e)
try {
BiometricManager.from(context).canAuthenticate(BIOMETRIC_WEAK)
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with weak biometric.", e)
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
}
}
return (biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
)
}
fun deviceCredentialUnlockSupported(context: Context): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val biometricCanAuthenticate = BiometricManager.from(context).canAuthenticate(DEVICE_CREDENTIAL)
(biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
)
} else {
true
}
}
/**
* Remove entry key in keystore
*/
fun deleteEntryKeyInKeystoreForBiometric(fragmentActivity: FragmentActivity,
advancedCallback: AdvancedUnlockErrorCallback) {
AdvancedUnlockManager{ fragmentActivity }.apply {
advancedUnlockCallback = object : AdvancedUnlockCallback {
override fun onAuthenticationSucceeded() {}
override fun onAuthenticationFailed() {}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {}
override fun handleEncryptedResult(encryptedValue: ByteArray, ivSpec: ByteArray) {}
override fun handleDecryptedResult(decryptedValue: ByteArray) {}
override fun onUnrecoverableKeyException(e: Exception) {
advancedCallback.onUnrecoverableKeyException(e)
}
override fun onInvalidKeyException(e: Exception) {
advancedCallback.onInvalidKeyException(e)
}
override fun onGenericException(e: Exception) {
advancedCallback.onGenericException(e)
}
}
deleteKeystoreKey()
}
}
fun deleteAllEntryKeysInKeystoreForBiometric(activity: FragmentActivity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
deleteEntryKeyInKeystoreForBiometric(
activity,
object : AdvancedUnlockErrorCallback {
fun showException(e: Exception) {
Toast.makeText(activity,
activity.getString(R.string.advanced_unlock_scanning_error, e.localizedMessage),
Toast.LENGTH_SHORT).show()
}
override fun onUnrecoverableKeyException(e: Exception) {
showException(e)
}
override fun onInvalidKeyException(e: Exception) {
showException(e)
}
override fun onGenericException(e: Exception) {
showException(e)
}
})
}
CipherDatabaseAction.getInstance(activity.applicationContext).deleteAll()
}
}
}

View File

@@ -0,0 +1,17 @@
package com.kunzisoft.keepass.biometric
import androidx.annotation.StringRes
import javax.crypto.Cipher
data class DeviceUnlockCryptoPrompt(
var type: DeviceUnlockCryptoPromptType,
var cipher: Cipher,
@StringRes var titleId: Int,
@StringRes var descriptionId: Int? = null,
var isDeviceCredentialOperation: Boolean,
var isBiometricOperation: Boolean
)
enum class DeviceUnlockCryptoPromptType {
CREDENTIAL_ENCRYPTION, CREDENTIAL_DECRYPTION
}

View File

@@ -0,0 +1,384 @@
/*
* Copyright 2020 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.biometric
import android.app.Activity
import android.app.KeyguardManager
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.core.view.MenuProvider
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity.Companion.UI_VISIBLE_DURING_LOCK
import com.kunzisoft.keepass.view.DeviceUnlockView
import com.kunzisoft.keepass.view.hideByFading
import com.kunzisoft.keepass.view.showByFading
import com.kunzisoft.keepass.viewmodels.DeviceUnlockPromptMode
import com.kunzisoft.keepass.viewmodels.DeviceUnlockViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.concurrent.Executors
@RequiresApi(Build.VERSION_CODES.M)
class DeviceUnlockFragment: Fragment() {
private var mDeviceUnlockView: DeviceUnlockView? = null
private val mDeviceUnlockViewModel: DeviceUnlockViewModel by activityViewModels()
private var mBiometricPrompt: BiometricPrompt? = null
// Only to fix multiple fingerprint menu #332
private var mAllowAdvancedUnlockMenu = false
private var mDeviceCredentialResultLauncher: ActivityResultLauncher<Intent>? = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
mDeviceUnlockViewModel.onAuthenticationSucceeded(result)
} else {
setAuthenticationFailed()
}
}
private var biometricAuthenticationCallback = object: BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
mDeviceUnlockViewModel.onAuthenticationSucceeded(result)
}
override fun onAuthenticationFailed() {
setAuthenticationFailed()
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
setAuthenticationError(errorCode, errString)
}
}
private val menuProvider: MenuProvider = object: MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// biometric menu
if (mAllowAdvancedUnlockMenu)
menuInflater.inflate(R.menu.advanced_unlock, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
when (menuItem.itemId) {
R.id.menu_keystore_remove_key ->
deleteEncryptedDatabaseKey()
}
return false
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
val rootView = inflater.inflate(R.layout.fragment_advanced_unlock, container, false)
mDeviceUnlockView = rootView.findViewById(R.id.advanced_unlock_view)
return rootView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Init device unlock prompt
mBiometricPrompt = BiometricPrompt(
this@DeviceUnlockFragment,
Executors.newSingleThreadExecutor(),
biometricAuthenticationCallback
)
activity?.addMenuProvider(menuProvider, viewLifecycleOwner)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
mDeviceUnlockViewModel.uiState.collect { uiState ->
// Change mode
toggleDeviceCredentialMode(uiState.newDeviceUnlockMode)
// Prompt
manageDeviceCredentialPrompt(uiState.cryptoPromptState)
// Advanced menu
mAllowAdvancedUnlockMenu = uiState.allowAdvancedUnlockMenu
activity?.invalidateOptionsMenu()
}
}
}
}
override fun onResume() {
super.onResume()
// Don't allow auto open prompt if lock become when UI visible
if (UI_VISIBLE_DURING_LOCK) {
mDeviceUnlockViewModel.allowAutoOpenBiometricPrompt = false
}
mDeviceUnlockViewModel.checkUnlockAvailability()
}
fun cancelBiometricPrompt() {
mBiometricPrompt?.cancelAuthentication()
}
private fun toggleDeviceCredentialMode(deviceUnlockMode: DeviceUnlockMode) {
try {
when (deviceUnlockMode) {
DeviceUnlockMode.BIOMETRIC_UNAVAILABLE -> setNotAvailableMode()
DeviceUnlockMode.BIOMETRIC_SECURITY_UPDATE_REQUIRED -> setSecurityUpdateRequiredMode()
DeviceUnlockMode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED -> setNotConfiguredMode()
DeviceUnlockMode.KEY_MANAGER_UNAVAILABLE -> setKeyManagerNotAvailableMode()
DeviceUnlockMode.WAIT_CREDENTIAL -> setWaitCredentialMode()
DeviceUnlockMode.STORE_CREDENTIAL -> setStoreCredentialMode()
DeviceUnlockMode.EXTRACT_CREDENTIAL -> setExtractCredentialMode()
}
} catch (e: Exception) {
mDeviceUnlockViewModel.setException(e)
}
}
private fun manageDeviceCredentialPrompt(
state: DeviceUnlockPromptMode
) {
mDeviceUnlockViewModel.cryptoPrompt?.let { prompt ->
when (state) {
DeviceUnlockPromptMode.IDLE -> {}
DeviceUnlockPromptMode.SHOW -> {
openPrompt(prompt)
mDeviceUnlockViewModel.promptShown()
}
DeviceUnlockPromptMode.CLOSE -> {
cancelBiometricPrompt()
mDeviceUnlockViewModel.biometricPromptClosed()
}
}
}
}
private fun openPrompt(cryptoPrompt: DeviceUnlockCryptoPrompt) {
try {
val promptTitle = getString(cryptoPrompt.titleId)
val promptDescription = cryptoPrompt.descriptionId?.let { descriptionId ->
getString(descriptionId)
} ?: ""
if (cryptoPrompt.isBiometricOperation) {
mBiometricPrompt?.authenticate(
BiometricPrompt.PromptInfo.Builder().apply {
setTitle(promptTitle)
if (promptDescription.isNotEmpty())
setDescription(promptDescription)
setConfirmationRequired(false)
if (isDeviceCredentialBiometricOperation(context)) {
setAllowedAuthenticators(DEVICE_CREDENTIAL)
} else {
setNegativeButtonText(getString(android.R.string.cancel))
}
}.build(),
BiometricPrompt.CryptoObject(cryptoPrompt.cipher))
} else if (cryptoPrompt.isDeviceCredentialOperation) {
context?.let { context ->
@Suppress("DEPRECATION")
mDeviceCredentialResultLauncher?.launch(
ContextCompat.getSystemService(
context,
KeyguardManager::class.java
)?.createConfirmDeviceCredentialIntent(
promptTitle,
promptDescription
)
)
}
}
} catch (e: Exception) {
Log.e(TAG, "Unable to open prompt", e)
mDeviceUnlockViewModel.setException(e)
}
}
private fun setNotAvailableMode() {
lifecycleScope.launch(Dispatchers.Main) {
showViews(false)
mDeviceUnlockView?.setDeviceUnlockButtonViewClickListener(null)
}
}
private fun openBiometricSetting() {
mDeviceUnlockView?.setDeviceUnlockButtonViewClickListener {
try {
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
context?.startActivity(Intent(Settings.ACTION_BIOMETRIC_ENROLL))
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> {
@Suppress("DEPRECATION") context
?.startActivity(Intent(Settings.ACTION_FINGERPRINT_ENROLL))
}
else -> {
context?.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
}
}
} catch (e: Exception) {
// ACTION_SECURITY_SETTINGS does not contain fingerprint enrollment on some devices...
context?.startActivity(Intent(Settings.ACTION_SETTINGS))
}
}
}
private fun setSecurityUpdateRequiredMode() {
lifecycleScope.launch(Dispatchers.Main) {
showViews(true)
setAdvancedUnlockedTitleView(R.string.biometric_security_update_required)
openBiometricSetting()
}
}
private fun setNotConfiguredMode() {
lifecycleScope.launch(Dispatchers.Main) {
showViews(true)
setAdvancedUnlockedTitleView(R.string.configure_biometric)
openBiometricSetting()
}
}
private fun setKeyManagerNotAvailableMode() {
lifecycleScope.launch(Dispatchers.Main) {
showViews(true)
setAdvancedUnlockedTitleView(R.string.keystore_not_accessible)
openBiometricSetting()
}
}
private fun setWaitCredentialMode() {
lifecycleScope.launch(Dispatchers.Main) {
showViews(true)
setAdvancedUnlockedTitleView(R.string.unavailable)
context?.let { context ->
mDeviceUnlockView?.setDeviceUnlockButtonViewClickListener {
mDeviceUnlockViewModel.setException(SecurityException(
context.getString(R.string.credential_before_click_advanced_unlock_button)
))
}
}
}
}
private fun setStoreCredentialMode() {
lifecycleScope.launch(Dispatchers.Main) {
showViews(true)
setAdvancedUnlockedTitleView(R.string.unlock_and_link_biometric)
context?.let { context ->
mDeviceUnlockView?.setDeviceUnlockButtonViewClickListener { view ->
mDeviceUnlockViewModel.showPrompt()
}
}
}
}
private fun setExtractCredentialMode() {
lifecycleScope.launch(Dispatchers.Main) {
showViews(true)
setAdvancedUnlockedTitleView(R.string.unlock)
context?.let { context ->
mDeviceUnlockView?.setDeviceUnlockButtonViewClickListener { view ->
mDeviceUnlockViewModel.showPrompt()
}
}
}
}
fun deleteEncryptedDatabaseKey() {
mDeviceUnlockViewModel.deleteEncryptedDatabaseKey()
}
private fun showViews(show: Boolean) {
lifecycleScope.launch(Dispatchers.Main) {
if (show) {
if (mDeviceUnlockView?.visibility != View.VISIBLE)
mDeviceUnlockView?.showByFading()
}
else {
if (mDeviceUnlockView?.visibility == View.VISIBLE)
mDeviceUnlockView?.hideByFading()
}
}
}
private fun setAdvancedUnlockedTitleView(textId: Int) {
lifecycleScope.launch(Dispatchers.Main) {
mDeviceUnlockView?.setTitle(textId)
}
}
private fun setAuthenticationError(errorCode: Int, errString: CharSequence) {
Log.e(TAG, "Biometric authentication error. Code : $errorCode Error : $errString")
when (errorCode) {
BiometricPrompt.ERROR_CANCELED,
BiometricPrompt.ERROR_NEGATIVE_BUTTON,
BiometricPrompt.ERROR_USER_CANCELED -> {
// Ignore negative button
}
else ->
mDeviceUnlockViewModel.setException(SecurityException(errString.toString()))
}
}
private fun setAuthenticationFailed() {
Log.e(TAG, "Biometric authentication failed, biometric not recognized")
mDeviceUnlockViewModel.setException(
SecurityException(getString(R.string.advanced_unlock_not_recognized))
)
}
override fun onDestroyView() {
mDeviceUnlockView = null
super.onDestroyView()
}
override fun onDestroy() {
mDeviceUnlockViewModel.disconnect()
super.onDestroy()
}
companion object {
private val TAG = DeviceUnlockFragment::class.java.name
}
}

View File

@@ -0,0 +1,430 @@
/*
* Copyright 2020 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.biometric
import android.app.KeyguardManager
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyPermanentlyInvalidatedException
import android.security.keystore.KeyProperties
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.core.content.ContextCompat
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.settings.PreferencesUtil
import java.security.KeyStore
import java.security.UnrecoverableKeyException
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec
@RequiresApi(api = Build.VERSION_CODES.M)
class DeviceUnlockManager(private var appContext: Context) {
private var keyStore: KeyStore? = null
private var keyGenerator: KeyGenerator? = null
private var cipher: Cipher? = null
private var biometricUnlockEnable = isBiometricUnlockEnable(appContext)
private var deviceCredentialUnlockEnable = isDeviceCredentialUnlockEnable(appContext)
init {
if (biometricUnlockEnable || deviceCredentialUnlockEnable) {
if (isDeviceSecure(appContext)) {
try {
this.keyStore = KeyStore.getInstance(ADVANCED_UNLOCK_KEYSTORE)
this.keyGenerator = KeyGenerator.getInstance(
ADVANCED_UNLOCK_KEY_ALGORITHM,
ADVANCED_UNLOCK_KEYSTORE
)
this.cipher = Cipher.getInstance(
ADVANCED_UNLOCK_KEY_ALGORITHM + "/"
+ ADVANCED_UNLOCK_BLOCKS_MODES + "/"
+ ADVANCED_UNLOCK_ENCRYPTION_PADDING
)
if (keyStore == null) {
throw SecurityException("Unable to initialize the keystore")
}
if (keyGenerator == null) {
throw SecurityException("Unable to initialize the key generator")
}
if (cipher == null) {
throw SecurityException("Unable to initialize the cipher")
}
} catch (e: Exception) {
Log.e(TAG, "Unable to initialize the device unlock manager", e)
throw e
}
} else {
throw SecurityException("Device not secure enough")
}
}
}
@Synchronized private fun getSecretKey(): SecretKey? {
try {
// Create new key if needed
keyStore?.let { keyStore ->
keyStore.load(null)
try {
if (!keyStore.containsAlias(ADVANCED_UNLOCK_KEYSTORE_KEY)) {
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
keyGenerator?.init(
KeyGenParameterSpec.Builder(
ADVANCED_UNLOCK_KEYSTORE_KEY,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(ADVANCED_UNLOCK_BLOCKS_MODES)
.setEncryptionPaddings(ADVANCED_UNLOCK_ENCRYPTION_PADDING)
.apply {
// Require the user to authenticate with a fingerprint to authorize every use
// of the key, don't use it for device credential because it's the user authentication
if (biometricUnlockEnable) {
setUserAuthenticationRequired(true)
}
// To store in the security chip
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& appContext.packageManager.hasSystemFeature(
PackageManager.FEATURE_STRONGBOX_KEYSTORE)) {
setIsStrongBoxBacked(true)
}
}
.build())
keyGenerator?.generateKey()
}
} catch (e: Exception) {
Log.e(TAG, "Unable to create a key in keystore", e)
throw e
}
return keyStore.getKey(ADVANCED_UNLOCK_KEYSTORE_KEY, null) as SecretKey?
}
} catch (e: Exception) {
Log.e(TAG, "Unable to retrieve the key in keystore", e)
throw e
}
return null
}
@Synchronized fun initEncryptData(
actionIfCypherInit: (cryptoPrompt: DeviceUnlockCryptoPrompt) -> Unit
) {
initEncryptData(true, actionIfCypherInit)
}
@Synchronized private fun initEncryptData(
firstLaunch: Boolean,
actionIfCypherInit: (cryptoPrompt: DeviceUnlockCryptoPrompt) -> Unit
) {
try {
getSecretKey()?.let { secretKey ->
cipher?.let { cipher ->
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
actionIfCypherInit.invoke(
DeviceUnlockCryptoPrompt(
type = DeviceUnlockCryptoPromptType.CREDENTIAL_ENCRYPTION,
cipher = cipher,
titleId = R.string.advanced_unlock_prompt_store_credential_title,
descriptionId = R.string.advanced_unlock_prompt_store_credential_message,
isDeviceCredentialOperation = isDeviceCredentialOperation(
deviceCredentialUnlockEnable
),
isBiometricOperation = isBiometricOperation(
biometricUnlockEnable, deviceCredentialUnlockEnable
)
)
)
}
}
} catch (unrecoverableKeyException: UnrecoverableKeyException) {
Log.e(TAG, "Unable to initialize encrypt data", unrecoverableKeyException)
throw unrecoverableKeyException
} catch (invalidKeyException: KeyPermanentlyInvalidatedException) {
Log.e(TAG, "Unable to initialize encrypt data", invalidKeyException)
if (firstLaunch) {
deleteAllEntryKeysInKeystoreForBiometric(appContext)
initEncryptData(false, actionIfCypherInit)
} else {
throw invalidKeyException
}
} catch (e: Exception) {
Log.e(TAG, "Unable to initialize encrypt data", e)
throw e
}
}
@Synchronized fun encryptData(
value: ByteArray,
cipher: Cipher?,
handleEncryptedResult: (encryptedValue: ByteArray, ivSpec: ByteArray) -> Unit
) {
try {
val encrypted = cipher?.doFinal(value) ?: byteArrayOf()
// passes updated iv spec on to callback so this can be stored for decryption
cipher?.parameters?.getParameterSpec(IvParameterSpec::class.java)?.let{ spec ->
handleEncryptedResult.invoke(encrypted, spec.iv)
}
} catch (e: Exception) {
Log.e(TAG, "Unable to encrypt data", e)
throw e
}
}
@Synchronized fun initDecryptData(
ivSpecValue: ByteArray,
actionIfCypherInit: (cryptoPrompt: DeviceUnlockCryptoPrompt) -> Unit
) {
initDecryptData(ivSpecValue, true, actionIfCypherInit)
}
@Synchronized private fun initDecryptData(
ivSpecValue: ByteArray,
firstLaunch: Boolean = true,
actionIfCypherInit: (cryptoPrompt: DeviceUnlockCryptoPrompt) -> Unit
) {
try {
// important to restore spec here that was used for decryption
val spec = IvParameterSpec(ivSpecValue)
getSecretKey()?.let { secretKey ->
cipher?.let { cipher ->
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
actionIfCypherInit.invoke(
DeviceUnlockCryptoPrompt(
type = DeviceUnlockCryptoPromptType.CREDENTIAL_DECRYPTION,
cipher = cipher,
titleId = R.string.advanced_unlock_prompt_extract_credential_title,
descriptionId = null,
isDeviceCredentialOperation = isDeviceCredentialOperation(
deviceCredentialUnlockEnable
),
isBiometricOperation = isBiometricOperation(
biometricUnlockEnable, deviceCredentialUnlockEnable
)
)
)
}
}
} catch (unrecoverableKeyException: UnrecoverableKeyException) {
Log.e(TAG, "Unable to initialize decrypt data", unrecoverableKeyException)
if (firstLaunch) {
deleteKeystoreKey()
initDecryptData(ivSpecValue, false, actionIfCypherInit)
} else {
throw unrecoverableKeyException
}
} catch (invalidKeyException: KeyPermanentlyInvalidatedException) {
Log.e(TAG, "Unable to initialize decrypt data", invalidKeyException)
if (firstLaunch) {
deleteAllEntryKeysInKeystoreForBiometric(appContext)
initDecryptData(ivSpecValue, false, actionIfCypherInit)
} else {
throw invalidKeyException
}
} catch (e: Exception) {
Log.e(TAG, "Unable to initialize decrypt data", e)
throw e
}
}
@Synchronized fun decryptData(
encryptedValue: ByteArray,
cipher: Cipher?,
handleDecryptedResult: (decryptedValue: ByteArray) -> Unit
) {
try {
// actual decryption here
cipher?.doFinal(encryptedValue)?.let { decrypted ->
handleDecryptedResult.invoke(decrypted)
}
} catch (e: Exception) {
Log.e(TAG, "Unable to decrypt data", e)
throw e
}
}
@Synchronized fun deleteKeystoreKey() {
try {
keyStore?.load(null)
keyStore?.deleteEntry(ADVANCED_UNLOCK_KEYSTORE_KEY)
} catch (e: Exception) {
Log.e(TAG, "Unable to delete entry key in keystore", e)
throw e
}
}
companion object {
private val TAG = DeviceUnlockManager::class.java.name
private const val ADVANCED_UNLOCK_KEYSTORE = "AndroidKeyStore"
private const val ADVANCED_UNLOCK_KEYSTORE_KEY = "com.kunzisoft.keepass.biometric.key"
private const val ADVANCED_UNLOCK_KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
private const val ADVANCED_UNLOCK_BLOCKS_MODES = KeyProperties.BLOCK_MODE_CBC
private const val ADVANCED_UNLOCK_ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
@RequiresApi(api = Build.VERSION_CODES.M)
fun canAuthenticate(context: Context): Int {
return try {
BiometricManager.from(context).canAuthenticate(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
&& PreferencesUtil.isDeviceCredentialUnlockEnable(context)) {
BIOMETRIC_STRONG or DEVICE_CREDENTIAL
} else {
BIOMETRIC_STRONG
}
)
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with strong biometric.", e)
try {
BiometricManager.from(context).canAuthenticate(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
&& PreferencesUtil.isDeviceCredentialUnlockEnable(context)) {
BIOMETRIC_WEAK or DEVICE_CREDENTIAL
} else {
BIOMETRIC_WEAK
}
)
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with weak biometric.", e)
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
}
}
}
fun isDeviceSecure(context: Context): Boolean {
return ContextCompat.getSystemService(context, KeyguardManager::class.java)
?.isDeviceSecure ?: false
}
fun biometricUnlockSupported(context: Context): Boolean {
val biometricCanAuthenticate = try {
BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG)
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with strong biometric.", e)
try {
BiometricManager.from(context).canAuthenticate(BIOMETRIC_WEAK)
} catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with weak biometric.", e)
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
}
}
return (biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
)
}
fun deviceCredentialUnlockSupported(context: Context): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val biometricCanAuthenticate = BiometricManager.from(context).canAuthenticate(DEVICE_CREDENTIAL)
(biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
)
} else {
true
}
}
/**
* Remove entry key in keystore
*/
fun deleteEntryKeyInKeystoreForBiometric(
appContext: Context
) {
DeviceUnlockManager(appContext).apply {
deleteKeystoreKey()
}
}
fun deleteAllEntryKeysInKeystoreForBiometric(appContext: Context) {
try {
deleteEntryKeyInKeystoreForBiometric(appContext)
} catch (e: Exception) {
Toast.makeText(appContext,
deviceUnlockError(e, appContext),
Toast.LENGTH_SHORT).show()
} finally {
CipherDatabaseAction.getInstance(appContext).deleteAll()
}
}
}
}
fun deviceUnlockError(error: Exception, context: Context): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& (error is UnrecoverableKeyException
|| error is KeyPermanentlyInvalidatedException)) {
context.getString(R.string.advanced_unlock_invalid_key)
} else
error.cause?.localizedMessage
?: error.localizedMessage
?: error.toString()
}
fun isBiometricUnlockEnable(appContext: Context) =
PreferencesUtil.isBiometricUnlockEnable(appContext)
fun isDeviceCredentialUnlockEnable(appContext: Context) =
PreferencesUtil.isDeviceCredentialUnlockEnable(appContext)
private fun isBiometricOperation(
biometricUnlockEnable: Boolean,
deviceCredentialUnlockEnable: Boolean
): Boolean {
return biometricUnlockEnable
|| isDeviceCredentialBiometricOperation(deviceCredentialUnlockEnable)
}
// Since Android 30, device credential is also a biometric operation
private fun isDeviceCredentialOperation(
deviceCredentialUnlockEnable: Boolean
): Boolean {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.R
&& deviceCredentialUnlockEnable
}
private fun isDeviceCredentialBiometricOperation(
deviceCredentialUnlockEnable: Boolean
): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
&& deviceCredentialUnlockEnable
}
fun isDeviceCredentialBiometricOperation(context: Context?): Boolean {
if (context == null) {
return false
}
return isDeviceCredentialBiometricOperation(
isDeviceCredentialUnlockEnable(context)
)
}

View File

@@ -0,0 +1,11 @@
package com.kunzisoft.keepass.biometric
enum class DeviceUnlockMode {
BIOMETRIC_UNAVAILABLE,
BIOMETRIC_SECURITY_UPDATE_REQUIRED,
DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED,
KEY_MANAGER_UNAVAILABLE,
WAIT_CREDENTIAL,
STORE_CREDENTIAL,
EXTRACT_CREDENTIAL
}

View File

@@ -41,7 +41,7 @@ import com.kunzisoft.keepass.activities.dialogs.ProFeatureDialogFragment
import com.kunzisoft.keepass.activities.dialogs.UnavailableFeatureDialogFragment
import com.kunzisoft.keepass.activities.stylish.Stylish
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
import com.kunzisoft.keepass.biometric.DeviceUnlockManager
import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.services.ClipboardEntryNotificationService
@@ -251,7 +251,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
val tempAdvancedUnlockPreference: TwoStatePreference? = findPreference(getString(R.string.temp_advanced_unlock_enable_key))
val biometricUnlockSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
AdvancedUnlockManager.biometricUnlockSupported(activity)
DeviceUnlockManager.biometricUnlockSupported(activity)
} else false
biometricUnlockEnablePreference?.apply {
// False if under Marshmallow
@@ -296,7 +296,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
}
val deviceCredentialUnlockSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
AdvancedUnlockManager.deviceCredentialUnlockSupported(activity)
DeviceUnlockManager.deviceCredentialUnlockSupported(activity)
} else false
deviceCredentialUnlockEnablePreference?.apply {
// Biometric unlock already checked
@@ -395,7 +395,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
validate?.invoke()
warningAlertDialog?.setOnDismissListener(null)
if (deleteKeys && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
AdvancedUnlockManager.deleteAllEntryKeysInKeystoreForBiometric(activity)
DeviceUnlockManager.deleteAllEntryKeysInKeystoreForBiometric(activity)
}
}
.setNegativeButton(resources.getString(android.R.string.cancel)

View File

@@ -29,7 +29,7 @@ import androidx.preference.PreferenceManager
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.Stylish
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
import com.kunzisoft.keepass.biometric.DeviceUnlockManager
import com.kunzisoft.keepass.database.element.SortNodeEnum
import com.kunzisoft.keepass.database.search.SearchParameters
import com.kunzisoft.keepass.education.Education
@@ -512,7 +512,7 @@ object PreferencesUtil {
return prefs.getBoolean(context.getString(R.string.biometric_unlock_enable_key),
context.resources.getBoolean(R.bool.biometric_unlock_enable_default))
&& (if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
AdvancedUnlockManager.biometricUnlockSupported(context)
DeviceUnlockManager.biometricUnlockSupported(context)
} else {
false
})

View File

@@ -25,15 +25,14 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.Button
import android.widget.LinearLayout
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import com.kunzisoft.keepass.R
@RequiresApi(api = Build.VERSION_CODES.M)
class AdvancedUnlockInfoView @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0)
class DeviceUnlockView @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0)
: LinearLayout(context, attrs, defStyle) {
private var biometricButtonView: Button? = null
@@ -45,7 +44,7 @@ class AdvancedUnlockInfoView @JvmOverloads constructor(context: Context,
biometricButtonView = findViewById(R.id.biometric_button)
}
fun setIconViewClickListener(listener: OnClickListener?) {
fun setDeviceUnlockButtonViewClickListener(listener: OnClickListener?) {
biometricButtonView?.setOnClickListener(listener)
}
@@ -60,14 +59,4 @@ class AdvancedUnlockInfoView @JvmOverloads constructor(context: Context,
fun setTitle(@StringRes textId: Int) {
title = context.getString(textId)
}
fun setMessage(text: CharSequence) {
if (text.isNotEmpty())
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
}
fun setMessage(@StringRes textId: Int) {
Toast.makeText(context, textId, Toast.LENGTH_LONG).show()
}
}

View File

@@ -53,9 +53,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
private var checkboxHardwareView: CompoundButton
private var hardwareKeySelectionView: HardwareKeySelectionView
var onPasswordChecked: (CompoundButton.OnCheckedChangeListener)? = null
var onKeyFileChecked: (CompoundButton.OnCheckedChangeListener)? = null
var onHardwareKeyChecked: (CompoundButton.OnCheckedChangeListener)? = null
var onConditionToStoreCredentialChanged: ((CredentialStorage, verified: Boolean) -> Unit)? = null
var onValidateListener: (() -> Unit)? = null
private var mCredentialStorage: CredentialStorage = CredentialStorage.PASSWORD
@@ -103,24 +101,33 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
handled
}
checkboxPasswordView.setOnCheckedChangeListener { view, checked ->
onPasswordChecked?.onCheckedChanged(view, checked)
checkboxPasswordView.setOnCheckedChangeListener { _, _ ->
onConditionToStoreCredentialChanged?.invoke(
mCredentialStorage,
conditionToStoreCredential()
)
}
checkboxKeyFileView.setOnCheckedChangeListener { view, checked ->
checkboxKeyFileView.setOnCheckedChangeListener { _, checked ->
if (checked) {
if (keyFileSelectionView.uri == null) {
checkboxKeyFileView.isChecked = false
}
}
onKeyFileChecked?.onCheckedChanged(view, checked)
onConditionToStoreCredentialChanged?.invoke(
mCredentialStorage,
conditionToStoreCredential()
)
}
checkboxHardwareView.setOnCheckedChangeListener { view, checked ->
checkboxHardwareView.setOnCheckedChangeListener { _, checked ->
if (checked) {
if (hardwareKeySelectionView.hardwareKey == null) {
checkboxHardwareView.isChecked = false
}
}
onHardwareKeyChecked?.onCheckedChanged(view, checked)
onConditionToStoreCredentialChanged?.invoke(
mCredentialStorage,
conditionToStoreCredential()
)
}
hardwareKeySelectionView.selectionListener = { _ ->

View File

@@ -1,32 +0,0 @@
package com.kunzisoft.keepass.viewmodels
import android.net.Uri
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
class AdvancedUnlockViewModel : ViewModel() {
var allowAutoOpenBiometricPrompt : Boolean = true
var deviceCredentialAuthSucceeded: Boolean? = null
val onInitAdvancedUnlockModeRequested : LiveData<Void?> get() = _onInitAdvancedUnlockModeRequested
private val _onInitAdvancedUnlockModeRequested = SingleLiveEvent<Void?>()
val onUnlockAvailabilityCheckRequested : LiveData<Void?> get() = _onUnlockAvailabilityCheckRequested
private val _onUnlockAvailabilityCheckRequested = SingleLiveEvent<Void?>()
val onDatabaseFileLoaded : LiveData<Uri?> get() = _onDatabaseFileLoaded
private val _onDatabaseFileLoaded = SingleLiveEvent<Uri?>()
fun initAdvancedUnlockMode() {
_onInitAdvancedUnlockModeRequested.call()
}
fun checkUnlockAvailability() {
_onUnlockAvailabilityCheckRequested.call()
}
fun databaseFileLoaded(databaseUri: Uri?) {
_onDatabaseFileLoaded.value = databaseUri
}
}

View File

@@ -0,0 +1,435 @@
package com.kunzisoft.keepass.viewmodels
import android.app.Application
import android.net.Uri
import android.os.Build
import androidx.activity.result.ActivityResult
import androidx.annotation.RequiresApi
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.lifecycle.AndroidViewModel
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.biometric.DeviceUnlockCryptoPrompt
import com.kunzisoft.keepass.biometric.DeviceUnlockCryptoPromptType
import com.kunzisoft.keepass.biometric.DeviceUnlockManager
import com.kunzisoft.keepass.biometric.DeviceUnlockMode
import com.kunzisoft.keepass.database.exception.UnknownDatabaseLocationException
import com.kunzisoft.keepass.model.CipherDecryptDatabase
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.CredentialStorage
import com.kunzisoft.keepass.settings.PreferencesUtil
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import javax.crypto.Cipher
class DeviceUnlockViewModel(application: Application): AndroidViewModel(application) {
var allowAutoOpenBiometricPrompt : Boolean = true
private var cipherDatabaseListener: CipherDatabaseAction.CipherDatabaseListener? = null
private var isConditionToStoreCredentialVerified: Boolean = false
private var deviceUnlockManager: DeviceUnlockManager? = null
private var databaseUri: Uri? = null
private var deviceUnlockMode = DeviceUnlockMode.BIOMETRIC_UNAVAILABLE
var cryptoPrompt: DeviceUnlockCryptoPrompt? = null
// TODO Retrieve credential storage from app database
var credentialDatabaseStorage: CredentialStorage = CredentialStorage.DEFAULT
val cipherDatabaseAction = CipherDatabaseAction.getInstance(getApplication())
private val _uiState = MutableStateFlow(DeviceUnlockState())
val uiState: StateFlow<DeviceUnlockState> = _uiState
fun checkConditionToStoreCredential(condition: Boolean, databaseFileUri: Uri?) {
isConditionToStoreCredentialVerified = condition
checkUnlockAvailability(databaseFileUri)
}
/**
* Check unlock availability by verifying device settings and database mode
*/
fun checkUnlockAvailability() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cipherDatabaseAction.containsCipherDatabase(databaseUri) { containsCipherDatabase ->
if (PreferencesUtil.isBiometricUnlockEnable(getApplication())) {
// biometric not supported (by API level or hardware) so keep option hidden
// or manually disable
val biometricCanAuthenticate = DeviceUnlockManager.canAuthenticate(getApplication())
if (!PreferencesUtil.isAdvancedUnlockEnable(getApplication())
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
changeMode(DeviceUnlockMode.BIOMETRIC_UNAVAILABLE)
} else if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED) {
changeMode(DeviceUnlockMode.BIOMETRIC_SECURITY_UPDATE_REQUIRED)
} else {
// biometric is available but not configured, show icon but in disabled state with some information
if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
changeMode(DeviceUnlockMode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED)
} else {
selectMode(containsCipherDatabase)
}
}
} else if (PreferencesUtil.isDeviceCredentialUnlockEnable(getApplication())) {
if (DeviceUnlockManager.isDeviceSecure(getApplication())) {
selectMode(containsCipherDatabase)
} else {
changeMode(DeviceUnlockMode.DEVICE_CREDENTIAL_OR_BIOMETRIC_NOT_CONFIGURED)
}
}
}
}
}
/**
* Check unlock availability and change the current mode depending of device's state
*/
fun checkUnlockAvailability(databaseFileUri: Uri?) {
databaseUri = databaseFileUri
checkUnlockAvailability()
}
@RequiresApi(Build.VERSION_CODES.M)
fun selectMode(containsCipherDatabase: Boolean) {
try {
if (isConditionToStoreCredentialVerified) {
deviceUnlockManager = DeviceUnlockManager(getApplication())
// listen for encryption
changeMode(DeviceUnlockMode.STORE_CREDENTIAL)
initEncryptData()
} else if (containsCipherDatabase) {
deviceUnlockManager = DeviceUnlockManager(getApplication())
// biometric available but no stored password found yet for this DB
// listen for decryption
changeMode(DeviceUnlockMode.EXTRACT_CREDENTIAL)
initDecryptData()
} else {
// wait for typing
changeMode(DeviceUnlockMode.WAIT_CREDENTIAL)
}
} catch (e: Exception) {
changeMode(DeviceUnlockMode.KEY_MANAGER_UNAVAILABLE)
setException(e)
}
}
fun connect(databaseUri: Uri) {
this.databaseUri = databaseUri
cipherDatabaseListener = object: CipherDatabaseAction.CipherDatabaseListener {
override fun onCipherDatabaseCleared() {
closeBiometricPrompt()
checkUnlockAvailability(databaseUri)
}
}
cipherDatabaseAction.apply {
reloadPreferences()
cipherDatabaseListener?.let {
registerDatabaseListener(it)
}
}
checkUnlockAvailability(databaseUri)
}
fun disconnect() {
this.databaseUri = null
cipherDatabaseListener?.let {
cipherDatabaseAction.unregisterDatabaseListener(it)
}
reset()
}
fun databaseFileLoaded(databaseUri: Uri?) {
// To get device credential unlock result, only if same database uri
if (databaseUri != null
&& PreferencesUtil.isAdvancedUnlockEnable(getApplication())) {
if (databaseUri != this.databaseUri) {
connect(databaseUri)
}
} else {
disconnect()
}
}
@RequiresApi(Build.VERSION_CODES.M)
fun onAuthenticationSucceeded(
activityResult: ActivityResult
) {
cryptoPrompt?.let { prompt ->
when (prompt.type) {
DeviceUnlockCryptoPromptType.CREDENTIAL_ENCRYPTION ->
retrieveCredentialForEncryption( prompt.cipher)
DeviceUnlockCryptoPromptType.CREDENTIAL_DECRYPTION ->
decryptCredential( prompt.cipher)
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
cryptoPrompt?.type?.let { type ->
when (type) {
DeviceUnlockCryptoPromptType.CREDENTIAL_ENCRYPTION ->
retrieveCredentialForEncryption(result.cryptoObject?.cipher)
DeviceUnlockCryptoPromptType.CREDENTIAL_DECRYPTION ->
decryptCredential(result.cryptoObject?.cipher)
}
}
}
private fun retrieveCredentialForEncryption(cipher: Cipher?) {
_uiState.update { currentState ->
currentState.copy(
credentialRequiredCipher = cipher
)
}
}
@RequiresApi(Build.VERSION_CODES.M)
fun encryptCredential(
credential: ByteArray,
cipher: Cipher?
) {
try {
deviceUnlockManager?.encryptData(
value = credential,
cipher = cipher,
handleEncryptedResult = { encryptedValue, ivSpec ->
databaseUri?.let { databaseUri ->
onCredentialEncrypted(
CipherEncryptDatabase().apply {
this.databaseUri = databaseUri
this.credentialStorage = credentialDatabaseStorage
this.encryptedValue = encryptedValue
this.specParameters = ivSpec
}
)
}
}
)
} catch (e: Exception) {
setException(e)
} finally {
// Reinit credential storage request
_uiState.update { currentState ->
currentState.copy(
credentialRequiredCipher = null
)
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
fun decryptCredential(cipher: Cipher?) {
// retrieve the encrypted value from preferences
databaseUri?.let { databaseUri ->
cipherDatabaseAction.getCipherDatabase(databaseUri) { cipherDatabase ->
cipherDatabase?.encryptedValue?.let { encryptedCredential ->
try {
deviceUnlockManager?.decryptData(
encryptedValue = encryptedCredential,
cipher = cipher,
handleDecryptedResult = { decryptedValue ->
// Load database directly with password retrieve
onCredentialDecrypted(
CipherDecryptDatabase().apply {
this.databaseUri = databaseUri
this.credentialStorage = credentialDatabaseStorage
this.decryptedValue = decryptedValue
}
)
cipherDatabaseAction.resetCipherParameters(databaseUri)
}
)
} catch (e: Exception) {
setException(e)
}
} ?: deleteEncryptedDatabaseKey()
}
} ?: run {
setException(UnknownDatabaseLocationException())
}
}
fun onCredentialEncrypted(cipherEncryptDatabase: CipherEncryptDatabase) {
_uiState.update { currentState ->
currentState.copy(
cipherEncryptDatabase = cipherEncryptDatabase
)
}
}
fun consumeCredentialEncrypted() {
_uiState.update { currentState ->
currentState.copy(
cipherEncryptDatabase = null
)
}
}
fun onCredentialDecrypted(cipherDecryptDatabase: CipherDecryptDatabase) {
_uiState.update { currentState ->
currentState.copy(
cipherDecryptDatabase = cipherDecryptDatabase
)
}
}
fun consumeCredentialDecrypted() {
_uiState.update { currentState ->
currentState.copy(
cipherDecryptDatabase = null
)
}
}
fun onPromptRequested(
cryptoPrompt: DeviceUnlockCryptoPrompt,
autoOpen: Boolean = false
) {
this@DeviceUnlockViewModel.cryptoPrompt = cryptoPrompt
if (autoOpen && PreferencesUtil.isAdvancedUnlockPromptAutoOpenEnable(getApplication()))
showPrompt()
}
fun showPrompt() {
_uiState.update { currentState ->
currentState.copy(
cryptoPromptState = DeviceUnlockPromptMode.SHOW
)
}
}
fun promptShown() {
allowAutoOpenBiometricPrompt = false
_uiState.update { currentState ->
currentState.copy(
cryptoPromptState = DeviceUnlockPromptMode.IDLE
)
}
}
fun setException(value: Exception?) {
_uiState.update { currentState ->
currentState.copy(
exception = value
)
}
}
fun exceptionShown() {
_uiState.update { currentState ->
currentState.copy(
exception = null
)
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun initEncryptData() {
try {
deviceUnlockManager?.initEncryptData { cryptoPrompt ->
onPromptRequested(cryptoPrompt)
} ?: setException(Exception("AdvancedUnlockManager not initialized"))
} catch (e: Exception) {
setException(e)
}
}
@RequiresApi(Build.VERSION_CODES.M)
private fun initDecryptData() {
databaseUri?.let { databaseUri ->
cipherDatabaseAction.getCipherDatabase(databaseUri) { cipherDatabase ->
cipherDatabase?.let {
try {
deviceUnlockManager?.initDecryptData(cipherDatabase.specParameters) { cryptoPrompt ->
onPromptRequested(cryptoPrompt, autoOpen = allowAutoOpenBiometricPrompt)
} ?: setException(Exception("AdvancedUnlockManager not initialized"))
} catch (e: Exception) {
setException(e)
}
} ?: deleteEncryptedDatabaseKey()
}
} ?: setException(UnknownDatabaseLocationException())
}
@RequiresApi(Build.VERSION_CODES.M)
private fun changeMode(deviceUnlockMode: DeviceUnlockMode) {
this.deviceUnlockMode = deviceUnlockMode
cipherDatabaseAction.containsCipherDatabase(databaseUri) { containsCipher ->
_uiState.update { currentState ->
currentState.copy(
newDeviceUnlockMode = deviceUnlockMode,
allowAdvancedUnlockMenu = containsCipher
&& deviceUnlockMode != DeviceUnlockMode.BIOMETRIC_UNAVAILABLE
&& deviceUnlockMode != DeviceUnlockMode.KEY_MANAGER_UNAVAILABLE
)
}
}
}
fun deleteEncryptedDatabaseKey() {
closeBiometricPrompt()
databaseUri?.let { databaseUri ->
cipherDatabaseAction.deleteByDatabaseUri(databaseUri) {
checkUnlockAvailability(databaseUri)
}
} ?: checkUnlockAvailability(null)
_uiState.update { currentState ->
currentState.copy(
allowAdvancedUnlockMenu = false
)
}
}
fun closeBiometricPrompt() {
_uiState.update { currentState ->
currentState.copy(
cryptoPromptState = DeviceUnlockPromptMode.CLOSE
)
}
}
fun biometricPromptClosed() {
cryptoPrompt = null
_uiState.update { currentState ->
currentState.copy(
cryptoPromptState = DeviceUnlockPromptMode.IDLE
)
}
}
fun reset() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
changeMode(DeviceUnlockMode.BIOMETRIC_UNAVAILABLE)
}
}
override fun onCleared() {
super.onCleared()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
deviceUnlockManager = null
}
}
}
enum class DeviceUnlockPromptMode {
IDLE, SHOW, CLOSE
}
data class DeviceUnlockState(
val newDeviceUnlockMode: DeviceUnlockMode = DeviceUnlockMode.BIOMETRIC_UNAVAILABLE,
val allowAdvancedUnlockMenu: Boolean = false,
val credentialRequiredCipher: Cipher? = null,
val cipherEncryptDatabase: CipherEncryptDatabase? = null,
val cipherDecryptDatabase: CipherDecryptDatabase? = null,
val cryptoPromptState: DeviceUnlockPromptMode = DeviceUnlockPromptMode.IDLE,
val autoOpenPrompt: Boolean = false,
val exception: Exception? = null
)

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<com.kunzisoft.keepass.view.AdvancedUnlockInfoView
<com.kunzisoft.keepass.view.DeviceUnlockView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/advanced_unlock_view"
android:layout_width="match_parent"

View File

@@ -18,28 +18,28 @@
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
--><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="homepage">الصفحة الرئيسة</string>
<string name="accept">قبول</string>
<string name="add_group">إضافة مجموعة</string>
<string name="encryption">التشفير</string>
<string name="encryption_algorithm">خوارزمية التشفير</string>
<string name="accept">اقبل</string>
<string name="add_group">أضف مجموعة</string>
<string name="encryption">التعمية</string>
<string name="encryption_algorithm">خوارزمية التعمية</string>
<string name="application">التطبيق</string>
<string name="brackets">الأقواس</string>
<string name="extended_ASCII">ASCII ممتد</string>
<string name="allow">سماح</string>
<string name="allow">اسمح</string>
<string name="clipboard_cleared">مُسِحت الحافظة</string>
<string name="clipboard_error_title">خطأ في الحافظة</string>
<string name="clipboard_error_clear">تعذَّر مسح الحافظة</string>
<string name="database">قاعدة البيانات</string>
<string name="decrypting_db">يفك تشفير محتوى قاعدة البيانات…</string>
<string name="decrypting_db">يفك تعمية محتوى قاعدة البيانات…</string>
<string name="digits">أرقام</string>
<string name="entry_cancel">إلغاء</string>
<string name="entry_cancel">ألغِ</string>
<string name="entry_notes">ملاحظات</string>
<string name="entry_confpassword">تأكيد كلمة السر</string>
<string name="entry_confpassword">أكّد كلمة السر</string>
<string name="entry_created">أُنشئ</string>
<string name="entry_modified">معدل</string>
<string name="entry_modified">مُعدل</string>
<string name="entry_not_found">تعذر العثور على بيانات المُدخلة.</string>
<string name="entry_password">كلمة السر</string>
<string name="save">حفظ</string>
<string name="save">احفظ</string>
<string name="entry_title">العنوان</string>
<string name="entry_url">رابط</string>
<string name="entry_user_name">اسم المستخدم</string>
@@ -49,8 +49,8 @@
<string name="error_pass_match">كلمتا السر غير متطابقتين.</string>
<string name="field_name">اسم الحقل</string>
<string name="field_value">قيمة الحقل</string>
<string name="generate_password">توليد كلمة سر</string>
<string name="hint_conf_pass">تأكيد كلمة السر</string>
<string name="generate_password">ولّد كلمة سر</string>
<string name="hint_conf_pass">أكّد كلمة السر</string>
<string name="hint_group_name">اسم المجموعة</string>
<string name="hint_length">الطول</string>
<string name="hint_pass">كلمة السر</string>
@@ -61,15 +61,15 @@
<string name="list_size_summary">حجم النص في قائمة العناصر</string>
<string name="loading_database">يحمل قاعدة البيانات…</string>
<string name="lowercase">حروف صغيرة</string>
<string name="hide_password_summary">إخفاء كلمات السر بشكل افتراضي</string>
<string name="hide_password_summary">أخفِ كلمات السر (***) افتراضيًا</string>
<string name="about">عن التطبيق</string>
<string name="menu_change_key_settings">تغيير المفتاح الرئيسي</string>
<string name="settings">الإعدادات</string>
<string name="menu_app_settings">إعدادات التطبيق</string>
<string name="menu_database_settings">إعدادات قاعدة البيانات</string>
<string name="menu_delete">حذف</string>
<string name="menu_delete">احذف</string>
<string name="menu_donate">التبرع</string>
<string name="menu_edit">تعديل</string>
<string name="menu_edit">عدّل</string>
<string name="menu_lock">اقفل قاعدة البيانات</string>
<string name="menu_open">فتح</string>
<string name="menu_search">البحث</string>
@@ -80,7 +80,7 @@
<string name="progress_create">إنشاء قاعدة بيانات جديدة …</string>
<string name="protection">الحماية</string>
<string name="read_only">محمي من التعديل</string>
<string name="content_description_remove_from_list">إزالة</string>
<string name="content_description_remove_from_list">أزل</string>
<string name="root">الجذر</string>
<string name="memory_usage">استخدام الذاكرة</string>
<string name="parallelism">التَّوازِي</string>
@@ -107,20 +107,19 @@
<string name="feedback">أرسل انطباعاتك</string>
<string name="about_description">\"KeePassDX\" هو تطبيق أندرويد لمدير كلمات المرور كي باس \"KeePass\"</string>
<string name="add_entry">أضف مدخل</string>
<string name="edit_entry">تحرير مدخل</string>
<string name="edit_entry">عدّل مدخل</string>
<string name="key_derivation_function">وظيفة اشتقاق المفتاح</string>
<string name="app_timeout">المهلة</string>
<string name="app_timeout_summary">مدة الخمول قبل قفل قاعدة البيانات</string>
<string name="file_manager_install_description">مدير الملفات الذي يمكنه القيام بالإجراءين ACTION_CREATE_DOCUMENT و ACTION_OPEN_DOCUMENT ضروري لانشاء, وفتح وحفض قواعد البيانات.</string>
<string name="file_manager_install_description">مدير الملفات الذي يمكنه القيام بالإجراءين ACTION_CREATE_DOCUMENT و ACTION_OPEN_DOCUMENT ضروري لانشاء، وفتح وحفظ قواعد البيانات.</string>
<string name="clipboard_error">بعض الأجهزة لا تسمح للتطبيقات باستعمال الحافظة.</string>
<string name="clipboard_timeout">مهلة الحافظة</string>
<string name="clipboard_timeout_summary">مدة التخزين في الحافظة(إذا كان جهازك يدعمها)</string>
<string name="clipboard_timeout_summary">مدة التخزين في الحافظة (إذا كان جهازك يدعمها)</string>
<string name="select_to_copy">اختر لنسخ %1$s إلى الحافظة</string>
<string name="retrieving_db_key">يجلب مفتاح قاعدة البيانات…</string>
<string name="default_checkbox">استخدامها كقاعدة بيانات افتراضية</string>
<string name="html_about_licence">KeePassDX © %1$d كونزيسوفت &lt;strong&gt;مفتوح المصدر&lt;/strong&gt; و &lt;strong&gt;بدون اعلانات&lt;/strong&gt;.
\n يوزع كما هو، بدون ضمان, تحت ترخيص &lt;strong&gt;GPLv3&lt;/strong&gt;.</string>
<string name="entry_accessed">نُفذ إليه</string>
<string name="html_about_licence">KeePassDX © %1$d كونزيسوفت &lt;strong&gt;مفتوح المصدر&lt;/strong&gt; و &lt;strong&gt;بدون إعلانات&lt;/strong&gt;. \n يوزع كما هو، دون ضمان، تحت ترخيص &lt;strong&gt;GPLv3&lt;/strong&gt;.</string>
<string name="entry_accessed">وُصِل إليه</string>
<string name="entry_expires">تنتهي صلاحيته في</string>
<string name="entry_keyfile">ملف المفتاح</string>
<string name="error_arc4">تشفير دفق Arcfour غير مدعوم.</string>
@@ -129,29 +128,29 @@
<string name="error_nokeyfile">اختر ملف مفتاح.</string>
<string name="error_out_of_memory">لا ذاكرة لتحميل قاعدة البيانات كاملة.</string>
<string name="error_load_database">تعذر تحميل قاعدة البيانات.</string>
<string name="error_load_database_KDF_memory">لا يمكن تحميل المفتاح، حاول تقليل \"الذاكرة المستخدمة\" من قبل KDF.</string>
<string name="error_load_database_KDF_memory">تعذر تحميل المفتاح. حاول تقليل \"الذاكرة المستخدمة\" من قِبل KDF.</string>
<string name="error_pass_gen_type">يجب تحديد نوع واحد على الأقل لتوليد كلمة السر.</string>
<string name="error_rounds_too_large">\"جولات التحويل\" كثيرة جداً. الإعداد إلى 2147483648.</string>
<string name="error_string_key">يجب أن يكون لكل سلسلة اسم حقل.</string>
<string name="error_wrong_length">أدخل عددًا صحيحًا موجبًا في حقل «الطول».</string>
<string name="error_autofill_enable_service">تعذر تمكين خدمة الملء التلقائي.</string>
<string name="file_not_found_content">تعذر إيجاد الملف. جرِّب فتحه من متصفح ملفات.</string>
<string name="file_browser">مدير الملفات.</string>
<string name="file_browser">مدير الملفات</string>
<string name="invalid_credentials">تعذر قراءة الإعتمادات.</string>
<string name="invalid_db_sig">تعذر تمييز نسق قاعدة البيانات.</string>
<string name="keyfile_is_empty">ملف المفتاح فارغ.</string>
<string name="list_entries_show_username_title">أظهر أسماء المستخدمين</string>
<string name="list_entries_show_username_summary">اعرض اسماء المستخدمين في قوائم المدخلات</string>
<string name="hint_generated_password">كلمة السر الموَلدة.</string>
<string name="hint_keyfile">الملف المفتاحي.</string>
<string name="hide_password_title">اخفاء كلمات السر</string>
<string name="list_entries_show_username_summary">يعرض اسماء المستخدمين في قوائم المدخلات</string>
<string name="hint_generated_password">كلمة السر مولّدة</string>
<string name="hint_keyfile">ملف المفتاح</string>
<string name="hide_password_title">أخفِ كلمات السر</string>
<string name="copy_field">نُسخة من %1$s</string>
<string name="menu_copy">نسخ</string>
<string name="menu_move">نقل</string>
<string name="menu_paste">لصق</string>
<string name="menu_cancel">الغاء</string>
<string name="menu_hide_password">اخفاء كلمة السر</string>
<string name="menu_showpass">اظهار كلمة السر</string>
<string name="menu_cancel">ألغِ</string>
<string name="menu_hide_password">أخفِ كلمة السر</string>
<string name="menu_showpass">أظهر كلمة السر</string>
<string name="menu_url">الانتقال الى الرابط</string>
<string name="menu_file_selection_read_only">محمي من التعديل</string>
<string name="menu_open_file_read_and_write">قابل للتعديل</string>
@@ -166,7 +165,7 @@
<string name="unavailable">غير متوفر</string>
<string name="menu_appearance_settings">المظهر</string>
<string name="general">عام</string>
<string name="autofill">ملأ تلقائي</string>
<string name="autofill">الملء التلقائي</string>
<string name="autofill_sign_in_prompt">سجل باستخدام KeePassDX</string>
<string name="set_autofill_service_title">تعيين خدمة الملأ التلقائي الافتراضية</string>
<string name="password_size_title">حجم كلمة السر المولدة</string>
@@ -178,7 +177,7 @@
<string name="clipboard_warning">اذا فشل الحذف التلقائي من الحافظة ,احذف تأريخه يدويا.</string>
<string name="lock_database_screen_off_title">قفل الشاشة</string>
<string name="lock_database_screen_off_summary">اقفل قاعدة البيانات بعد بضع ثوانٍ بمجرد إيقاف تشغيل الشاشة</string>
<string name="biometric_delete_all_key_title">حذف مفاتيح التشفير</string>
<string name="biometric_delete_all_key_title">احذف مفاتيح التعمية</string>
<string name="unavailable_feature_text">لا يمكن بدأ هذه الميزة .</string>
<string name="unavailable_feature_version">هذا الجهاز يعمل بأندرويد %1$s لكن يحتاج نسخة %2$s على الأقل.</string>
<string name="file_name">اسم الملف</string>
@@ -234,10 +233,10 @@
<string name="keyboard_notification_entry_content_title">%1$s متوفر على Magikeyboard</string>
<string name="keyboard_notification_entry_content_text">%1$s</string>
<string name="reset_education_screens_title">إعادة تعيين التلميحات التعليمية</string>
<string name="education_search_title">البحث من خلال الإدخالات</string>
<string name="education_search_title">ابحث من خلال المدخلات</string>
<string name="content_description_open_file">افتح الملف</string>
<string name="content_description_add_entry">إضافة مدخلة</string>
<string name="content_description_add_group">إضافة مجموعة</string>
<string name="content_description_add_entry">أضف مدخل</string>
<string name="content_description_add_group">أضف مجموعة</string>
<string name="content_description_file_information">معلومات الملف</string>
<string name="entry_password_generator">مولد كلمة السر</string>
<string name="content_description_background">الخلفية</string>
@@ -250,22 +249,22 @@
<string name="do_not_kill_app">لا تقتل التطبيق…</string>
<string name="content_description_node_children">العقد الفرعية</string>
<string name="content_description_add_node">أضف عقدة</string>
<string name="content_description_entry_icon">ايقونة المدخل</string>
<string name="content_description_entry_icon">أيقونة المدخل</string>
<string name="content_description_password_length">طول كلمة السر</string>
<string name="entry_add_field">أضف حقل</string>
<string name="content_description_remove_field">أزل حقل</string>
<string name="error_move_entry_here">يتعذر نقل مدخل إلى هنا.</string>
<string name="error_copy_entry_here">يتعذر نسخ مدخال إلى هنا.</string>
<string name="list_groups_show_number_entries_title">عرض عدد المدخلات</string>
<string name="list_groups_show_number_entries_summary">عرض عدد المدخلات في المجموعة</string>
<string name="content_description_update_from_list">تحديث</string>
<string name="error_move_entry_here">لا يمكنك نقل مدخل هنا.</string>
<string name="error_copy_entry_here">لا يمكنك نسخ مدخل هنا.</string>
<string name="list_groups_show_number_entries_title">أظهر عدد المدخلات</string>
<string name="list_groups_show_number_entries_summary">يعرض عدد المدخلات في المجموعة</string>
<string name="content_description_update_from_list">حدِّث</string>
<string name="content_description_keyboard_close_fields">أغلق الحقول</string>
<string name="error_create_database_file">لا يمكن انشاء قاعدة بيانات بكلمة السر وملف المفتاح الحاليين.</string>
<string name="error_create_database_file">تعذر إنشاء قاعدة بيانات بكلمة السر وملف المفتاح الحاليين.</string>
<string name="menu_advanced_unlock_settings">فك قفل الجهاز</string>
<string name="entry_attachments">مرفقات</string>
<string name="entry_history">السجل</string>
<string name="entry_add_attachment">أضف مرفقا</string>
<string name="discard">إلغاء</string>
<string name="entry_history">التاريخ</string>
<string name="entry_add_attachment">أضف مرفقًا</string>
<string name="discard">تجاهل</string>
<string name="discard_changes">تجاهل التغييرات؟</string>
<string name="validate">تأكيد</string>
<string name="security">الأمان</string>
@@ -273,27 +272,27 @@
<string name="error_otp_period">يجب ان تكون المدة بين %1$d و%2$d ثانية.</string>
<string name="error_otp_secret_key">المفتاح السري يجب ان يكون بصيغة Base32.</string>
<string name="error_save_database">تعذر حفظ قاعدة البيانات.</string>
<string name="error_create_database">لا يمكن إنشاء ملف قاعدة البيانات.</string>
<string name="error_copy_group_here">لا يمكن نسخ مجموعة هنا.</string>
<string name="error_create_database">تعذر إنشاء ملف قاعدة البيانات.</string>
<string name="error_copy_group_here">لا يمكنك نسخ مجموعة هنا.</string>
<string name="error_label_exists">هذه التسمية موجودة بالفعل.</string>
<string name="otp_period">المدة (ثواني)</string>
<string name="otp_algorithm">الخوارزمية</string>
<string name="otp_digits">أرقام</string>
<string name="otp_counter">العداد</string>
<string name="entry_setup_otp">عيّن كلمة مرور لمرة واحدة</string>
<string name="entry_setup_otp">عيّن كلمة سر لمرة واحدة</string>
<string name="entry_UUID">UUID</string>
<string name="html_about_contribution">من أجل &lt;strong&gt;حماية خصوصيتا&lt;/strong&gt;٫&lt;strong&gt; إصلاح العلل&lt;/strong&gt;٫ &lt;strong&gt;إضافة مميزات&lt;/strong&gt; &lt;strong&gt;وجعلنا نشطاء دائما&lt;/strong&gt;٫ نحن نعتمد على &lt;strong&gt;مساهمتك&lt;/strong&gt;.</string>
<string name="content_description_keyfile_checkbox">خانة تأشير الملف المفتاحي</string>
<string name="html_about_contribution">لكي &lt;strong&gt;نحافظ على حريتنا&lt;/strong&gt;، و&lt;strong&gt;نصلح الأخطاء&lt;/strong&gt;، و&lt;strong&gt;نضيف ميزات&lt;/strong&gt;، و&lt;strong&gt;نبقى دائمًا نشطين&lt;/strong&gt;، فإننا نعتمد على &lt;strong&gt;مساهمتكم&lt;/strong&gt;.</string>
<string name="content_description_keyfile_checkbox">خانة تأشير ملف المفتاح</string>
<string name="content_description_password_checkbox">خانة تأشير كلمة السر</string>
<string name="content_description_add_item">أضف عنصر</string>
<string name="warning_permanently_delete_nodes">حذف العقد المحددة نهائيا؟</string>
<string name="filter">مرشح</string>
<string name="command_execution">ينفذ الأمر…</string>
<string name="hide_broken_locations_title">اِخفي روابط قواعد البيانات المعطلة</string>
<string name="hide_broken_locations_title">أخفِ روابط قواعد البيانات المعطوبة</string>
<string name="show_recent_files_summary">أظهر موقع قواعد البيانات الأخيرة</string>
<string name="show_recent_files_title">أظهر الملفات الأخيرة</string>
<string name="remember_keyfile_locations_summary">تعقب موقع الملفات المفتاحية لقاعدة البيانات</string>
<string name="remember_keyfile_locations_title">تذكر موقع الملف المفتاحي</string>
<string name="remember_keyfile_locations_title">تذكر موقع ملف المفتاح</string>
<string name="remember_database_locations_summary">تعقب موقع قاعدة البيانات</string>
<string name="remember_database_locations_title">تذكر موقع تخزين قاعدة البيانات</string>
<string name="contains_duplicate_uuid_procedure">للمتابعة هل تريد حل المشكلة بتوليد UUID للعناصر المكررة ؟</string>
@@ -308,42 +307,42 @@
<string name="creating_database">ينشئ قاعدة البيانات…</string>
<string name="error_string_type">لا يطابق هذا النص العنصر المطلوب.</string>
<string name="error_otp_counter">على العداد أن يكون ما بين %1$d و %2$d.</string>
<string name="entry_otp">كلمة مرور لمرة واحدة</string>
<string name="otp_type">نوع كلمة المرور لمرة واحدة</string>
<string name="entry_otp">كلمة سر لمرة واحدة</string>
<string name="otp_type">نوع كلمة السر لمرة واحدة (OTP)</string>
<string name="error_disallow_no_credentials">عين اعتماد واحد على الأقل.</string>
<string name="contribution">ساهم</string>
<string name="contact">الإتصال بنا</string>
<string name="contact">التواصل</string>
<string name="biometric">البصمة</string>
<string name="warning_empty_keyfile_explanation">يجب ألا تغير محتوى ملف المفتاح، في أحسن الحالات يجب أن يحتوي بيانات مولدة عشوائيا.</string>
<string name="warning_empty_keyfile">من غير المستحسن اضافة ملف مفتاح فارغ.</string>
<string name="warning_sure_remove_data">أزل هذه البيانات عل أي حال؟</string>
<string name="warning_sure_add_file">أأضف الملف على أي حال؟</string>
<string name="warning_replace_file">رفعُ هذا الملف سيستبدل الموجود مسبقا.</string>
<string name="warning_replace_file">رفع هذا الملف سيستبدل الموجود مسبقًا.</string>
<string name="warning_database_link_revoked">أبطلَ مدير الملفات الوصول للملف</string>
<string name="warning_database_read_only">أعط صلاحية الكتابة من أجل حفظ قاعدة البيانات</string>
<string name="warning_password_encoding">تجنب استخدام المحارف غير الموجودة في ترميز قاعدة البيانات (تحوَّل المحارف غير الموجودة لنفس الحرف).</string>
<string name="hide_broken_locations_summary">إخف الروابط المعطلة في قائمة قواعد البيانات الحديثة</string>
<string name="hide_broken_locations_summary">أخفِ الروابط المعطوبة في قائمة قواعد البيانات الحديثة</string>
<string name="auto_focus_search_summary">افتح البحث عند فتح قاعدة البيانات</string>
<string name="content_description_credentials_information">معلومات بيانات الاعتماد</string>
<string name="max_history_items_title">العدد الأقصى</string>
<string name="recycle_bin_group_title">مجموعة سلة المحذوفات</string>
<string name="recycle_bin_summary">أُنقل المجموعات والمدخلات لسلة المحذوفات قبل حذفها</string>
<string name="database_data_remove_unlinked_attachments_summary">أزِل المرفقات غير المرتبطة بإدخال في قاعدة البيانات</string>
<string name="database_data_remove_unlinked_attachments_summary">أزل المرفقات غير المرتبطة بمدخل في قاعدة البيانات</string>
<string name="database_data_remove_unlinked_attachments_title">أزل البيانات غير المرتبطة</string>
<string name="database_data_compression_summary">ضغط البيانات يقلص من حجم قاعدة البيانات</string>
<string name="database_data_compression_title">ضغط البيانات</string>
<string name="data">البيانات</string>
<string name="unavailable_feature_hardware">تعذر العثور على ماسح البصمة.</string>
<string name="biometric_delete_all_key_summary">احذف كل مفاتيح التشفير المرتبطة بفتح الجهاز</string>
<string name="biometric_delete_all_key_summary">احذف كل مفاتيح التعمية المرتبطة بفتح الجهاز</string>
<string name="advanced_unlock_explanation_summary">استخدم إلغاء القفل الجهاز لفتح قاعدة البيانات بسهولة</string>
<string name="lock_database_show_button_summary">يعرض زر القَفل في الواجهة</string>
<string name="lock_database_show_button_title">اعرض زر القَفل</string>
<string name="lock_database_show_button_title">أظهر زر القفل</string>
<string name="lock_database_back_root_summary">قفل قاعدة البيانات عند النقر على زر الرجوع في الشاشة الرئيسية</string>
<string name="lock_database_back_root_title">اضغط على \"رجوع\" للإقفال</string>
<string name="clipboard_explanation_summary">انسخ حقول الإدخال باستخدام الحافظة</string>
<string name="clipboard_explanation_summary">انسخ حقول المدخل باستخدام الحافظة</string>
<string name="database_opened">قاعدة البيانات مفتوحة</string>
<string name="autofill_preference_title">إعدادات الملء التلقائي</string>
<string name="education_entry_edit_title">حرر المدخلة</string>
<string name="education_entry_edit_title">عدّل المدخل</string>
<string name="education_advanced_unlock_summary">لفتح قاعدة البيانات بسرعة اربط كلمة المرور بالبصمة.</string>
<string name="education_search_summary">لإيجاد كلمة المرور، أدخل العنوان أو اسم المستخدم أو محتوى أحد الحقول.</string>
<string name="education_new_node_summary">المدخلات لإدارة معرفاتك الرقمية.
@@ -357,15 +356,15 @@
<string name="autofill_web_domain_blocklist_title">قائمة النطاقات المحظورة</string>
<string name="autofill_application_id_blocklist_summary">منع الملء التلقائي للتطبيقات الموجودة في القائمة</string>
<string name="autofill_application_id_blocklist_title">قائمة التطبيقات المحظورة</string>
<string name="content_description_repeat_toggle_password_visibility">بدِّل ظهور كلمة السر</string>
<string name="hide_expired_entries_summary">لن تعرض المدخلات منتهية الصلاحية</string>
<string name="content_description_repeat_toggle_password_visibility">أعد تبديل ظهور كلمة السر</string>
<string name="hide_expired_entries_summary">لا يتم عرض المدخلات منتهية الصلاحية</string>
<string name="education_read_only_summary">تغيير وضع الافتتاح للجلسة.
\n
\nيمنع \"محمي ضد الكتابة\" التغييرات غير المقصودة في قاعدة البيانات.
\n\"قابل للتعديل\" يتيح لك إضافة أو حذف أو تعديل جميع العناصر كما تريد.</string>
<string name="education_read_only_title">احمي قاعدة البيانات من التعديل</string>
<string name="education_unlock_title">افتح قاعدة البيانات</string>
<string name="education_add_attachment_summary">أضف مرفقا للمدخلة لحفظ بيانات اضافية.</string>
<string name="education_add_attachment_summary">ارفع مرفقًا إلى مدخلك لحفظ البيانات الخارجية الهامة.</string>
<string name="education_add_attachment_title">أضف مرفقا</string>
<string name="autofill_block">احظر الملء التلقائي</string>
<string name="keyboard_previous_database_credentials_title">شاشة بيانات اعتماد قاعدة البيانات</string>
@@ -402,7 +401,7 @@
<string name="education_generate_password_title">أنشئ كلمة سر قوية</string>
<string name="save_mode">وضع الحفظ</string>
<string name="search_mode">وضع البحث</string>
<string name="version">النسخة</string>
<string name="version">النُسخة</string>
<string name="template_group_name">النماذج</string>
<string name="holder">الحامل</string>
<string name="number">الرقم</string>
@@ -410,7 +409,7 @@
<string name="personal_identification_number">PIN</string>
<string name="id_card">بطاقة الهوية</string>
<string name="type">النوع</string>
<string name="cryptocurrency">محفظة عملات مشفرة</string>
<string name="cryptocurrency">محفظة عملات التعموية</string>
<string name="public_key">المفتاح العمومي</string>
<string name="private_key">المفتاح الخاص</string>
<string name="account">الحساب</string>
@@ -418,13 +417,13 @@
<string name="bank_name">اسم المصرف</string>
<string name="secure_note">ملاحظة آمنة</string>
<string name="error_word_reserved">هذه الكلمة محجوزة ولا يمكن استخدامها.</string>
<string name="error_field_name_already_exists">اسم الحقل موجود سلفًا.</string>
<string name="error_file_to_big">الملف الذي ترفعه كبير.</string>
<string name="error_field_name_already_exists">اسم الحقل موجود بالفعل.</string>
<string name="error_file_to_big">الملف الذي تحاول رفعه كبير جدًا.</string>
<string name="error_upload_file">حدث خطأ أثناء رفع الملف.</string>
<string name="error_duplicate_file">بيانات الملف موجودة سلفًا.</string>
<string name="error_duplicate_file">بيانات الملف موجودة بالفعل.</string>
<string name="error_remove_file">حدث خطأ أثناء إزالة بيانات الملف.</string>
<string name="error_start_database_action">حدث خطأ أثناء تنفيذ إجراء على قاعدة البيانات.</string>
<string name="content_description_otp_information">معلومات كلمة المرور لمرة واحدة</string>
<string name="content_description_otp_information">معلومات كلمة السر لمرة واحدة</string>
<string name="membership">العضوية</string>
<string name="name">الاسم</string>
<string name="email">البريد الإلكتروني</string>
@@ -452,8 +451,8 @@
<string name="properties">الخصائص</string>
<string name="token">الرمز</string>
<string name="seed">البذرة</string>
<string name="error_database_uri_null">يتعذر استرداد مسار قاعدة البيانات.</string>
<string name="error_rebuild_list">يتعذر إعادة بناء القائمة بشكل صحيح.</string>
<string name="error_database_uri_null">لا يمكن استرداد URI قاعدة البيانات.</string>
<string name="error_rebuild_list">تعذر إعادة بناء القائمة بشكل صحيح.</string>
<string name="menu_keystore_remove_key">احذف رمز فك القفل الجهاز</string>
<string name="menu_form_filling_settings">ملء النموذج</string>
<string name="menu_reload_database">أعد تحميل البيانات</string>
@@ -474,7 +473,7 @@
<string name="keyboard_previous_fill_in_title">العودة إلى الوراء</string>
<string name="keyboard_previous_lock_title">اقفل قاعدة البيانات</string>
<string name="education_advanced_unlock_title">فتح قاعدة بيانات الجهاز</string>
<string name="hint_icon_name">اسم الأيقونة.</string>
<string name="hint_icon_name">اسم الأيقونة</string>
<string name="autofill_manual_selection_title">اختيار يدوي</string>
<string name="description_app_properties">خصائص KeePassDX لإدارة إعدادات التطبيقات</string>
<string name="warning_empty_recycle_bin">هل تريد حذف جميع العقد نهائيًا من سلة المهملات؟</string>
@@ -482,7 +481,7 @@
<string name="autofill_ask_to_save_data_title">اسأل لحفظ البيانات</string>
<string name="content_description_database_color">لون قاعدة البيانات</string>
<string name="menu_merge_from">ادمج من…</string>
<string name="show_uuid_summary">يعرض \"المعرف العام\" المرتبط بمُدخل او بمجموعة</string>
<string name="show_uuid_summary">يعرض UUID المرتبط بمدخل أو بمجموعة</string>
<string name="expired">انتهت</string>
<string name="tags">الوسوم</string>
<string name="menu_merge_database">ادمج البيانات</string>
@@ -491,10 +490,10 @@
<string name="warning_file_too_big">يفترض بقاعدة البيانات أن تحوي ملفات صغيرة الحجم ( كمفاتيح PGP).
\n
\nبرفع هذا الملف قد يزداد حجم قاعدة البيانات ويضعف أداءها.</string>
<string name="error_move_group_here">يتعذر نقل المجموعة إلى هنا.</string>
<string name="error_move_group_here">لا يمكنك نقل مجموعة هنا.</string>
<string name="menu_save_copy_to">احفظ نسخة إلى…</string>
<string name="searchable">يمكن البحث عنه</string>
<string name="custom_data">بيانات مخصصة</string>
<string name="custom_data">بيانات مخصّصة</string>
<string name="case_sensitive">حساسة لحالة الأحرف</string>
<string name="regex">تعابير نمطية</string>
<string name="enable_keep_screen_on_title">أبقِ الشاشة شغّالة</string>
@@ -512,18 +511,18 @@
<string name="templates_group_uuid_title">مجموعة القوالب</string>
<string name="advanced_unlock_timeout">انتهت مهلة فتح الجهاز</string>
<string name="temp_advanced_unlock_timeout_summary">مهلة استخدام فتح الجهاز قبل حذف محتواها</string>
<string name="advanced_unlock_delete_all_key_warning">أتريد حذف كل مفاتيح التشفير المرتبطة بفتح الجهاز؟</string>
<string name="advanced_unlock_delete_all_key_warning">أتريد حذف كل مفاتيح التعمية المرتبطة بفتح الجهاز؟</string>
<string name="templates">القوالب</string>
<string name="templates_group_enable_title">استخدام القوالب</string>
<string name="notification">الإشعارات</string>
<string name="temp_advanced_unlock_enable_summary">لا تقم بتخزين أي محتوى مشفر لاستخدام إلغاء قفل الجهاز</string>
<string name="temp_advanced_unlock_timeout_title">انتهاء صلاحية فتح الحهاز</string>
<string name="hide_expired_entries_title">إخفاء الإدخالات منتهية الصلاحية</string>
<string name="content_description_hardware_key_checkbox">خانة إختيار مفتاح الجهاز</string>
<string name="hide_expired_entries_title">أخفِ المدخلات منتهية الصلاحية</string>
<string name="content_description_hardware_key_checkbox">خانة إختيار مفتاح العتاد</string>
<string name="content_description_passphrase_word_count">عدد عبارات المرور</string>
<string name="content_description_entry_background_color">لون خلفية المدخل</string>
<string name="passphrase">عبارة المرور</string>
<string name="colorize_password_title">تلوين كلمات المرور</string>
<string name="passphrase">عبارة السر</string>
<string name="colorize_password_title">لوّن كلمات السر</string>
<string name="permission">الإذن</string>
<string name="advanced_unlock_prompt_not_initialized">تعذر تهيئة موجه إلغاء قفل الجهاز.</string>
<string name="biometric_security_update_required">مطلوب تحديث أمان المقاييس الحيوية.</string>
@@ -536,12 +535,12 @@
\nاستخدم طريقة ملء النموذج التي تفضلها.</string>
<string name="html_text_dev_feature_work_hard">نحن نعمل بجد لإصدار هذه الميزة بسرعة.</string>
<string name="autofill_inline_suggestions_summary">حاول عرض اقتراحات الملء التلقائي مباشرة من لوحة مفاتيح متوافقة</string>
<string name="delete_entered_password_summary">يحذف كلمة المرور التي تم إدخالها بعد محاولة الاتصال بقاعدة البيانات</string>
<string name="delete_entered_password_summary">يحذف كلمة السر التي أُدخلت بعد محاولة الاتصال بقاعدة البيانات</string>
<string name="education_lock_summary">اقفل قاعدةبياناتك بسرعة، يمكنك إعداد التطبيق لقفلها بعد فترة، وعند إيقاف تشغيل الشاشة.</string>
<string name="education_sort_title">فرز العنصر</string>
<string name="contribute">ساهِم</string>
<string name="upload_attachment">رفع %1$s</string>
<string name="download_canceled">ألغيت!</string>
<string name="upload_attachment">ارفع %1$s</string>
<string name="download_canceled">أُلغِيَ!</string>
<string name="unit_kibibyte">كيلو بايت</string>
<string name="unit_mebibyte">ميغا بايت</string>
<string name="unit_gibibyte">جيجابت</string>
@@ -557,7 +556,7 @@
<string name="title_case">حالة العنوان</string>
<string name="character_count">عدد الأحرف: %1$d</string>
<string name="style_choose_summary">السمة المستخدمة في التطبيق</string>
<string name="show_entry_colors_summary">يعرض ألوان المقدمة والخلفية لإدخال</string>
<string name="show_entry_colors_summary">يعرض ألوان المقدمة والخلفية للمدخل</string>
<string name="icon_pack_choose_summary">حزمة الأيقونات المستخدمة في التطبيق</string>
<string name="show_entry_colors_title">ألوان الدخول</string>
<string name="device_credential_unlock_enable_title">فتح بيانات اعتماد الجهاز</string>
@@ -574,15 +573,15 @@
<string name="keyboard_previous_fill_in_summary">العودة تلقائيًا إلى لوحة المفاتيح السابقة بعد تنفيذ \"إجراء المفتاح التلقائي\"</string>
<string name="download_attachment">تثبيت %1$s</string>
<string name="html_about_privacy">&lt;strong&gt; لا يتم استرداد أي بيانات مستخدم&lt;/strong&gt;، هذا التطبيق لا يتصل بأي خادم، ويعمل محليًا فقط ويحترم خصوصية المستخدمين تمامًا.</string>
<string name="error_cancel_by_user">ألغى المستخدم.</string>
<string name="show_otp_token_title">إظهار رمز \"الاقتران لمرة واحدة\" OTP</string>
<string name="show_otp_token_summary">إظهار رموز\"الاقتران لمرة واحدة\" في قائمة المدخلات</string>
<string name="error_cancel_by_user">أُلغِيَ بواسطة المستخدم.</string>
<string name="show_otp_token_title">أظهر رمز OTP</string>
<string name="show_otp_token_summary">يعرض رموز OTP في قائمة المدخلات</string>
<string name="warning_database_already_opened">قاعدة البيانات مفتوحة بالفعل، أغلقها أولاً لفتح قاعدة البيانات الجديدة</string>
<string name="warning_database_info_reloaded">ستؤدي إعادة تحميل قاعدة البيانات إلى حذف البيانات المعدلة محليًا.</string>
<string name="templates_group_enable_summary">استخدم القوالب الديناميكية لملء حقول الإدخال</string>
<string name="templates_group_enable_summary">استخدم القوالب الديناميكية لملء حقول المدخل</string>
<string name="keyboard_auto_go_action_summary">إجراء مفتاح \"Go\" بعد الضغط على مفتاح \"Field\"</string>
<string name="allow_no_password_summary">يسمح بالنقر فوق الزر \"فتح\" إذا لم يتم تحديد بيانات اعتماد</string>
<string name="education_generate_password_summary">أنشئ كلمة مرور قوية لربطها بإدخالك، وحددها بسهولة وفقًا لمعايير النموذج ولا تنس كلمة المرور الآمنة.</string>
<string name="education_generate_password_summary">أنشئ كلمة سر قوية لربطها بإدخالك، وحددها بسهولة وفقًا لمعايير النموذج ولا تنسَ كلمة السر الآمنة.</string>
<string name="education_setup_OTP_title">قم بإعداد OTP</string>
<string name="style_brightness_title">سطوع السمة</string>
<string name="word_separator">الفاصل</string>
@@ -597,26 +596,26 @@
<string name="kdf_explanation">لإنشاء مفتاح خوارزمية التشفير، يتحول المفتاح الرئيسي باستخدام وظيفة اشتقاق مفتاح مملح عشوائيًا.</string>
<string name="html_text_dev_feature_buy_pro">بشراء الإصدار &lt;strong&gt; pro &lt;/strong&gt;،</string>
<string name="auto_type">كتابة تلقائيًا</string>
<string name="hardware_key">مفتاح الجهاز</string>
<string name="hardware_key">مفتاح العتاد</string>
<string name="advanced_unlock_prompt_store_credential_title">رابط لفتح الجهاز</string>
<string name="backspace">فراغ للخلف</string>
<string name="enter">دخول</string>
<string name="education_sort_summary">اختر كيفية فرز الإدخالات والمجموعات.</string>
<string name="education_sort_summary">اختر كيفية فرز المدخلات والمجموعات.</string>
<string name="html_text_feature_generosity">هذا &lt;strong&gt; النمط المرئي&lt;/strong&gt; متاح بفضل كرمك.</string>
<string name="info">المعلومات</string>
<string name="waiting_challenge_response">في انتظار استجابة التحدي…</string>
<string name="bank_identifier_code">SWIFT / BIC</string>
<string name="international_bank_account_number">IBAN</string>
<string name="error_no_hardware_key">حدد مفتاح الجهاز.</string>
<string name="colorize_password_summary">تلوين أحرف كلمة المرور حسب النوع</string>
<string name="enable_keep_screen_on_summary">استمر في تشغيل الشاشة عند مشاهدة إدخال أو تعديله</string>
<string name="error_no_hardware_key">حدّد مفتاح العتاد.</string>
<string name="colorize_password_summary">لوّن أحرف كلمة السر حسب النوع</string>
<string name="enable_keep_screen_on_summary">استمر في تشغيل الشاشة عند مشاهدة مدخل أو تعديله</string>
<string name="enable_screenshot_mode_title">وضع لقطة الشاشة</string>
<string name="navigation_drawer_open">درج التنقل مفتوح</string>
<string name="waiting_challenge_request">في انتظار طلب التحدي…</string>
<string name="navigation_drawer_close">درج التنقل مقفول</string>
<string name="error_XML_malformed">XML تالف.</string>
<string name="error_otp_type">لم يتم التعرف على نوع OTP الحالي من خلال هذا النموذج، وقد لا يؤدي التحقق من صحته إلى إنشاء الرمز المميز بشكل صحيح.</string>
<string name="error_challenge_already_requested">التحدي مطلوب بالفعل.</string>
<string name="error_challenge_already_requested">التحدي طُلَب بالفعل.</string>
<string name="error_response_already_provided">تقدم الرد بالفعل.</string>
<string name="error_no_response_from_challenge">غير قادر على الحصول على رد من التحدي.</string>
<string name="error_driver_required">مطلوب تعريف لـ%1$s.</string>
@@ -625,15 +624,15 @@
<string name="menu_advanced_unlock_settings_summary">القياس الحيوي، بيانات اعتماد الجهاز</string>
<string name="menu_database_settings_summary">البيانات الوصفية، سلة المحذوفات، القوالب، التاريخ</string>
<string name="menu_security_settings_summary">التشفير، وظيفة اشتقاق المفتاح</string>
<string name="error_hardware_key_unsupported">مفتاح الجهاز غير مدعوم.</string>
<string name="error_hardware_key_unsupported">مفتاح العتاد غير مدعوم.</string>
<string name="master_key_settings_summary">التغيير والتجديد</string>
<string name="error_empty_key">لا يمكن أن يكون المفتاح فارغًا.</string>
<string name="corrupted_file">ملف تالف.</string>
<string name="warning_keyfile_integrity">لا يتم ضمان تجزئة الملف لأن Android يمكنه تغيير بياناته بسرعة. قم بتغيير امتداد الملف إلى bin. من أجل التكامل الصحيح.</string>
<string name="invalid_db_same_uuid">%1$s بنفس UUID %2$s موجود بالفعل.</string>
<string name="remember_hardware_key_title">تذكر مفاتيح الأجهزة</string>
<string name="remember_hardware_key_title">تذكر مفاتيح العتاد</string>
<string name="warning_exact_alarm">لم تسمح للتطبيق باستخدام منبه دقيق. نتيجة لذلك، لن يتم تنفيذ الميزات التي تتطلب مؤقتًا في وقت محدد.</string>
<string name="remember_hardware_key_summary">يتتبع مفاتيح الأجهزة المستخدمة</string>
<string name="remember_hardware_key_summary">يتتبع مفاتيح العتاد المستخدمة</string>
<string name="warning_database_notification_permission">يسمح لك إذن الإشعار بعرض حالة قاعدة البيانات وقفلها باستخدام زر يسهل الوصول إليه.
\n
\nإذا لم تنشط هذا الإذن، فلن تكون قاعدة البيانات المفتوحة في الخلفية مرئية إذا كان هناك تطبيق آخر في المقدمة.</string>
@@ -646,10 +645,9 @@
<string name="advanced_unlock_prompt_extract_credential_message">استخراج بيانات اعتماد قاعدة البيانات مع بيانات فتح الجهاز</string>
<string name="ask">إسأل</string>
<string name="configure_biometric">لم تسجل بيانات اعتماد المقاييس الحيوية أو الجهاز.</string>
<string name="show_uuid_title">إظهار \"المعرف العام المميز\" UUID</string>
<string name="show_uuid_title">أظهر \"المعرّف العام المميز\" UUID</string>
<string name="unlock_and_link_biometric">رابط فتح الجهاز</string>
<string name="advanced_unlock_invalid_key">لا يمكن قراءة مفتاح فتح الجهاز. يرجى حذفه وتكرار إجراء التعرف على الفتح.</string>
<string name="advanced_unlock_scanning_error">خطأ في فتح الجهاز: %1$s</string>
<string name="menu_appearance_settings_summary">المظاهر والألوان والسمات</string>
<string name="autofill_explanation_summary">تمكين الملء التلقائي لملء النماذج بسرعة في التطبيقات الأخرى</string>
<string name="device_credential_unlock_enable_summary">يتيح لك استخدام بيانات اعتماد جهازك لفتح قاعدة البيانات</string>
@@ -662,14 +660,12 @@
\nاعتمادًا على تطبيق API الأصلي لنظام التشغيل، قد لا يعمل بكامل طاقته.
\n
\nتحقق من توافق وأمن KeyStore مع الشركة المصنعة لجهازك ومنشئ ROM الذي تستخدمه.</string>
<string name="keyboard_selection_entry_summary">عند عرض إدخال في KeePassDX، عبئ Magikeyboard بهذا الإدخال</string>
<string name="keyboard_selection_entry_summary">عند عرض مدخل في KeePassDX، عبئ Magikeyboard بهذا المدخل</string>
<string name="enable_screenshot_mode_summary">اسمح لتطبيقات الطرف الثالث بتسجيل أو التقاط لقطات شاشة للتطبيق</string>
<string name="keyboard_save_search_info_summary">حاول حفظ المعلومات المشتركة عند إجراء اختيار إدخال يدوي لاستخدامات مستقبلية أسهل</string>
<string name="education_entry_edit_summary">تحرير الإدخال الخاص بك مع الحقول المخصصة. يمكن الرجوع إلى بيانات التجمع بين حقول الإدخال المختلفة.</string>
<string name="education_validate_entry_title">تحقق من صحة الإدخال</string>
<string name="education_validate_entry_summary">تذكر التحقق من صحة الإدخال الخاص بك وحفظ قاعدة البيانات الخاصة بك.
\n
\nإذا تم تنشيط القفل التلقائي ونسيت أنك تجري تعديلاً، فإنك تخاطر بفقدان بياناتك.</string>
<string name="keyboard_save_search_info_summary">حاول حفظ المعلومات المشتركة عند إجراء اختيار مدخل يدوي لاستخدامات مستقبلية أسهل</string>
<string name="education_entry_edit_summary">عدّل إدخالك مع الحقول المخصّصة. يمكن الرجوع إلى بيانات التجمع بين حقول مدخل المختلفة.</string>
<string name="education_validate_entry_title">تحقق من صحة المدخل</string>
<string name="education_validate_entry_summary">تذكر التحقق من صحة إدخالك وحفظ قاعدة بياناتك. \n \nإذا القفل التلقائي مُنشّط ونسيت أنك تجري تعديلاً، فإنك تخاطر بفقدان بياناتك.</string>
<string name="education_entry_new_field_summary">قم بتسجيل حقل إضافي، أضف قيمة وقم بحمايته بشكل اختياري.</string>
<string name="education_unlock_summary">أدخل كلمة المرور و/أو ملف المفتاح لفتح قاعدة بياناتك.
\n
@@ -697,10 +693,10 @@
<string name="style_name_follow_system">اتبع النظام</string>
<string name="style_name_light">فاتح</string>
<string name="hide_templates_summary">لا يتم عرض القوالب</string>
<string name="generate_keyfile">ولّد ملف المفتاح</string>
<string name="generate_keyfile">ولّد ملف مفتاح</string>
<string name="nodes">العُقد</string>
<string name="recursive_number_entries_title">عدد متكرر من الإدخالات</string>
<string name="recursive_number_entries_summary">يحسب بشكل متكرر عدد الإدخالات في المجموعة</string>
<string name="recursive_number_entries_title">عدد متكرر من المدخلات</string>
<string name="recursive_number_entries_summary">يحسب بشكل متكرر عدد المدخلات في المجموعة</string>
<string name="warning_large_keyfile">لا يُنصح بإضافة ملف مفتاحي كبير، فقد يؤدي هذا إلى منع فتح قاعدة البيانات.</string>
<string name="hide_templates_title">أخفِ القوالب</string>
</resources>

View File

@@ -199,7 +199,6 @@
<string name="keystore_not_accessible">Açar ehtiyyatı düzgün formada başladılmadı.</string>
<string name="advanced_unlock_prompt_store_credential_title">Cihaz kilidini açma linki</string>
<string name="database_history">Tarixçə</string>
<string name="advanced_unlock_scanning_error">Cihaz kilidini açma xətası: %1$s</string>
<string name="warning_database_info_reloaded">Məlumat bazasını yenidən yükləmək lokal olaraq modifikasiya olunmuş faylları siləcəkdir.</string>
<string name="warning_database_revoked">Fayla giriş fayl meneceri tərəfindən ləğv edildi, məlumat bazasını bağlayın və onu olduğu yerdən yenidən açın.</string>
<string name="warning_exact_alarm">Siz tətəbiqin zəngli saatdan istifadə etməsinə icazə verməmisiniz. Nəticədə, taymer tələb edən funksiyalar dəqiq bir zamanda işləməyəckdir.</string>

View File

@@ -536,7 +536,6 @@
<string name="recycle_bin_title">Използване на кошчето</string>
<string name="recycle_bin_summary">Премества групите и записите в групата „Кошче“ вместо да ги премахва директно</string>
<string name="advanced_unlock_prompt_store_credential_title">Отключване с устройството</string>
<string name="advanced_unlock_scanning_error">Грешка при отключване на устройството: %1$s</string>
<string name="advanced_unlock_not_recognized">Не може да бъде разпознато кога устройството е отключено</string>
<string name="advanced_unlock_prompt_not_initialized">Заявката за отключване не може да бъде подготвена.</string>
<string name="password_size_summary">Подразбирана дължина на създаваните пароли</string>

View File

@@ -613,7 +613,6 @@
<string name="configure">Configura</string>
<string name="biometric_security_update_required">Cal actualitzar la seguretat biomètrica.</string>
<string name="unlock_and_link_biometric">Enllaç de desbloqueig del dispositiu</string>
<string name="advanced_unlock_scanning_error">Error en desbloquejar el dispositiu: %1$s</string>
<string name="unavailable">No disponible</string>
<string name="advanced_unlock_prompt_not_initialized">No s\'ha pogut inicialitzar l\'indicador de desbloqueig del dispositiu.</string>
<string name="credential_before_click_advanced_unlock_button">Escriviu la contrasenya i, a continuació, feu clic en aquest botó.</string>

View File

@@ -266,7 +266,7 @@
<string name="html_text_ad_free">Na rozdíl od mnoha aplikací pro správu hesel je tato &lt;strong&gt;bez reklam&lt;/strong&gt;, je &lt;strong&gt;svobodný software pod copyleft licencí&lt;/strong&gt; a nesbírá žádné osobní údaje na svých serverech bez ohledu na to, jakou verzi používáte.</string>
<string name="html_text_buy_pro">Zakoupením varianty \"pro\" získáte přístup k tomuto &lt;strong&gt;vizuálnímu stylu&lt;/strong&gt; a hlavně pomůžete &lt;strong&gt;uskutečnění komunitních projektů.&lt;/strong&gt;</string>
<string name="html_text_feature_generosity">Tento &lt;strong&gt;vizuální styl&lt;/strong&gt; je k dispozici díky vaší štědrosti.</string>
<string name="html_text_donation">&lt;strong&gt;Přispěním&lt;/strong&gt; projektu <i>(peněžně, kódem, překlady)</i> mu pomůžete žít a prosperovat a dostanete přístup k postupu odemčení &lt;strong&gt;motivů&lt;/strong&gt;.</string>
<string name="html_text_donation">&lt;strong&gt;Přispěním&lt;/strong&gt; do projektu <i>(peněžně, kódem, překlady)</i> mu pomůžete žít a prosperovat a dostanete přístup k postupu odemčení &lt;strong&gt;motivů&lt;/strong&gt;.</string>
<string name="html_text_dev_feature">Tato funkce je &lt;strong&gt;ve vývoji&lt;/strong&gt; a potřebuje Váš &lt;strong&gt;příspěvek&lt;/strong&gt;, aby byla brzy k dispozici.</string>
<string name="html_text_dev_feature_buy_pro">Zakoupením &lt;strong&gt;pro&lt;/strong&gt; varianty,</string>
<string name="html_text_dev_feature_contibute">&lt;strong&gt;Podpořením vývoje&lt;/strong&gt;,</string>
@@ -484,7 +484,7 @@
<string name="configure_biometric">Žádné přihlašovací ani biometrické údaje nejsou registrovány.</string>
<string name="warning_empty_recycle_bin">Trvale odstranit všechny uzly z koše\?</string>
<string name="registration_mode">Registrace</string>
<string name="save_mode">Uložit</string>
<string name="save_mode">Režim ukládání</string>
<string name="search_mode">Vyhledávání</string>
<string name="error_field_name_already_exists">Jméno kolonky již existuje.</string>
<string name="error_registration_read_only">Uložení nové položky v režimu databáze pouze pro čtení není dovoleno.</string>
@@ -499,7 +499,6 @@
<string name="device_credential">Heslo zařízení</string>
<string name="credential_before_click_advanced_unlock_button">Zadejte heslo a pak klepněte na toto tlačítko.</string>
<string name="advanced_unlock_prompt_not_initialized">Nepodařilo se inicializovat nabídku pro odemykání zařízení.</string>
<string name="advanced_unlock_scanning_error">Chyba při odemykání zařízení: %1$s</string>
<string name="advanced_unlock_not_recognized">Otisk pro odemykání zařízení nebyl rozpoznán</string>
<string name="advanced_unlock_invalid_key">Nepodařilo se načíst klíč odemykání zařízení. Odstraňte ho a opakujte proces rozpoznání odemknutí.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Načíst údaj z databáze pomocí dat odemykání zařízení</string>

View File

@@ -507,7 +507,6 @@
<string name="content">Indhold</string>
<string name="credential_before_click_advanced_unlock_button">Indtast adgangskoden, og klik derefter på denne knap.</string>
<string name="advanced_unlock_prompt_not_initialized">Kunne ikke initialisere oplåsningsprompt.</string>
<string name="advanced_unlock_scanning_error">Fejl ved oplåsning: %1$s</string>
<string name="advanced_unlock_not_recognized">Kunne ikke genkende aftryk til oplåsning</string>
<string name="advanced_unlock_invalid_key">Oplåsningsnøgle kan ikke læses. Slet den og gentag proceduren for genkendelse af oplåsning.</string>
<string name="advanced_unlock_prompt_extract_credential_title">Enhedsoplåsningsgenkendelse</string>

View File

@@ -529,7 +529,6 @@
<string name="device_credential">Geräteanmeldedaten</string>
<string name="credential_before_click_advanced_unlock_button">Passwort eingeben und dann diese Taste drücken.</string>
<string name="advanced_unlock_prompt_not_initialized">Geräteentsperrungsabfrage konnte nicht gestartet werden.</string>
<string name="advanced_unlock_scanning_error">Fehler bei Geräteentsperrung: %1$s</string>
<string name="advanced_unlock_not_recognized">Fingerabdruck für Geräteentsperrung wurde nicht erkannt</string>
<string name="advanced_unlock_invalid_key">Der Geräteentsperrschlüssel ist nicht lesbar. Bitte diesen löschen und den Vorgang zur Entsperr-Erkennung wiederholen.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Datenbankanmeldedaten aus Geräteentsperrdaten gewinnen</string>

View File

@@ -503,7 +503,6 @@
<string name="credential_before_click_advanced_unlock_button">Πληκτρολογήστε τον κωδικό πρόσβασης, και στη συνέχεια κάντε κλικ αυτό το κουμπί.</string>
<string name="advanced_unlock_prompt_not_initialized">Δεν είναι δυνατή η προετοιμασία της προτροπής ξεκλειδώματος συσκευής.</string>
<string name="advanced_unlock_not_recognized">Δεν ήταν δυνατή η αναγνώριση αποτυπώματος ξεκλειδώματος συσκευής</string>
<string name="advanced_unlock_scanning_error">Σφάλμα ξεκλειδώματος συσκευής: %1$s</string>
<string name="advanced_unlock_invalid_key">Δεν είναι δυνατή η ανάγνωση του κλειδιού ξεκλειδώματος της συσκευής. Διαγράψτε το και επαναλάβετε τη διαδικασία αναγνώρισης ξεκλειδώματος.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Εξαγωγή διαπιστευτηρίων βάσης δεδομένων με δεδομένα ξεκλειδώματος συσκευής</string>
<string name="education_advanced_unlock_summary">Συνδέστε τον κωδικό πρόσβασής σας με το σαρωμένο βιομετρικό ή τα διαπιστευτήρια της συσκευής σας για να ξεκλειδώσετε γρήγορα τη βάση δεδομένων σας.</string>

View File

@@ -5,8 +5,8 @@
<string name="colorize_password_summary">Colourise password characters by type</string>
<string name="content_description_entry_background_color">Entry background colour</string>
<string name="invalid_db_sig">Could not recognise the database format.</string>
<string name="advanced_unlock_not_recognized">Could not recognise advanced unlock print</string>
<string name="advanced_unlock_prompt_not_initialized">Unable to initialise advanced unlock prompt.</string>
<string name="advanced_unlock_not_recognized">Could not recognise device unlock print</string>
<string name="advanced_unlock_prompt_not_initialized">Unable to initialise device unlock prompt.</string>
<string name="download_initialization">Initialising…</string>
<string name="download_finalization">Finalising…</string>
<string name="download_canceled">Cancelled!</string>
@@ -33,10 +33,10 @@
<string name="encryption">Encryption</string>
<string name="contact">Contact</string>
<string name="contribution">Contribution</string>
<string name="about_description">password</string>
<string name="encryption_algorithm">Encryption</string>
<string name="about_description">Android implementation of the KeePass password manager</string>
<string name="encryption_algorithm">Encryption algorithm</string>
<string name="app_timeout">Timeout</string>
<string name="app_timeout_summary">database</string>
<string name="app_timeout_summary">Idle time before locking the database</string>
<string name="application">App</string>
<string name="brackets">Brackets</string>
<string name="extended_ASCII">Extended ASCII</string>

View File

@@ -73,7 +73,7 @@
<string name="hint_conf_pass">Confirmar contraseña</string>
<string name="hint_generated_password">Contraseña generada</string>
<string name="hint_group_name">Nombre del grupo</string>
<string name="hint_keyfile">Cerrojo</string>
<string name="hint_keyfile">Archivo de clave</string>
<string name="hint_length">Longitud</string>
<string name="password">Contraseña</string>
<string name="hint_pass">Contraseña</string>
@@ -118,7 +118,7 @@
<string name="unsupported_db_version">Versión de base de datos incompatible.</string>
<string name="uppercase">Mayúsculas</string>
<string name="version_label">Versión %1$s</string>
<string name="education_unlock_summary">Introduzca la contraseña y/o el cerrojo para desbloquear su base de datos.\n\nRespalde su base de datos en un lugar seguro tras cada cambio.</string>
<string name="education_unlock_summary">Introduzca la contraseña y/o el archivo de clave para desbloquear su base de datos.\n\nRespalde su base de datos en un lugar seguro tras cada cambio.</string>
<string-array name="list_size_options">
<item>Pequeño</item>
<item>Mediano</item>
@@ -137,7 +137,7 @@
<string name="field_value">Valor del campo</string>
<string name="file_not_found_content">No se ha podido encontrar el archivo. Intente volver a abrirlo en el explorador de archivos.</string>
<string name="invalid_algorithm">Algoritmo incorrecto.</string>
<string name="keyfile_is_empty">El cerrojo está vacío.</string>
<string name="keyfile_is_empty">El archivo de clave está vacío.</string>
<string name="copy_field">Copia de %1$s</string>
<string name="menu_form_filling_settings">Rellenado de formularios</string>
<string name="protection">Protección</string>
@@ -399,7 +399,7 @@
<string name="error_copy_group_here">No puede copiar un grupo aquí.</string>
<string name="database_data_compression_summary">La compresión de datos reduce el tamaño de la base de datos</string>
<string name="database_data_compression_title">Compresión de datos</string>
<string name="warning_empty_keyfile">No se recomienda agregar un cerrojo vacío.</string>
<string name="warning_empty_keyfile">No se recomienda agregar un archivo de clave vacío.</string>
<string name="warning_sure_remove_data">¿Eliminar estos datos de todos modos\?</string>
<string name="warning_sure_add_file">¿Agregar el archivo de todos modos\?</string>
<string name="warning_replace_file">Al cargar este archivo, se reemplazará el existente.</string>
@@ -408,7 +408,7 @@
<string name="command_execution">Ejecutando el comando…</string>
<string name="hide_broken_locations_summary">Oculta los enlaces rotos en la lista de bases de datos recientes</string>
<string name="hide_broken_locations_title">Ocultar enlaces rotos de la base de datos</string>
<string name="remember_keyfile_locations_summary">Mantiene seguimiento de dónde los cerrojos son almacenados</string>
<string name="remember_keyfile_locations_summary">Mantiene seguimiento de dónde los archivos de claves son almacenados</string>
<string name="remember_keyfile_locations_title">Recordar las ubicaciones de los archivos clave</string>
<string name="subdomain_search_summary">Busca dominios web con restricciones de subdominios</string>
<string name="subdomain_search_title">Búsqueda de subdominio</string>
@@ -444,7 +444,6 @@
<string name="device_credential">Credenciales del dispositivo</string>
<string name="credential_before_click_advanced_unlock_button">Teclee la contraseña y luego pulse sobre este botón.</string>
<string name="advanced_unlock_prompt_not_initialized">No se puede inicializar el aviso de desbloqueo avanzado.</string>
<string name="advanced_unlock_scanning_error">Error de desbloqueo del dispositivo: %1$s</string>
<string name="advanced_unlock_not_recognized">No se ha podido reconocer la impresión de desbloqueo avanzado</string>
<string name="advanced_unlock_invalid_key">No se puede leer la clave de desbloqueo del dispositivo. Por favor, bórrala y repite el procedimiento de reconocimiento del desbloqueo.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Extraer la credencial de la base de datos con los datos de desbloqueo del dispositivo</string>
@@ -454,7 +453,7 @@
<string name="keystore_not_accessible">El almacén de claves no está debidamente inicializado.</string>
<string name="biometric_security_update_required">Se requiere actualización de seguridad biométrica.</string>
<string name="configure_biometric">No se ha inscrito ninguna credencial biométrica o del dispositivo.</string>
<string name="warning_empty_keyfile_explanation">El contenido del cerrojo nunca debe modificarse y, en el mejor de los casos, debe contener datos generados al azar.</string>
<string name="warning_empty_keyfile_explanation">El contenido del archivo de clave nunca debe modificarse y, en el mejor de los casos, debe contener datos generados al azar.</string>
<string name="warning_empty_recycle_bin">¿Borrar permanentemente todos los nodos de la papelera de reciclaje\?</string>
<string name="registration_mode">Modo de registro</string>
<string name="save_mode">Modo de guardado</string>
@@ -462,7 +461,7 @@
<string name="contains_duplicate_uuid_procedure">¿Solucionar el problema generando nuevos UUID para que los duplicados continúen?</string>
<string name="menu_keystore_remove_key">Eliminar la clave de desbloqueo del dispositivo</string>
<string name="error_field_name_already_exists">El nombre del campo ya existe.</string>
<string name="error_registration_read_only">Guardar un nuevo elemento no está permitido en una base de datos de sólo lectura</string>
<string name="error_registration_read_only">Guardar un nuevo elemento no está permitido en una base de datos de sólo lectura.</string>
<string name="settings_database_recommend_changing_master_key_title">Recomendar renovación</string>
<string name="max_history_size_summary">Limitar el tamaño del historial por apunte</string>
<string name="max_history_items_summary">Limitar el número de elementos del historial por apunte</string>
@@ -639,7 +638,7 @@
<string name="screenshot_mode_banner_text">Modo de captura de pantalla</string>
<string name="error_hardware_key_unsupported">La llave por hardware no es compatible.</string>
<string name="html_about_privacy">&lt;strong&gt;No se recupera ningún dato del usuario&lt;/strong&gt;, esta aplicación no se conecta a ningún servidor y funciona solo localmente y respeta plenamente la privacidad de los usuarios.</string>
<string name="error_unable_merge_database_kdb">No se puede fusionar con un archivo de base de datos kdb</string>
<string name="error_unable_merge_database_kdb">No se puede fusionar con un archivo de base de datos kdb.</string>
<string name="error_cancel_by_user">Cancelado por el usuario.</string>
<string name="error_no_response_from_challenge">No se puede obtener la respuesta del desafío.</string>
<string name="auto_type">Auto-teclear</string>

View File

@@ -129,7 +129,7 @@
<string name="error_pass_match">Salasõnad ei klapi.</string>
<string name="error_create_database">Andmebaasifaili loomine ei õnnestunud.</string>
<string name="entry_url">URL</string>
<string name="error_file_not_create">Faili loomine ei õnnestunud</string>
<string name="error_file_not_create">Faili loomine ei õnnestunud.</string>
<string name="entry_otp">Ühekordne salasõna</string>
<string name="clipboard_timeout_summary">Lõikelauale kopeeritud andmete hoidmise aeg (kui sinu seade sellist võimalust toetab)</string>
<string name="content_description_keyboard_close_fields">Sulge väljad</string>
@@ -234,7 +234,7 @@
<string name="menu_delete">Kustuta</string>
<string name="menu_paste">Aseta</string>
<string name="menu_cancel">Katkesta</string>
<string name="error_unable_merge_database_kdb">Mestimine teise kdb andmebaasifailiga ei õnnestu</string>
<string name="error_unable_merge_database_kdb">Mestimine teise kdb andmebaasifailiga ei õnnestu.</string>
<string name="error_location_unknown">Andmebaasi asukoht pole teada ja toimingut andmebaasiga ei saa teha.</string>
<string name="menu_empty_recycle_bin">Tühjenda prügikast</string>
<string name="menu_restore_entry_history">Taasta ajalugu</string>
@@ -274,7 +274,7 @@
<string name="import_app_properties_summary">Rakenduse seadistuste importimiseks vali fail</string>
<string name="export_app_properties_title">Ekspordi rakenduse seadistused</string>
<string name="success_export_app_properties">Rakenduse seadistused on eksporditud</string>
<string name="error_export_app_properties">Rakenduse seadistuste eksportimisel tekkis viga</string>
<string name="error_export_app_properties">Rakenduse seadistuste eksportimisel tekkis viga.</string>
<string name="root">Juurkaust</string>
<string name="memory_usage">Mälukasutus</string>
<string name="error_string_type">See tekst ei vasta päringule.</string>
@@ -295,7 +295,7 @@
<string name="hide_broken_locations_title">Peida katkised andmebaaside lingid</string>
<string name="hide_broken_locations_summary">Peida hiljutikasutatud andmebaaside loendist need kirjed, mille lingid enam ei toimi</string>
<string name="success_import_app_properties">Rakenduse seadistused on imporditud</string>
<string name="error_import_app_properties">Viga rakenduse seadistuste importimisel</string>
<string name="error_import_app_properties">Viga rakenduse seadistuste importimisel.</string>
<string name="description_app_properties">KeePassDX võimalused rakenduse seadistuste haldamiseks</string>
<string name="encryption_explanation">Salasõnalaeka andmebaasi puhul kasutatud krüptoalgoritm</string>
<string name="kdf_explanation">Krüptoalgoritmi jaoks võtme loomisel peavõtit muudetakse võtetuletusfunktsiooniga, mis kasutab juhuslikke soolaterakesi.</string>
@@ -440,7 +440,6 @@
<string name="merge_success">Mestimine õnnestus</string>
<string name="biometric_security_update_required">Vajalik on biomeetrilise turvalisuse uuendus.</string>
<string name="encrypted_value_stored">Krüptitud salasõna on salvestatud</string>
<string name="advanced_unlock_scanning_error">Viga seadme lukustuse eemaldamisel: %1$s</string>
<string name="advanced_unlock_not_recognized">Ei õnnestunud tuvastada lukustuse eemaldamiseks vajalikku tunnust</string>
<string name="advanced_unlock_prompt_not_initialized">Seadme lukustuse eemaldamise päringu käivitamine ei õnnestu.</string>
<string name="biometric">Biomeetriline</string>
@@ -594,7 +593,7 @@
<string name="education_field_copy_title">Välja kopeerimine</string>
<string name="education_field_copy_summary">Kopeeritud väljade sisu saad asetada kõikjale.\n\nVäljade täitmiseks kasuta meetodist, mis sulle sobib. Ära unusta kopeeritud saalsõna lõikelaualt kustutada.</string>
<string name="unavailable">Pole saadaval</string>
<string name="error_challenge_already_requested">Autentimispäring on juba esitatud</string>
<string name="error_challenge_already_requested">Autentimispäring on juba esitatud.</string>
<string name="error_response_already_provided">Autentimisvastus on juba antud.</string>
<string name="error_no_response_from_challenge">Autentimisvastust ei õnnestu autentimispäringust tuletada.</string>
<string name="warning_database_notification_permission">Teevituste saatmise õigused võimaldavad sulle kuvada andmebaasi olekut ning lukustada seda lihtsalt ligipääsetvast nupust.\n\nKui sa seda õigust ei anna, siis taustal olev andmebaas pole nähtav, kui mõni muu rakendus on parasjagu esiplaanil.</string>
@@ -662,7 +661,7 @@
<string name="enter">Sisestusklahv (Enter)</string>
<string name="education_validate_entry_summary">Ära unusta kirje sisu kontrollida ja salvestada.\n\nKui automaatne lukustus käivitub ja sa unustad, et muutmine oli pooleli, siis võid kaotada oma muudatused.</string>
<string name="html_text_ad_free">Erinevalt paljudest teistest salasõnahalduritest, on meie oma &lt;strong&gt;reklaamivaba&lt;/strong&gt; ja &lt;strong&gt;avatud lähtekoodiga vaba tarkvara&lt;/strong&gt;, mis ei kogu kasutajate isiklikke andmeid oma serveritesse ja seda kõikide versioonide puhul.</string>
<string name="html_text_donation">&lt;strong&gt;Aidates kaasa&lt;/strong&gt; selle projekti tegevusele <i>(rahaliselt, koodi kirjutades või tõlkides)</i> aitad kogu ettevõtmisel areneda ning soovi korral võid kasutada ka &lt;strong&gt;seda kujundust&lt;/strong&gt;.</string>
<string name="html_text_donation">&lt;strong&gt;Aidates kaasa&lt;/strong&gt; selle projekti tegevusele <i>(rahaliselt, koodi kirjutades või tõlkides)</i>, aitad kogu ettevõtmisel areneda ning soovi korral võid kasutada ka &lt;strong&gt;seda kujundust&lt;/strong&gt;.</string>
<string name="advanced_unlock_keystore_warning">See funktsionaalsus salvestab krüptitud salasõnad ja kasutajanimed sinu nutiseadme turvalises võtmehoidlas.\n\nSõltuvalt sinu nutiseadme operatsioonisüstemi konkreetsest implementatsioonist, ei pruugi see lahendus siiski täismahus toimida.\n\nPalun kontrolli oma seadme võtmehoidla (KeyStore) ühilduvust ja turvalisust tootjalt ja/või tarkvara loojalt.</string>
<string name="keyboard_previous_fill_in_summary">Peale tegevust „Automaatne võtmetoiming“ lülita automaatselt tagasi eelmisele klahvistikule</string>
<string name="education_read_only_summary">Saad juhtida sessioonil kasutatavat avamisviisi.\n\n„Kirjutuskaitstud“ tagab, et juhuslike muudatustega ei läheks andmeid kaotsi.\n„Muudetav“ võimaldab sul lisada, kustutada või muuta kõiki andmebaasi kirjeid.</string>

View File

@@ -489,7 +489,6 @@
<string name="advanced_unlock_prompt_store_credential_message">Zure kutxa gotorraren pasahitz-nagusia gogoratu behar duzu naiz eta desblokeo aurreratuko ezagutzea erabili arren.</string>
<string name="encrypted_value_stored">Zifratutako pasahitza gordeta</string>
<string name="unavailable">Datu-base honek ez du biltegiratuta kredentzialik.</string>
<string name="advanced_unlock_scanning_error">Gailuaren desblokeatze errorea: %1$s</string>
<string name="menu_appearance_settings">Itxura</string>
<string name="autofill_sign_in_prompt">KeePassDXekin erregistratu</string>
<string name="autofill_explanation_summary">Gaitu betetze automatikoa beste aplikazioetako formularioak errez betetzeko</string>

View File

@@ -509,7 +509,6 @@
<string name="device_credential">Identifiant de l\'appareil</string>
<string name="credential_before_click_advanced_unlock_button">Tapez le mot de passe, puis cliquez sur ce bouton.</string>
<string name="advanced_unlock_prompt_not_initialized">Impossible d\'initialiser l\'invite de déverrouillage avancé.</string>
<string name="advanced_unlock_scanning_error">Erreur de déverrouillage avancé : %1$s</string>
<string name="advanced_unlock_not_recognized">Impossible de reconnaître l\'empreinte de déverrouillage de l\'appareil</string>
<string name="advanced_unlock_invalid_key">Impossible de lire la clé de déverrouillage de l\'appareil. Veuillez la supprimer et répéter la procédure de reconnaissance de déverrouillage.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Extraire les identifiants de la base de données avec des données de déverrouillage de l\'appareil</string>
@@ -704,6 +703,6 @@
<string name="recursive_number_entries_title">Nombre d\'entrées récursif</string>
<string name="recursive_number_entries_summary">Calcule récursivement le nombre d\'entrées dans un groupe</string>
<string name="warning_large_keyfile">Il n\'est pas recommandé d\'ajouter un fichier clé volumineux, car cela pourrait empêcher l\'ouverture de la base de données.</string>
<string name="hide_templates_title">Cacher les modèles</string>
<string name="hide_templates_summary">Les modèles ne sont pas affichés</string>
<string name="hide_templates_title">Cacher les gabarits</string>
<string name="hide_templates_summary">Les gabarits ne sont pas affichés</string>
</resources>

View File

@@ -425,7 +425,6 @@
<string name="advanced_unlock_invalid_key">Non foi posíbel ler a clave de desbloqueo avanzado. Por favor, bórrea e repita o procedemento de recoñecemento do desbloqueo.</string>
<string name="encrypted_value_stored">Contrasinal cifrado almacenado</string>
<string name="advanced_unlock_not_recognized">Non foi posíbel recoñecer a pegada do desbloqueo avanzado</string>
<string name="advanced_unlock_scanning_error">Erro de desbloqueo avanzado: %1$s</string>
<string name="autofill_service_name">Servizo de autocompletado do KeePassDX</string>
<string name="database_history">Historial</string>
<string name="advanced_unlock_prompt_store_credential_message">Aínda precisa lembrar a súa credencial principal se usar o recoñecemento de desbloqueo avanzado.</string>

View File

@@ -490,7 +490,6 @@
<string name="menu_keystore_remove_key">Izbriši ključ za otključavanje uređaja</string>
<string name="advanced_unlock_prompt_store_credential_title">Poveznica za otključavanje uređaja</string>
<string name="advanced_unlock_prompt_not_initialized">Nije moguće pokrenuti prozor za otključavanje uređaja.</string>
<string name="advanced_unlock_scanning_error">Greška otključavanja uređaja: %1$s</string>
<string name="advanced_unlock_prompt_extract_credential_message">Izdvoji podatake za prijavu na bazu podataka pomoću podataka za otključavanje uređaja</string>
<string name="advanced_unlock_not_recognized">Nije bilo moguće prepoznati ispis za otključavanje uređaja</string>
<string name="advanced_unlock_invalid_key">Nije moguće pročitati ključ za otključavanje uređaja. Izbriši ga i ponovi postupak prepoznavanja otključavanja.</string>

View File

@@ -287,7 +287,7 @@
<string name="html_text_ad_free">Számos más jelszókezelő alkalmazással ellentétben, ez egy &lt;strong&gt;reklámmentes&lt;/strong&gt;, &lt;strong&gt;copyleft licencelésű szabad szoftver&lt;/strong&gt;, amely nem gyűjt személyes adatokat a kiszolgálókon, bármelyik verziót is használja.</string>
<string name="html_text_buy_pro">A pro verzió megvásárlásával hozzáférést kap ehhez a &lt;strong&gt;vizuális stílushoz&lt;/strong&gt;, és segít a &lt;strong&gt;közösségi projektek megvalósulásában.&lt;/strong&gt;</string>
<string name="html_text_feature_generosity">Ez a &lt;strong&gt;vizuális stílus&lt;/strong&gt; az Ön nagylelkűségének köszönhetően érhető el.</string>
<string name="html_text_donation">Azzal, hogy &lt;strong&gt;hozzájárul&lt;/strong&gt; a projekthez <i>(pénzzel, kóddal, fordítással)</i>, segít abban, hogy a projekt tovább éljen és gyarapodjon, továbbá Ön jogosulttá válik a &lt;strong&gt;téma&lt;/strong&gt; feloldási eljárásra is.</string>
<string name="html_text_donation">Azzal, hogy &lt;strong&gt;hozzájárul&lt;/strong&gt; a projekthez <i>(pénzzel, kóddal, fordítással)</i>, segít abban, hogy a projekt tovább éljen és gyarapodjon, továbbá Ön jogosulttá válik a &lt;strong&gt;téma&lt;/strong&gt;feloldásának lehetősége.</string>
<string name="html_text_dev_feature">Ez a funkció &lt;strong&gt;fejlesztés alatt áll&lt;/strong&gt;, és az Ön &lt;strong&gt;támogatására&lt;/strong&gt; van szükség, hogy hamarosan elérhető legyen.</string>
<string name="html_text_dev_feature_buy_pro">A &lt;strong&gt;pro&lt;/strong&gt; verzió megvásárlásával,</string>
<string name="html_text_dev_feature_contibute">A &lt;strong&gt;támogatással&lt;/strong&gt;</string>
@@ -537,7 +537,6 @@
<string name="credential_before_click_advanced_unlock_button">Írja be a jelszót, majd kattintson erre a gombra.</string>
<string name="temp_advanced_unlock_enable_title">Ideiglenes eszközfeloldás</string>
<string name="permission">Engedély</string>
<string name="advanced_unlock_scanning_error">Eszközfeloldási hiba: %1$s</string>
<string name="content">Tartalom</string>
<string name="advanced_unlock_tap_delete">Koppintson az eszközfeloldási kulcsok törléséhez</string>
<string name="device_credential_unlock_enable_title">Eszköz hitelesítő adataival történő feloldás</string>

View File

@@ -582,7 +582,6 @@
<string name="upper_case">HURUF BESAR</string>
<string name="title_case">Huruf Judul</string>
<string name="character_count">Jumlah karakter: %1$d</string>
<string name="advanced_unlock_scanning_error">Terjadi kesalahan buka kunci perangkat: %1$s</string>
<string name="advanced_unlock_prompt_not_initialized">Tidak dapat menginisialisasi perintah buka kunci perangkat.</string>
<string name="monospace_font_fields_enable_title">Bidang tipe huruf</string>
<string name="keyboard_save_search_info_title">Simpan info terbagi</string>

View File

@@ -264,7 +264,7 @@
<string name="html_text_ad_free">Diversamente da molte app di gestione password, questa è &lt;strong&gt;senza pubblicità&lt;/strong&gt;, &lt;strong&gt;software libero (copyleft)&lt;/strong&gt; e non raccoglie dati personali nei suoi server, non importa quale versione usi.</string>
<string name="html_text_buy_pro">Acquistando la versione pro, avrai accesso a questo &lt;strong&gt;stile visivo&lt;/strong&gt; e soprattutto aiuterai nella &lt;strong&gt;realizzazione dei progetti della comunità.&lt;/strong&gt;</string>
<string name="html_text_feature_generosity">Questo &lt;strong&gt;stile visivo&lt;/strong&gt; è disponibile grazie alla tua generosità.</string>
<string name="html_text_donation">Se &lt;strong&gt;contribuirai&lt;/strong&gt; al progretto <i>(tramite una donazione, del codice una traduzione)</i>, lo aiuterai a vivere e a prosperare, e avrai a disposizione anche la procedura di sblocco del &lt;strong&gt;tema&lt;/strong&gt;.</string>
<string name="html_text_donation">Se &lt;strong&gt;contribuirai&lt;/strong&gt;al progretto <i>(tramite una donazione, del codice, una traduzione)</i>, lo aiuterai a vivere e a prosperare, e avrai a disposizione anche la procedura di sblocco del &lt;strong&gt;tema&lt;/strong&gt;.</string>
<string name="html_text_dev_feature">Questa funzione è &lt;strong&gt;in sviluppo&lt;/strong&gt; e richiede il tuo &lt;strong&gt;contributo&lt;/strong&gt; per essere disponibile a breve.</string>
<string name="html_text_dev_feature_buy_pro">Acquistando la versione &lt;strong&gt;pro&lt;/strong&gt;,</string>
<string name="html_text_dev_feature_contibute">
@@ -515,7 +515,6 @@
<string name="advanced_unlock_prompt_store_credential_title">Collegamento allo sblocco con dispositivo</string>
<string name="device_credential">Credenziali del dispositivo</string>
<string name="credential_before_click_advanced_unlock_button">Inserisci la password, poi clicca questo pulsante.</string>
<string name="advanced_unlock_scanning_error">Errore sblocco con dispositivo: %1$s</string>
<string name="advanced_unlock_prompt_extract_credential_title">Riconoscimento sblocco con dispositivo</string>
<string name="menu_keystore_remove_key">Elimina chiave di sblocco del dispositivo</string>
<string name="error_rebuild_list">Non è possibile ricostruire la lista correttamente.</string>

View File

@@ -59,7 +59,7 @@
<string name="entry_user_name">שם משתמש</string>
<string name="error_arc4">הצופן זרם Arcfour אינו נתמך.</string>
<string name="error_can_not_handle_uri">לא ניתן לטפל ב-URI הזה ב-KeePassDX.</string>
<string name="error_file_not_create">לא ניתן ליצור קובץ</string>
<string name="error_file_not_create">לא ניתן ליצור קובץ.</string>
<string name="error_invalid_db">לא ניתן לקרוא את מסד הנתונים.</string>
<string name="error_invalid_path">ודא שהנתיב נכון.</string>
<string name="error_no_name">הזן שם.</string>
@@ -190,7 +190,7 @@
<string name="error_copy_entry_here">אינך יכול להעתיק רשומה לכאן.</string>
<string name="error_otp_counter">המונה חייב להיות בין %1$d ל-%2$d.</string>
<string name="error_otp_period">פרק זמן חייב להיות בין %1$d ל-%2$d שניות.</string>
<string name="error_registration_read_only">שמירת פריט חדש אינה מותרת במסד נתונים לקריאה בלבד</string>
<string name="error_registration_read_only">שמירת פריט חדש אינה מותרת במסד נתונים לקריאה בלבד.</string>
<string name="error_string_type">טקסט זה אינו תואם את הפריט המבוקש.</string>
<string name="error_field_name_already_exists">שם השדה כבר קיים.</string>
<string name="error_database_uri_null">לא ניתן לאחזר URI של מסד הנתונים.</string>
@@ -203,7 +203,7 @@
<string name="error_cancel_by_user">בוטל על ידי המשתמש.</string>
<string name="error_driver_required">נדרש מנהל התקן עבור %1$s.</string>
<string name="error_location_unknown">מיקום מסד הנתונים אינו ידוע, לא ניתן לבצע פעולת מסד נתונים.</string>
<string name="error_unable_merge_database_kdb">לא ניתן למזג עם קובץ מסד נתונים kdb</string>
<string name="error_unable_merge_database_kdb">לא ניתן למזג עם קובץ מסד נתונים kdb.</string>
<string name="error_hardware_key_unsupported">מפתח חומרה אינו נתמך.</string>
<string name="error_empty_key">המפתח לא יכול להיות ריק.</string>
<string name="file_not_found_content">לא ניתן למצוא את הקובץ. נסה לפתוח אותו מחדש מסייר הקבצים שלך.</string>
@@ -539,7 +539,6 @@
<string name="advanced_unlock_prompt_extract_credential_message">חלץ אישור מסד נתונים עם נתוני ביטול נעילת מכשיר</string>
<string name="advanced_unlock_invalid_key">לא ניתן לקרוא את מפתח ביטול נעילת המכשיר. נא למחוק אותו ולחזור על התהליך לזיהוי ביטול נעילה.</string>
<string name="advanced_unlock_not_recognized">לא היה ניתן לזהות טביעת ביטול נעילת מכשיר</string>
<string name="advanced_unlock_scanning_error">שגיאת ביטול נעילת מכשיר: %1$s</string>
<string name="credential_before_click_advanced_unlock_button">הקלד את הסיסמה, ואז לחץ על הכפתור הזה.</string>
<string name="lock_database_show_button_title">הצג כפתור נעילה</string>
<string name="lock_database_show_button_summary">הצג את כפתור הנעילה בממשק המשתמש</string>
@@ -579,7 +578,7 @@
<string name="success_import_app_properties">הגדרות יישום יובאו</string>
<string name="sort_title">כותרת</string>
<string name="description_app_properties">מאפייני KeePassDX לניהול הגדרות יישום</string>
<string name="error_import_app_properties">שגיאה במהלך ייבוא הגדרות יישום</string>
<string name="error_import_app_properties">שגיאה במהלך ייבוא הגדרות יישום.</string>
<string name="style_name_divine">שמיימי</string>
<string name="style_name_classic">קלאסי</string>
<string name="style_name_kunzite">Kunzite</string>
@@ -639,7 +638,7 @@
<string name="import_app_properties_title">ייבוא הגדרות יישום</string>
<string name="export_app_properties_title">ייצוא הגדרות יישום</string>
<string name="export_app_properties_summary">צור קובץ כדי לייצא הגדרות יישום</string>
<string name="error_export_app_properties">שגיאה במהלך ייצוא הגדרות יישום</string>
<string name="error_export_app_properties">שגיאה במהלך ייצוא הגדרות יישום.</string>
<string name="html_text_feature_generosity">&lt;strong&gt;הסגנון החזותי&lt;/strong&gt; הזה זמין הודות לנדיבות שלך.</string>
<string name="html_text_dev_feature_contibute">על ידי &lt;strong&gt;תרומה&lt;/strong&gt;,</string>
<string name="hide_templates_title">הסתר תבניות</string>

View File

@@ -491,7 +491,6 @@
<string name="device_credential">デバイス認証情報</string>
<string name="credential_before_click_advanced_unlock_button">パスワードを入力し、このボタンをタップします。</string>
<string name="advanced_unlock_prompt_not_initialized">デバイスのロック解除プロンプトを初期化できません。</string>
<string name="advanced_unlock_scanning_error">デバイスのロック解除エラー: %1$s</string>
<string name="advanced_unlock_invalid_key">デバイスのロック解除キーを読み取ることができません。削除して、ロック解除認識手順を繰り返してください。</string>
<string name="advanced_unlock_prompt_extract_credential_message">デバイスのロック解除データを使用してデータベースの資格情報を抽出する</string>
<string name="advanced_unlock_prompt_extract_credential_title">デバイスのロック解除認識</string>
@@ -662,7 +661,7 @@
<string name="waiting_challenge_request">チャレンジリクエストを待っています…</string>
<string name="error_response_already_provided">回答はすでに提供されています。</string>
<string name="error_no_response_from_challenge">チャレンジからの応答を取得できません。</string>
<string name="error_unable_merge_database_kdb">kdbデータベースファイルとのマージはできません</string>
<string name="error_unable_merge_database_kdb">kdbデータベースファイルとのマージはできません</string>
<string name="menu_advanced_unlock_settings_summary">生体認証、デバイス認証</string>
<string name="menu_database_settings_summary">メタデータ、ごみ箱、テンプレート、履歴</string>
<string name="menu_security_settings_summary">暗号化、鍵導出関数</string>
@@ -673,7 +672,7 @@
<string name="warning_database_notification_permission">通知権限を使用すると、データベースのステータスを表示し、簡単にアクセスできるボタンでロックすることができます。
\n
\nこの権限を有効にしないと、別のアプリケーションがフォアグラウンドにある場合、バックグラウンドで開いているデータベースは表示されません。</string>
<string name="ask">話す</string>
<string name="ask">権限を要求する</string>
<string name="merge_success">マージが正常に完了しました</string>
<string name="configure">設定する</string>
<string name="menu_appearance_settings_summary">テーマ、色、属性</string>

View File

@@ -395,7 +395,6 @@
<string name="configure">Konfigūruoti</string>
<string name="biometric_security_update_required">Reikalingas biometrinių duomenų saugumo atnaujinimas.</string>
<string name="advanced_unlock_prompt_store_credential_message">Jei naudojate įrenginio atrakinimo atpažinimą, vis tiek turite prisiminti pagrindinį saugyklos raktą</string>
<string name="advanced_unlock_scanning_error">Įrenginio atrakinimo klaida: %1$s</string>
<string name="database_history">Istorija</string>
<string name="properties">Nustatymai</string>
<string name="menu_appearance_settings_summary">Temos, spalvos, atributai</string>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="contact">Контакт</string>
<string name="info">Инфо:</string>
<string name="contribution">Придонес</string>
<string name="feedback">Повратни информации</string>
<string name="homepage">Дома</string>
<string name="about_description">Имплементација на менаџерот за лозинки KeePass за Android</string>
<string name="accept">Прифати</string>
<string name="edit_entry">Уреди запис</string>
<string name="add_group">Додади група</string>
<string name="master_key">Главен клуч</string>
<string name="security">Безбедност</string>
<string name="encryption">Енкрипција</string>
<string name="encryption_algorithm">Енкрипциски алгоритам</string>
<string name="key_derivation_function">Функција за изведување на клучеви</string>
<string name="app_timeout">Истекување на времето</string>
<string name="app_timeout_summary">Време на мирување пред заклучување на базата</string>
<string name="application">Апликација</string>
<string name="brackets">Загради</string>
<string name="extended_ASCII">Проширен ASCII</string>
<string name="file_manager_install_description">За креирање, отворање и зачувување на датотеки од базата е потребен менаџер на датотеки што ги прифаќа ACTION_CREATE_DOCUMENT и ACTION_OPEN_DOCUMENT.</string>
<string name="allow">Дозволи</string>
<string name="clipboard_cleared">Clipboard исчистенa</string>
<string name="clipboard_error_title">Clipboard проблем</string>
<string name="clipboard_error">Некои уреди не дозволуваат апликациите да ја користат clipboard.</string>
<string name="clipboard_error_clear">Не може да се исчисти clipboard</string>
<string name="clipboard_timeout">Clipboard тајмаут</string>
<string name="clipboard_timeout_summary">Времетраење на складирањето во clipboard (доколку е поддржано од вашиот уред)</string>
<string name="content_description_background">Позадина</string>
<string name="content_description_open_file">Отвори датотека</string>
<string name="content_description_node_children">Поддеца</string>
<string name="content_description_add_node">Додај јазол</string>
<string name="content_description_add_entry">Додај запис</string>
<string name="content_description_add_group">Додади група</string>
<string name="content_description_add_item">Додај ставка</string>
<string name="content_description_file_information">Информации за датотеката</string>
</resources>

View File

@@ -405,7 +405,6 @@
<string name="hide_broken_locations_summary">Skjul ødelagte lenker i listen over nylige databaser</string>
<string name="hide_broken_locations_title">Skjul ødelagte databaselenker</string>
<string name="autofill_ask_to_save_data_title">Spør om lagring av data</string>
<string name="advanced_unlock_scanning_error">Feil ved opplåsing: %1$s</string>
<string name="warning_empty_keyfile">Det anbefales ikke å legge til en tom nøkkelfil.</string>
<string name="warning_sure_add_file">Legg til filen uansett\?</string>
<string name="registration_mode">Registreringsmodus</string>

View File

@@ -204,7 +204,7 @@
<string name="biometric_unlock_enable_title">Ontgrendelen met biometrie</string>
<string name="biometric_unlock_enable_summary">Gebruik biometrische herkenning om de database te openen</string>
<string name="biometric_delete_all_key_title">Coderingssleutels verwijderen</string>
<string name="biometric_delete_all_key_summary">Alle coderingssleutels met betrekking tot apparaat-ontgrendeling verwijderen</string>
<string name="biometric_delete_all_key_summary">Alle coderingssleutels met betrekking tot apparaatontgrendeling verwijderen</string>
<string name="unavailable_feature_text">Kan deze functie niet starten.</string>
<string name="unavailable_feature_version">Dit apparaat draait op Android %1$s, maar %2$s of hoger is vereist.</string>
<string name="unavailable_feature_hardware">De bijbehorende hardware werd niet gevonden.</string>
@@ -272,7 +272,7 @@
<string name="html_text_ad_free">In tegenstelling tot veel apps voor wachtwoordbeheer, is deze &lt;strong&gt; reclamevrij&lt;/strong&gt;, &lt;strong&gt; vrije software &lt;/strong&gt; en verzamelt het geen persoonlijke gegevens op haar servers, ongeacht de versie die je gebruikt.</string>
<string name="html_text_buy_pro">Door de pro-versie te kopen krijg je toegang tot dit &lt;strong&gt;visuele thema&lt;/strong&gt; en draag je bij aan het &lt;strong&gt;realiseren van gemeenschapsprojecten.&lt;/strong&gt;</string>
<string name="html_text_feature_generosity">Dit &lt;strong&gt;visuele thema&lt;/strong&gt; is beschikbaar gemaakt dankzij jouw vrijgevigheid.</string>
<string name="html_text_donation">Met een &lt;strong&gt;bijdrage&lt;/strong&gt; <i>(financieel, code, vertaling),</i> help je dit project in het voortbestaan en verdere groei. Bovendien kom je in aanmerking ook de &lt;strong&gt;procedure&lt;/strong&gt; waarmee je de extra thema\'s kunt ontgrendelen.</string>
<string name="html_text_donation">Door &lt;strong&gt;bij te dragen&lt;/strong&gt; aan het project <i>(financieel, code, vertaling)</i>, help je het om te blijven leven en bloeien en kom je bovendien in aanmerking voor het &lt;strong&gt;thema&lt;/strong&gt; ontgrendelingsprocedure.</string>
<string name="html_text_dev_feature">Deze functie &lt;strong&gt;wordt momenteel ontwikkeld&lt;/strong&gt; en kan alleen beschikbaar komen middels jouw &lt;strong&gt;bijdrage&lt;/strong&gt;.</string>
<string name="html_text_dev_feature_buy_pro">Door de &lt;strong&gt;pro&lt;/strong&gt;-versie te kopen,</string>
<string name="html_text_dev_feature_contibute">Door &lt;strong&gt;bij te dragen&lt;/strong&gt;,</string>
@@ -342,7 +342,7 @@
<string name="menu_advanced_unlock_settings">Apparaatontgrendeling</string>
<string name="biometric">Biometrie</string>
<string name="biometric_auto_open_prompt_title">Auto-open suggestie</string>
<string name="biometric_auto_open_prompt_summary">Automatisch om apparaat-ontgrendeling vragen als een database hiervoor is ingesteld</string>
<string name="biometric_auto_open_prompt_summary">Automatisch om apparaatontgrendeling vragen als een database hiervoor is ingesteld</string>
<string name="enable">Inschakelen</string>
<string name="disable">Uitschakelen</string>
<string name="master_key">Hoofdsleutel</string>
@@ -371,7 +371,7 @@
<string name="contains_duplicate_uuid_procedure">Probleem oplossen door nieuwe UUID\'s te genereren voor de duplicaten\?</string>
<string name="database_opened">Database geopend</string>
<string name="clipboard_explanation_summary">Kopieer velden met behulp van het klembord van dit apparaat</string>
<string name="advanced_unlock_explanation_summary">Apparaat-ontgrendeling gebruiken om een database eenvoudiger te openen</string>
<string name="advanced_unlock_explanation_summary">Apparaatontgrendeling gebruiken om een database eenvoudiger te openen</string>
<string name="database_data_compression_title">Gegevenscompressie</string>
<string name="database_data_compression_summary">Gegevenscompressie verkleint de omvang van de database</string>
<string name="max_history_items_title">Maximum aantal</string>
@@ -489,33 +489,32 @@
<string name="search_mode">Zoekmodus</string>
<string name="error_registration_read_only">Het opslaan van een nieuw item is niet toegestaan in een alleen-lezen database.</string>
<string name="education_advanced_unlock_summary">Koppel je wachtwoord aan je gescande biometrische gegevens of apparaatreferentie om je database snel te ontgrendelen.</string>
<string name="education_advanced_unlock_title">Apparaat-ontgrendeling database</string>
<string name="education_advanced_unlock_title">Ontgrendeling database op apparaat</string>
<string name="enter">Enter</string>
<string name="backspace">Backspace</string>
<string name="select_entry">Item selecteren</string>
<string name="back_to_previous_keyboard">Terug naar vorig toetsenbord</string>
<string name="custom_fields">Aangepaste velden</string>
<string name="advanced_unlock_delete_all_key_warning">Alle coderingssleutels met betrekking tot apparaat-ontgrendeling verwijderen?</string>
<string name="advanced_unlock_timeout">Time-out bij apparaat-ontgrendeling</string>
<string name="temp_advanced_unlock_timeout_summary">Duur van apparaat-ontgrendeling voordat de inhoud wordt verwijderd</string>
<string name="temp_advanced_unlock_timeout_title">Vervaltijd voor apparaat-ontgrendeling</string>
<string name="temp_advanced_unlock_enable_summary">Sla geen versleutelde inhoud op om apparaat-ontgrendeling te gebruiken</string>
<string name="temp_advanced_unlock_enable_title">Tijdelijke apparaat-ontgrendeling</string>
<string name="advanced_unlock_timeout">Time-out bij apparaatontgrendeling</string>
<string name="temp_advanced_unlock_timeout_summary">Duur van apparaatontgrendeling voordat de inhoud wordt verwijderd</string>
<string name="temp_advanced_unlock_timeout_title">Vervaltijd voor apparaatontgrendeling</string>
<string name="temp_advanced_unlock_enable_summary">Sla geen versleutelde inhoud op om apparaatontgrendeling te gebruiken</string>
<string name="temp_advanced_unlock_enable_title">Tijdelijke apparaatontgrendeling</string>
<string name="device_credential_unlock_enable_summary">Hiermee kan je de referentie van je apparaat gebruiken om de database te openen</string>
<string name="device_credential_unlock_enable_title">Ontgrendeling met apparaatreferenties</string>
<string name="advanced_unlock_tap_delete">Tik om apparaat-ontgrendelingssleutels te verwijderen</string>
<string name="advanced_unlock_tap_delete">Tik om sleutels voor apparaatontgrendeling te verwijderen</string>
<string name="content">Inhoud</string>
<string name="device_credential">Apparaatreferentie</string>
<string name="credential_before_click_advanced_unlock_button">Typ het wachtwoord en klik vervolgens op deze knop.</string>
<string name="advanced_unlock_prompt_not_initialized">Kan apparaat-ontgrendelingsprompt niet initialiseren.</string>
<string name="advanced_unlock_scanning_error">Apparaat-ontgrendelingsfout: %1$s</string>
<string name="advanced_unlock_not_recognized">Kan apparaat-ontgrendelingsafdruk niet herkennen</string>
<string name="advanced_unlock_invalid_key">Kan de apparaat-ontgrendelingssleutel niet lezen. Verwijder deze en herhaal de herkenningsprocedure voor het ontgrendelen.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Databasegegevens uitpakken met apparaat-ontgrendelingsgegevens</string>
<string name="advanced_unlock_prompt_extract_credential_title">Apparaat-ontgrendeling</string>
<string name="advanced_unlock_prompt_store_credential_message">Je moet nog steeds je hoofdwachtwoord onthouden als je apparaat-ontgrendelingsherkenning gebruikt.</string>
<string name="advanced_unlock_prompt_store_credential_title">Koppeling naar Apparaat-ontgrendeling</string>
<string name="menu_keystore_remove_key">Apparaat-ontgrendelingssleutel verwijderen</string>
<string name="advanced_unlock_prompt_not_initialized">Kan apparaatontgrendeling niet initialiseren.</string>
<string name="advanced_unlock_not_recognized">Vingerafdruk niet herkent bij apparaatontgrendeling</string>
<string name="advanced_unlock_invalid_key">Kan de sleutel voor apparaatontgrendeling niet lezen. Verwijder deze en herhaal de herkenningsprocedure voor het ontgrendelen.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Database uitpakken met gegevens voor apparaatontgrendeling</string>
<string name="advanced_unlock_prompt_extract_credential_title">Apparaatontgrendeling</string>
<string name="advanced_unlock_prompt_store_credential_message">Je moet nog steeds je hoofdwachtwoord onthouden als je apparaatontgrendeling gebruikt.</string>
<string name="advanced_unlock_prompt_store_credential_title">Koppeling naar apparaatontgrendeling</string>
<string name="menu_keystore_remove_key">Sleutel voor apparaatontgrendeling verwijderen</string>
<string name="error_field_name_already_exists">De veldnaam bestaat al.</string>
<string name="unit_gibibyte">GiB</string>
<string name="unit_mebibyte">MiB</string>
@@ -685,7 +684,7 @@
<string name="warning_database_notification_permission">Met de meldingstoestemming kunt u de status van de database weergeven en vergrendelen met een gemakkelijk toegankelijke knop.
\n
\nAls u deze toestemming niet verleent, is de database die op de achtergrond is geopend niet zichtbaar als er een andere applicatie op de voorgrond staat.</string>
<string name="unlock_and_link_biometric">Apparaat-ontgrendelingslink</string>
<string name="unlock_and_link_biometric">Koppeling naar apparaatontgrendeling</string>
<string name="education_validate_entry_summary">Vergeet niet om de invoer te valideren en de database op te slaan.
\n
\nWanneer automatische vergrendeling is geactiveerd en u vergeet dat u een wijziging aan het aanbrengen was, dan loopt u het risico gegevens te verliezen.</string>

View File

@@ -445,7 +445,7 @@
<string name="autofill_application_id_blocklist_title">Lista zablokowanych aplikacji</string>
<string name="keyboard_previous_fill_in_summary">Automatycznie przełącz się z powrotem na poprzednią klawiaturę po wykonaniu automatycznej akcji klawiszy</string>
<string name="keyboard_previous_fill_in_title">Przełącz się z powrotem</string>
<string name="keyboard_previous_database_credentials_summary">Automatycznie przełącz się z powrotem do poprzedniej klawiatury na ekranie poświadczeń bazy danych</string>
<string name="keyboard_previous_database_credentials_summary">Automatycznie przełącz się z powrotem do poprzedniej klawiatury na ekranie poświadczeń bazy danych</string>
<string name="keyboard_previous_database_credentials_title">Ekran poświadczeń bazy danych</string>
<string name="keyboard_change">Przełącz klawiaturę</string>
<string name="upload_attachment">Prześlij %1$s</string>
@@ -511,7 +511,6 @@
<string name="advanced_unlock_tap_delete">Stuknij, aby usunąć klucze odblokowywania urządzenia</string>
<string name="content">Zawartość</string>
<string name="advanced_unlock_prompt_extract_credential_title">Rozpoznawanie odblokowania urządzenia</string>
<string name="advanced_unlock_scanning_error">Błąd odblokowania urządzenia: %1$s</string>
<string name="error_rebuild_list">Nie można poprawnie odbudować listy.</string>
<string name="error_database_uri_null">Nie można pobrać identyfikatora URI bazy danych.</string>
<string name="autofill_inline_suggestions_keyboard">Dodano sugestie autouzupełniania.</string>
@@ -654,7 +653,7 @@
<string name="error_hardware_key_unsupported">Klucz sprzętowy nie jest obsługiwany.</string>
<string name="error_empty_key">Klucz nie może być pusty.</string>
<string name="corrupted_file">Uszkodzony plik.</string>
<string name="remember_hardware_key_title">Zapamiętaj klawisze sprzętowe</string>
<string name="remember_hardware_key_title">Zapamiętaj klucze sprzętowe</string>
<string name="remember_hardware_key_summary">Śledzi używane klucze sprzętowe</string>
<string name="enable_screenshot_mode_title">Tryb zrzutu ekranu</string>
<string name="enable_screenshot_mode_summary">Zezwól aplikacjom innych dostawców na nagrywanie lub robienie zrzutów ekranu aplikacji</string>

View File

@@ -512,7 +512,6 @@
<string name="properties">Propriedades</string>
<string name="credential_before_click_advanced_unlock_button">Digite a senha e clique neste botão.</string>
<string name="advanced_unlock_prompt_not_initialized">Não foi possível inicializar o prompt de desbloqueio do dispositivo.</string>
<string name="advanced_unlock_scanning_error">Erro de desbloqueio do dispositivo: %1$s</string>
<string name="advanced_unlock_not_recognized">Não foi possível reconhecer a impressão de desbloqueio</string>
<string name="advanced_unlock_invalid_key">Não é possível ler a chave de desbloqueio do dispositivo. Exclua-o e repita o procedimento de reconhecimento de desbloqueio.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Extraia a credencial do banco de dados com os dados de desbloqueio do dispositivo</string>

View File

@@ -468,7 +468,6 @@
<string name="device_credential">Credencial do dispositivo</string>
<string name="credential_before_click_advanced_unlock_button">Digite a palavra-passe e depois clique neste botão.</string>
<string name="advanced_unlock_prompt_not_initialized">Não foi possível inicializar a solicitação de desbloqueio do dispositivo.</string>
<string name="advanced_unlock_scanning_error">Erro de desbloqueio do dispositivo: %1$s</string>
<string name="advanced_unlock_not_recognized">Não foi possível reconhecer a impressão de desbloqueio do dispositivo</string>
<string name="advanced_unlock_invalid_key">Não é possível ler a chave de desbloqueio do dispositivo. Elimine-a e repita o procedimento de reconhecimento de desbloqueio.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Extrair credencial da base de dados com dados de desbloqueio do dispositivo</string>

View File

@@ -446,7 +446,6 @@
<string name="accept">Aceitar</string>
<string name="device_credential">Credencial do dispositivo</string>
<string name="advanced_unlock_prompt_not_initialized">Não foi possível inicializar a solicitação de desbloqueio do dispositivo.</string>
<string name="advanced_unlock_scanning_error">Erro de desbloqueio do dispositivo: %1$s</string>
<string name="advanced_unlock_not_recognized">Não foi possível reconhecer a impressão de desbloqueio do dispositivo</string>
<string name="advanced_unlock_invalid_key">Não é possível ler a chave de desbloqueio do dispositivo. Elimine-a e repita o procedimento de reconhecimento de desbloqueio.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Extrair credencial da base de dados com dados de desbloqueio do dispositivo</string>

View File

@@ -597,7 +597,6 @@
<string name="warning_copy_permission">Permisiunea de notificare este necesară pentru a utiliza funcția de notificare a clipboardului.</string>
<string name="advanced_unlock_prompt_store_credential_title">Legătură la deblocarea dispozitivului</string>
<string name="advanced_unlock_prompt_extract_credential_title">Recunoașterea deblocării dispozitivului</string>
<string name="advanced_unlock_scanning_error">Eroare de deblocare a dispozitivului: %1$s</string>
<string name="unlock_and_link_biometric">Legătură de deblocare a dispozitivului</string>
<string name="configure_biometric">Nu se înrolează nicio credențială biometrică sau de dispozitiv.</string>
<string name="autofill_select_entry">Selectați intrarea…</string>

View File

@@ -266,11 +266,11 @@
<string name="education_donation_title">Участвуйте</string>
<string name="education_donation_summary">Примите участие в проекте для повышения стабильности, безопасности и добавления новых возможностей.</string>
<string name="html_text_ad_free">В отличие от многих других приложений управления паролями, здесь &lt;strong&gt;нет рекламы&lt;/strong&gt;, и оно &lt;strong&gt;свободно от лицензирования&lt;/strong&gt;. Приложение не собирает ваши личные данные на своих серверах независимо от версии, которую вы используете.</string>
<string name="html_text_buy_pro">Купите про–версию и откройте доступ к этой &lt;strong&gt;теме&lt;/strong&gt;. Покупая про-версию, вы помогаете &lt;strong&gt;разработчикам открытого ПО.&lt;/strong&gt;</string>
<string name="html_text_buy_pro">Купите pro–версию и откройте доступ к этой &lt;strong&gt;теме&lt;/strong&gt;. Покупая pro-версию, вы помогаете &lt;strong&gt;разработчикам открытого ПО.&lt;/strong&gt;</string>
<string name="html_text_feature_generosity">Эти &lt;strong&gt;визуальные стили&lt;/strong&gt; доступны благодаря вашей щедрости.</string>
<string name="html_text_donation">Произведя &lt;strong&gt;вклад&lt;/strong&gt; в проект <i>(в денежном выражении, программным кодом или переводом)</i>, вы поможете ему жить и процветать, а также получите право на разблокировку &lt;strong&gt;тем&lt;/strong&gt;.</string>
<string name="html_text_donation">Произведя &lt;strong&gt;вклад&lt;/strong&gt; в проект <i>(в денежном выражении, программным кодом или переводом)</i>, вы поможете ему жить и процветать, а также получите право на разблокировку &lt;strong&gt;тем оформления&lt;/strong&gt;.</string>
<string name="html_text_dev_feature">Эта функция находится &lt;strong&gt;в разработке&lt;/strong&gt; и требует вашего &lt;strong&gt;участия&lt;/strong&gt;, чтобы стать доступной в ближайшее время.</string>
<string name="html_text_dev_feature_buy_pro">Покупая &lt;strong&gt;про&lt;/strong&gt;–версию,</string>
<string name="html_text_dev_feature_buy_pro">Покупая &lt;strong&gt;pro&lt;/strong&gt;–версию,</string>
<string name="html_text_dev_feature_contibute">&lt;strong&gt;Участвуя в проекте&lt;/strong&gt;,</string>
<string name="html_text_dev_feature_encourage">вы поощряете разработчиков добавлять &lt;strong&gt;новые возможности&lt;/strong&gt; и &lt;strong&gt;исправлять ошибки&lt;/strong&gt; в соответствии с вашими замечаниями.</string>
<string name="html_text_dev_feature_thanks">Большое спасибо за поддержку.</string>
@@ -498,7 +498,6 @@
<string name="advanced_unlock_prompt_extract_credential_title">Распознавание разблокировки устройства</string>
<string name="advanced_unlock_prompt_store_credential_message">При использовании разблокировки устройства вам всё равно необходимо помнить основные учётные данные.</string>
<string name="advanced_unlock_delete_all_key_warning">Удалить все ключи шифрования, связанные с распознаванием разблокировки устройства\?</string>
<string name="advanced_unlock_scanning_error">Ошибка разблокировки устройства: %1$s</string>
<string name="advanced_unlock_prompt_store_credential_title">Настройка разблокировки устройства</string>
<string name="menu_keystore_remove_key">Удалить ключ разблокировки устройства</string>
<string name="enter">Ввод</string>
@@ -652,7 +651,7 @@
<string name="error_response_already_provided">Ответ уже предоставлен.</string>
<string name="error_challenge_already_requested">Вызов уже запрошен.</string>
<string name="error_no_response_from_challenge">Невозможно получить ответ на вызов.</string>
<string name="error_unable_merge_database_kdb">Невозможно выполнить объединение с базой паролей в формате kdb</string>
<string name="error_unable_merge_database_kdb">Невозможно выполнить объединение с базой паролей в формате kdb.</string>
<string name="error_hardware_key_unsupported">Аппаратный ключ не поддерживается.</string>
<string name="error_empty_key">Ключ не может быть пустым.</string>
<string name="corrupted_file">Файл повреждён.</string>

View File

@@ -476,7 +476,6 @@
<string name="style_choose_title">Téma aplikácie</string>
<string name="protection">Ochrana</string>
<string name="configure_biometric">Nie sú zaregistrované žiadne biometrické údaje ani poverenia zariadenia.</string>
<string name="advanced_unlock_scanning_error">Chyba odomykania zariadenia: %1$s</string>
<string name="lock">Zamknúť</string>
<string name="custom_fields">Vlastné polia</string>
<string name="education_sort_summary">Vyberte spôsob triedenia záznamov a skupín.</string>

View File

@@ -344,7 +344,6 @@
<string name="show_entry_colors_title">Ngjyra zërash</string>
<string name="hide_expired_entries_title">Fshihi zërat e skaduar</string>
<string name="error_XML_malformed">XML e keqformuar.</string>
<string name="advanced_unlock_scanning_error">Gabim shkyçjeje pajisjeje: %1$s</string>
<string name="autofill_block">Blloko vetëplotësim</string>
<string name="allow_no_password_summary">Lejon prekjen e butoni “Hape”, nëse sjanë përzgjedhur kredenciale</string>
<string name="about_description">Sendërtim për Android i përgjegjësit KeePass të fjalëkalimeve</string>

View File

@@ -16,7 +16,7 @@
<string name="save">சேமி</string>
<string name="content_description_passphrase_word_count">கடவுச்சொல் சொல் எண்ணிக்கை</string>
<string name="error_string_key">ஒவ்வொரு சரத்திற்கும் புலத்தின் பெயர் இருக்க வேண்டும்.</string>
<string name="error_registration_read_only">புதிய உருப்படியைச் சேமிப்பது படிக்க மட்டுமே தரவுத்தளத்தில் அனுமதிக்கப்படாது</string>
<string name="error_registration_read_only">புதிய உருப்படியைச் சேமிப்பது படிக்க மட்டுமே தரவுத்தளத்தில் அனுமதிக்கப்படாது.</string>
<string name="field_name">புலம் பெயர்</string>
<string name="menu_form_filling_settings">படிவம் நிரப்புதல்</string>
<string name="menu_copy">நகலெடு</string>
@@ -106,7 +106,7 @@
<string name="education_field_copy_title">ஒரு புலத்தை நகலெடுக்கவும்</string>
<string name="education_field_copy_summary">நகலெடுக்கப்பட்ட புலங்களை எங்கும் ஒட்டலாம்.\n\n நீங்கள் விரும்பும் படிவ நிரப்புதல் முறையைப் பயன்படுத்தவும்.</string>
<string name="education_sort_summary">உள்ளீடுகள் மற்றும் குழுக்கள் எவ்வாறு வரிசைப்படுத்தப்படுகின்றன என்பதைத் தேர்வுசெய்க.</string>
<string name="html_text_buy_pro">புரோ பதிப்பை வாங்குவதன் மூலம், இந்த &lt;strong&gt; விசுவல் பாணி &lt;/strong&gt; க்கான அணுகலை நீங்கள் பெறுவீர்கள், மேலும் நீங்கள் குறிப்பாக &lt;strong&gt; சமூக திட்டங்களை உணர உதவுவீர்கள். &lt;/Strong&gt;</string>
<string name="html_text_buy_pro">புரோ பதிப்பை வாங்குவதன் மூலம், இந்த &lt;strong&gt; விசுவல் பாணி &lt;/strong&gt; க்கான அணுகலை நீங்கள் பெறுவீர்கள், மேலும் நீங்கள் குறிப்பாக &lt;strong&gt; சமூக திட்டங்களை உணர உதவுவீர்கள்.&lt;/strong&gt;</string>
<string name="html_text_dev_feature_thanks">உங்கள் பங்களிப்புக்கு மிக்க நன்றி.</string>
<string name="html_text_dev_feature_work_hard">இந்த அம்சத்தை விரைவாக வெளியிட நாங்கள் கடுமையாக உழைத்து வருகிறோம்.</string>
<string name="html_text_dev_feature_upgrade">புதிய பதிப்புகளை நிறுவுவதன் மூலம் உங்கள் பயன்பாட்டை புதுப்பித்த நிலையில் வைத்திருக்க நினைவில் கொள்ளுங்கள்.</string>
@@ -150,7 +150,7 @@
<string name="version">பதிப்பு</string>
<string name="entry_otp">OTP</string>
<string name="entry_url">முகவரி</string>
<string name="error_file_not_create">கோப்பை உருவாக்க முடியவில்லை</string>
<string name="error_file_not_create">கோப்பை உருவாக்க முடியவில்லை.</string>
<string name="error_load_database">தரவுத்தளத்தை ஏற்ற முடியவில்லை.</string>
<string name="error_pass_gen_type">குறைந்தது ஒரு கடவுச்சொல் உருவாக்கும் வகை தேர்ந்தெடுக்கப்பட வேண்டும்.</string>
<string name="error_label_exists">இந்த சிட்டை ஏற்கனவே உள்ளது.</string>
@@ -176,7 +176,6 @@
<string name="sort_groups_before">முன் குழுக்கள்</string>
<string name="warning_no_encryption_key">குறியாக்க விசை இல்லாமல் தொடரவா?</string>
<string name="warning_permanently_delete_nodes">தேர்ந்தெடுக்கப்பட்ட முனைகளை நிரந்தரமாக நீக்கவா?</string>
<string name="advanced_unlock_scanning_error">சாதனம் திறத்தல் பிழை: %1$s</string>
<string name="device_credential_unlock_enable_summary">தரவுத்தளத்தைத் திறக்க உங்கள் சாதன நற்சான்றிதழைப் பயன்படுத்தலாம்</string>
<string name="biometric_delete_all_key_title">குறியாக்க விசைகளை நீக்கு</string>
<string name="biometric_delete_all_key_summary">சாதன திறத்தல் ஏற்பு தொடர்பான அனைத்து குறியாக்க விசைகளையும் நீக்கு</string>
@@ -192,7 +191,7 @@
<string name="advanced_unlock_prompt_extract_credential_message">சாதன திறத்தல் தரவுடன் தரவுத்தள நற்சான்றிதழைப் பிரித்தெடுக்கவும்</string>
<string name="password_size_title">உருவாக்கப்பட்ட கடவுச்சொல் அளவு</string>
<string name="assign_master_key">ஒரு முதன்மை விசையை ஒதுக்குங்கள்</string>
<string name="error_import_app_properties">பயன்பாட்டு அமைப்புகள் இறக்குமதி செய்யும் போது பிழை</string>
<string name="error_import_app_properties">பயன்பாட்டு அமைப்புகள் இறக்குமதி செய்யும்போது பிழை.</string>
<string name="success_export_app_properties">பயன்பாட்டு அமைப்புகள் ஏற்றுமதி செய்யப்பட்டன</string>
<string name="passphrase">கடவுச்சொல்</string>
<string name="create_keepass_file">புதிய பெட்டகத்தை உருவாக்கவும்</string>
@@ -221,7 +220,7 @@
<string name="icon_pack_choose_summary">பயன்பாட்டில் பயன்படுத்தப்படும் படவுரு பேக்</string>
<string name="hide_templates_title">வார்ப்புருக்கள் மறைக்க</string>
<string name="hide_templates_summary">வார்ப்புருக்கள் காட்டப்படவில்லை</string>
<string name="application">பயன்பாடு</string>
<string name="application">செயலி</string>
<string name="brackets">அடைப்புக்குறிப்புகள்</string>
<string name="entry_accessed">அணுகப்பட்டது</string>
<string name="entry_notes">குறிப்புகள்</string>
@@ -269,7 +268,7 @@
<string name="error_start_database_action">தரவுத்தளத்தில் ஒரு செயலைச் செய்யும்போது பிழை ஏற்பட்டது.</string>
<string name="error_no_response_from_challenge">சவாலிலிருந்து பதிலைப் பெற முடியவில்லை.</string>
<string name="error_driver_required">%1$s க்கான இயக்கி தேவை.</string>
<string name="error_unable_merge_database_kdb">KDB தரவுத்தள கோப்புடன் ஒன்றிணைக்க முடியவில்லை</string>
<string name="error_unable_merge_database_kdb">KDB தரவுத்தள கோப்புடன் ஒன்றிணைக்க முடியவில்லை.</string>
<string name="error_location_unknown">தரவுத்தள இருப்பிடம் தெரியவில்லை, தரவுத்தள செயலைச் செய்ய முடியாது.</string>
<string name="error_hardware_key_unsupported">வன்பொருள் விசை ஆதரிக்கப்படவில்லை.</string>
<string name="error_empty_key">விசை காலியாக இருக்க முடியாது.</string>
@@ -322,7 +321,7 @@
<string name="export_app_properties_summary">பயன்பாட்டு அமைப்புகளை ஏற்றுமதி செய்ய ஒரு கோப்பை உருவாக்கவும்</string>
<string name="description_app_properties">பயன்பாட்டு அமைப்புகளை நிர்வகிக்க KeepASSDX பண்புகள்</string>
<string name="success_import_app_properties">பயன்பாட்டு அமைப்புகள் இறக்குமதி செய்யப்பட்டன</string>
<string name="error_export_app_properties">பயன்பாட்டு அமைப்புகள் ஏற்றுமதியின் போது பிழை</string>
<string name="error_export_app_properties">பயன்பாட்டு அமைப்புகள் ஏற்றுமதியின்போது பிழை.</string>
<string name="encryption_explanation">எல்லா தரவிற்கும் பயன்படுத்தப்படும் தரவுத்தள குறியாக்க வழிமுறை</string>
<string name="kdf_explanation">குறியாக்க வழிமுறைக்கான விசையை உருவாக்க, முதன்மை விசை தோராயமாக உப்பு விசை வழித்தோன்றல் செயல்பாட்டைப் பயன்படுத்தி மாற்றப்படுகிறது.</string>
<string name="style_name_forest">காடு</string>
@@ -337,7 +336,7 @@
<string name="extended_ASCII">நீட்டிக்கப்பட்ட ASCII</string>
<string name="allow">இசைவு</string>
<string name="app_timeout_summary">தரவுத்தளத்தை பூட்டுவதற்கு முன் செயலற்ற நேரம்</string>
<string name="app_timeout">நேரம் முடிந்தது</string>
<string name="app_timeout">முடிவு நேரம்</string>
<string name="key_derivation_function">முக்கிய வழித்தோன்றல் செயல்பாடு</string>
<string name="validate">சரிபார்க்கவும்</string>
<string name="entry_expires">காலாவதியாகிறது</string>
@@ -385,7 +384,7 @@
<string name="generate_keyfile">கீஃபைலை உருவாக்குங்கள்</string>
<string name="nodes">முனைகள்</string>
<string name="recursive_number_entries_title">உள்ளீடுகளின் சுழல்நிலை எண்ணிக்கை</string>
<string name="recursive_number_entries_summary">ஒரு குழுவில் உள்ளீடுகளின் எண்ணிக்கையை மீண்டும் மீண்டும் கணக்கிடுகிறது</string>
<string name="recursive_number_entries_summary">ஒரு குழுவில் உள்ளீடுகளின் எண்ணிக்கையை மறுநிகழ்வு கணக்கிடுகிறது</string>
<string name="menu_app_settings_summary">தேடல், பூட்டு, வரலாறு, பண்புகள்</string>
<string name="menu_form_filling_settings_summary">விசைப்பலகை, ஆட்டோஃபில், இடைநிலைப்பலகை</string>
<string name="menu_empty_recycle_bin">மறுசுழற்சி தொட்டியை வெறுமை செய்யுங்கள்</string>
@@ -398,8 +397,8 @@
<string name="advanced_unlock_delete_all_key_warning">சாதன திறத்தல் ஏற்பு தொடர்பான அனைத்து குறியாக்க விசைகளையும் நீக்கவா?</string>
<string name="recycle_bin_title">மறுசுழற்சி பின் பயன்பாடு</string>
<string name="application_appearance">இடைமுகம்</string>
<string name="magic_keyboard_title">Magikeyboard</string>
<string name="keyboard_name">Magikeyboard</string>
<string name="magic_keyboard_title">மாயவிசைப்பலகை</string>
<string name="keyboard_name">மாயவிசைப்பலகை</string>
<string name="keyboard_label">Magikeyboard (keepassdx)</string>
<string name="keyboard_selection_entry_title">நுழைவு தேர்வு</string>
<string name="keyboard_notification_entry_title">அறிவிப்பு செய்தி</string>
@@ -439,7 +438,7 @@
<string name="entry_user_name">பயனர்பெயர்</string>
<string name="error_arc4">ஆர்க்ஃபோர் ச்ட்ரீம் சைஃபர் ஆதரிக்கப்படவில்லை.</string>
<string name="error_otp_type">தற்போதுள்ள OTP வகை இந்த படிவத்தால் அங்கீகரிக்கப்படவில்லை, அதன் சரிபார்ப்பு இனி கிள்ளாக்கை சரியாக உருவாக்காது.</string>
<string name="error_challenge_already_requested">அறைகூவல் ஏற்கனவே கோரப்பட்டது</string>
<string name="error_challenge_already_requested">அறைகூவல் ஏற்கனவே கோரப்பட்டது.</string>
<string name="error_response_already_provided">பதில் ஏற்கனவே வழங்கப்பட்டுள்ளது.</string>
<string name="error_cancel_by_user">பயனரால் ரத்து செய்யப்பட்டது.</string>
<string name="list_entries_show_username_title">பயனர்பெயர்களைக் காட்டு</string>
@@ -662,7 +661,7 @@
<string name="education_donation_summary">ச்திரத்தன்மை, பாதுகாப்பு மற்றும் கூடுதல் அம்சங்களைச் சேர்ப்பதில் அதிகரிக்க உதவுங்கள்.</string>
<string name="html_text_ad_free">பல கடவுச்சொல் மேலாண்மை பயன்பாடுகளைப் போலன்றி, இது &lt;strong&gt; விளம்பர-இலவச &lt;/strong&gt;, &lt;strong&gt; நகலெடுக்கப்பட்ட லிப்ரே மென்பொருள் &lt;/strong&gt; மற்றும் நீங்கள் எந்த பதிப்பைப் பயன்படுத்தினாலும் அதன் சேவையகங்களில் தனிப்பட்ட தரவை சேகரிக்காது.</string>
<string name="html_text_feature_generosity">இந்த &lt;strong&gt; விசுவல் பாணி &lt;/strong&gt; உங்கள் தாராள மனப்பான்மைக்கு நன்றி.</string>
<string name="html_text_donation">திட்டத்திற்கு <i> (பண ரீதியாக, குறியீடு, மொழிபெயர்ப்பு) </i> க்கு &lt;strong&gt; பங்களிப்பு &lt;/strong&gt; மூலம், நீங்கள் தொடர்ந்து வாழவும் வளரவும் உதவுவீர்கள், மேலும் நீங்கள் &lt;strong&gt; கருப்பொருளுக்கும் தகுதி பெறுவீர்கள் &lt;/strong&gt; திறத்தல் செயல்முறை.</string>
<string name="html_text_donation">திட்டத்திற்கு &lt;strong&gt;பங்களிப்பு&lt;/strong&gt; மூலம் <i>(பண ரீதியாக, குறியீடு, மொழிபெயர்ப்பு)</i>, நீங்கள் தொடர்ந்து வாழவும் வளரவும் உதவுவீர்கள், மேலும் நீங்கள் &lt;strong&gt;கருப்பொருளுக்கும்&lt;/strong&gt; தகுதி பெறுவீர்கள்திறத்தல் செயல்முறை.</string>
<string name="upload_attachment">%1$s ஐ பதிவேற்றவும்</string>
<string name="download_initialization">தொடங்குதல்…</string>
<string name="download_progression">செயலில்:%1$d %%</string>

View File

@@ -525,7 +525,6 @@
<string name="advanced_unlock_invalid_key">อ่านกุญแจการปลดล็อกของอุปกรณ์ไม่ได้ โปรดลบข้อมูลออกและเพื่มข้อมูลการปลดล็อกด้วยอุปกรณ์อีกครั้ง</string>
<string name="advanced_unlock_not_recognized">ไม่รู้จักลายนิ้วมือ</string>
<string name="advanced_unlock_prompt_extract_credential_message">แยกข้อมูลประจำตัวออกด้วยข้อมูลการปลดล็อกด้วยอุปกรณ์</string>
<string name="advanced_unlock_scanning_error">การปลดล็อกด้วยอุปกรณ์ผิดพลาด: %1$s</string>
<string name="properties">คุณสมบัติ</string>
<string name="unavailable">ฐานข้อมูลนี้ยังไม่มีข้อมูลการเข้าสูระบบเลย</string>
<string name="database_history">ประวัติ</string>

View File

@@ -488,7 +488,6 @@
<string name="credential_before_click_advanced_unlock_button">Parolayı yazın ve ardından bu düğmeye tıklayın.</string>
<string name="advanced_unlock_prompt_not_initialized">Cihaz kilit açma istemi başlatılamıyor.</string>
<string name="unavailable">Kullanım dışı</string>
<string name="advanced_unlock_scanning_error">Cihaz kilit açma hatası: %1$s</string>
<string name="advanced_unlock_not_recognized">Cihaz kilit açma parmak izi tanınamadı</string>
<string name="advanced_unlock_invalid_key">Cihazın kilit açma anahtarı okunamıyor. Lütfen silin ve kilit açma tanıma prosedürünü tekrarlayın.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Cihaz kilit açma verileriyle veritabanı kimlik bilgilerini çıkarın</string>

View File

@@ -493,7 +493,6 @@
<string name="device_credential">Облікові дані пристрою</string>
<string name="credential_before_click_advanced_unlock_button">Введіть пароль, а потім натисніть цю кнопку.</string>
<string name="advanced_unlock_prompt_not_initialized">Не вдалося ініціалізувати запит на розблокування пристрою.</string>
<string name="advanced_unlock_scanning_error">Помилка розблокування пристрою: %1$s</string>
<string name="advanced_unlock_not_recognized">Не вдалося розпізнати розблокування пристрою</string>
<string name="advanced_unlock_invalid_key">Не вдалося розпізнати ключ розблокування пристрою. Видаліть його й повторіть процедуру створення ключа.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Витягування облікових даних бази даних за допомогою даних розблокування пристрою</string>

View File

@@ -400,7 +400,6 @@
<string name="encrypted_value_stored">Đã lưu trữ mật khẩu được mã hóa</string>
<string name="advanced_unlock_invalid_key">Không thể đọc được mã mở khóa thiết bị. Vui lòng xóa nó và lặp lại quy trình nhận dạng mở khóa.</string>
<string name="advanced_unlock_not_recognized">Không thể nhận dạng vân tay mở khóa thiết bị</string>
<string name="advanced_unlock_scanning_error">Lỗi mở khóa thiết bị: %1$s</string>
<string name="unavailable">Không có sẵn</string>
<string name="advanced_unlock_prompt_not_initialized">Không thể khởi tạo lời nhắc mở khóa thiết bị.</string>
<string name="credential_before_click_advanced_unlock_button">Nhập mật khẩu rồi nhấp vào nút này.</string>

View File

@@ -294,7 +294,7 @@
<string name="html_text_ad_free">不同于大多数的密码管理应用,无论您是使用免费版本还是付费版本的 KeePassDX这都是一款&lt;strong&gt;没有广告&lt;/strong&gt;&lt;strong&gt;基于 copylefted 版权协议的自由软件&lt;/strong&gt;。同时,本软件的任何版本都不会收集您的任何个人信息。</string>
<string name="html_text_buy_pro">通过购买高级版本,您将解锁全部&lt;strong&gt;主题样式&lt;/strong&gt;,重要的是,您会为&lt;strong&gt;社区项目的进行&lt;/strong&gt;提供帮助</string>
<string name="html_text_feature_generosity">&lt;strong&gt;主题样式&lt;/strong&gt;已可用,感谢您的慷慨相助。</string>
<string name="html_text_donation">通过多方面向本项目&lt;strong&gt;作贡献&lt;/strong&gt;,可以是<i>财政、代码、翻译等)</i>,你将帮助本项目继续走下去并茁壮成长,并获得&lt;strong&gt;主题&lt;/strong&gt;解锁步骤资格。</string>
<string name="html_text_donation">通过多方面向本项目&lt;strong&gt;作贡献&lt;/strong&gt;,可以是<i>金钱、代码、翻译等)</i>,你将帮助本项目继续走下去并茁壮成长,并获得&lt;strong&gt;主题&lt;/strong&gt;解锁步骤资格。</string>
<string name="html_text_dev_feature">此功能目前&lt;strong&gt;仍在开发中&lt;/strong&gt;&lt;strong&gt;捐助&lt;/strong&gt;将使该功能很快变得可用。</string>
<string name="html_text_dev_feature_buy_pro">通过购买&lt;strong&gt;专业&lt;/strong&gt;版,</string>
<string name="html_text_dev_feature_contibute">通过&lt;strong&gt;贡献&lt;/strong&gt;</string>
@@ -492,7 +492,6 @@
<string name="device_credential">设备凭据</string>
<string name="credential_before_click_advanced_unlock_button">输入密码,然后点击这个按钮。</string>
<string name="advanced_unlock_prompt_not_initialized">无法初始化设备解锁提示。</string>
<string name="advanced_unlock_scanning_error">设备解锁出错:%1$s</string>
<string name="advanced_unlock_not_recognized">无法识别设备解锁印记</string>
<string name="advanced_unlock_invalid_key">无法读取设备解锁密钥。请删除它,并重复解锁识别步骤。</string>
<string name="advanced_unlock_prompt_extract_credential_message">用设备解锁数据提取数据库凭据</string>

View File

@@ -29,7 +29,6 @@
<string name="advanced_unlock_prompt_not_initialized">無法初始化裝置解鎖提示。</string>
<string name="advanced_unlock_prompt_store_credential_message">即使你使用裝置解鎖識別,你仍然需要記住你的解鎖憑證。</string>
<string name="advanced_unlock_prompt_store_credential_title">裝置解鎖連線</string>
<string name="advanced_unlock_scanning_error">裝置解鎖出錯:%1$s</string>
<string name="advanced_unlock_tap_delete">點擊刪除裝置解鎖密鑰</string>
<string name="advanced_unlock_timeout">裝置解鎖超時</string>
<string name="allow">允許</string>

View File

@@ -407,7 +407,6 @@
<string name="encrypted_value_stored">Encrypted password stored</string>
<string name="advanced_unlock_invalid_key">Cannot read the device unlock key. Please delete it and repeat the unlock recognition procedure.</string>
<string name="advanced_unlock_not_recognized">Could not recognize device unlock print</string>
<string name="advanced_unlock_scanning_error">Device unlock error: %1$s</string>
<string name="unavailable">Unavailable</string>
<string name="advanced_unlock_prompt_not_initialized">Unable to initialize device unlock prompt.</string>
<string name="credential_before_click_advanced_unlock_button">Type in the password, and then click this button.</string>

View File

@@ -10,7 +10,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.11.0'
classpath 'com.android.tools.build:gradle:8.11.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View File

@@ -42,5 +42,6 @@ dependencies {
// Crypto
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
androidTestImplementation "androidx.test:runner:$android_test_version"
testImplementation "androidx.test:runner:$android_test_version"
}

View File

@@ -892,6 +892,7 @@ open class Database {
return mSearchHelper.createVirtualGroupWithSearchResult(this,
SearchParameters().apply {
searchQuery = searchInfoString
allowEmptyQuery = false
searchInTitles = true
searchInUsernames = false
searchInPasswords = false

View File

@@ -24,52 +24,115 @@ import java.util.*
class TemplateBuilder {
private val urlAttribute = TemplateAttribute(TemplateField.LABEL_URL, TemplateAttributeType.TEXT)
private val usernameAttribute = TemplateAttribute(TemplateField.LABEL_USERNAME, TemplateAttributeType.TEXT)
private val urlAttribute = TemplateAttribute(
label = TemplateField.LABEL_URL,
type = TemplateAttributeType.TEXT
)
private val usernameAttribute = TemplateAttribute(
label = TemplateField.LABEL_USERNAME,
type = TemplateAttributeType.TEXT
)
private val notesAttribute = TemplateAttribute(
TemplateField.LABEL_NOTES,
TemplateAttributeType.TEXT,
false,
TemplateAttributeOption().apply {
label = TemplateField.LABEL_NOTES,
type = TemplateAttributeType.TEXT,
protected = false,
options = TemplateAttributeOption().apply {
setNumberLinesToMany()
})
private val holderAttribute = TemplateAttribute(TemplateField.LABEL_HOLDER, TemplateAttributeType.TEXT)
private val numberAttribute = TemplateAttribute(TemplateField.LABEL_NUMBER, TemplateAttributeType.TEXT)
private val cvvAttribute = TemplateAttribute(TemplateField.LABEL_CVV, TemplateAttributeType.TEXT, true)
private val pinAttribute = TemplateAttribute(TemplateField.LABEL_PIN, TemplateAttributeType.TEXT, true)
private val nameAttribute = TemplateAttribute(TemplateField.LABEL_NAME, TemplateAttributeType.TEXT)
private val placeOfIssueAttribute = TemplateAttribute(TemplateField.LABEL_PLACE_OF_ISSUE, TemplateAttributeType.TEXT)
}
)
private val holderAttribute = TemplateAttribute(
label = TemplateField.LABEL_HOLDER,
type = TemplateAttributeType.TEXT
)
private val numberAttribute = TemplateAttribute(
label = TemplateField.LABEL_NUMBER,
type = TemplateAttributeType.TEXT
)
private val cvvAttribute = TemplateAttribute(
label = TemplateField.LABEL_CVV,
type = TemplateAttributeType.TEXT,
protected = true
)
private val pinAttribute = TemplateAttribute(
label = TemplateField.LABEL_PIN,
type = TemplateAttributeType.TEXT,
protected = true
)
private val nameAttribute = TemplateAttribute(
label = TemplateField.LABEL_NAME,
type = TemplateAttributeType.TEXT
)
private val placeOfIssueAttribute = TemplateAttribute(
label = TemplateField.LABEL_PLACE_OF_ISSUE,
type = TemplateAttributeType.TEXT
)
private val dateOfIssueAttribute = TemplateAttribute(
TemplateField.LABEL_DATE_OF_ISSUE,
TemplateAttributeType.DATETIME,
false,
TemplateAttributeOption().apply {
label = TemplateField.LABEL_DATE_OF_ISSUE,
type = TemplateAttributeType.DATETIME,
protected = false,
options = TemplateAttributeOption().apply {
setDateFormatToDate()
})
}
)
private val expirationDateAttribute = TemplateAttribute(
TemplateField.LABEL_EXPIRATION,
TemplateAttributeType.DATETIME,
false,
TemplateAttributeOption().apply {
label = TemplateField.LABEL_EXPIRATION,
type = TemplateAttributeType.DATETIME,
protected = false,
options = TemplateAttributeOption().apply {
setDateFormatToDate()
})
private val emailAddressAttribute = TemplateAttribute(TemplateField.LABEL_EMAIL_ADDRESS, TemplateAttributeType.TEXT)
private val passwordAttribute = TemplateAttribute(TemplateField.LABEL_PASSWORD, TemplateAttributeType.TEXT, true)
private val ssidAttribute = TemplateAttribute(TemplateField.LABEL_SSID, TemplateAttributeType.TEXT)
}
)
private val emailAddressAttribute = TemplateAttribute(
label = TemplateField.LABEL_USERNAME,
type = TemplateAttributeType.TEXT,
options = TemplateAttributeOption().apply {
alias = TemplateField.LABEL_EMAIL_ADDRESS
}
)
private val passwordAttribute = TemplateAttribute(
label = TemplateField.LABEL_PASSWORD,
type = TemplateAttributeType.TEXT,
protected = true
)
private val ssidAttribute = TemplateAttribute(
label = TemplateField.LABEL_SSID,
type = TemplateAttributeType.TEXT
)
private val wirelessTypeAttribute = TemplateAttribute(
TemplateField.LABEL_TYPE,
TemplateAttributeType.LIST,
false,
TemplateAttributeOption().apply{
label = TemplateField.LABEL_TYPE,
type = TemplateAttributeType.LIST,
protected = false,
options = TemplateAttributeOption().apply{
setListItems("WPA3", "WPA2", "WPA", "WEP")
default = "WPA2"
})
private val tokenAttribute = TemplateAttribute(TemplateField.LABEL_TOKEN, TemplateAttributeType.TEXT)
private val publicKeyAttribute = TemplateAttribute(TemplateField.LABEL_PUBLIC_KEY, TemplateAttributeType.TEXT)
private val privateKeyAttribute = TemplateAttribute(TemplateField.LABEL_PRIVATE_KEY, TemplateAttributeType.TEXT, true)
private val seedAttribute = TemplateAttribute(TemplateField.LABEL_SEED, TemplateAttributeType.TEXT, true)
private val bicAttribute = TemplateAttribute(TemplateField.LABEL_BIC, TemplateAttributeType.TEXT)
private val ibanAttribute = TemplateAttribute(TemplateField.LABEL_IBAN, TemplateAttributeType.TEXT)
}
)
private val tokenAttribute = TemplateAttribute(
label = TemplateField.LABEL_TOKEN,
type = TemplateAttributeType.TEXT
)
private val publicKeyAttribute = TemplateAttribute(
label = TemplateField.LABEL_PUBLIC_KEY,
type = TemplateAttributeType.TEXT
)
private val privateKeyAttribute = TemplateAttribute(
label = TemplateField.LABEL_PRIVATE_KEY,
type = TemplateAttributeType.TEXT,
protected = true
)
private val seedAttribute = TemplateAttribute(
label = TemplateField.LABEL_SEED,
type = TemplateAttributeType.TEXT,
protected = true
)
private val bicAttribute = TemplateAttribute(
label = TemplateField.LABEL_BIC,
type = TemplateAttributeType.TEXT
)
private val ibanAttribute = TemplateAttribute(
label = TemplateField.LABEL_IBAN,
type = TemplateAttributeType.TEXT
)
val email: Template
get() {

View File

@@ -123,11 +123,9 @@ class SearchHelper {
*/
fun searchInEntry(entry: Entry,
searchParameters: SearchParameters): Boolean {
val searchQuery = searchParameters.searchQuery
// Not found if the search string is empty
if (searchQuery.isEmpty())
return false
if (searchParameters.searchQuery.isEmpty())
return searchParameters.allowEmptyQuery
// Exclude entry expired
if (!searchParameters.searchInExpired) {
@@ -205,10 +203,11 @@ class SearchHelper {
}
regex.matches(stringToCheck)
} else {
searchParameters.searchQuery.split(" ").any { word ->
specialComparison?.invoke(stringToCheck, word)
?: stringToCheck.contains(word, !searchParameters.caseSensitive)
}
specialComparison?.invoke(stringToCheck, searchParameters.searchQuery)
?: stringToCheck.contains(
searchParameters.searchQuery,
!searchParameters.caseSensitive
)
}
}
}

View File

@@ -27,6 +27,7 @@ import android.os.Parcelable
*/
class SearchParameters() : Parcelable{
var searchQuery: String = ""
var allowEmptyQuery = true
var caseSensitive = false
var isRegex = false
@@ -49,6 +50,7 @@ class SearchParameters() : Parcelable{
constructor(parcel: Parcel) : this() {
searchQuery = parcel.readString() ?: searchQuery
allowEmptyQuery = parcel.readByte() != 0.toByte()
caseSensitive = parcel.readByte() != 0.toByte()
isRegex = parcel.readByte() != 0.toByte()
searchInTitles = parcel.readByte() != 0.toByte()
@@ -69,6 +71,7 @@ class SearchParameters() : Parcelable{
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(searchQuery)
parcel.writeByte(if (allowEmptyQuery) 1 else 0)
parcel.writeByte(if (caseSensitive) 1 else 0)
parcel.writeByte(if (isRegex) 1 else 0)
parcel.writeByte(if (searchInTitles) 1 else 0)

View File

@@ -0,0 +1,23 @@
تطبيق KeePassDX هو <b>خزنة ومدير كلمات سر</b> يسمح بتحرير <b>البيانات المُعمَّاة في ملف واحد</b> بتنسيق KeePass المفتوح و<b>ملء النماذج بطريقة آمنة</b>، ولا يتطلب <b>اتصالاً بالإنترنت</b> ويتكامل مع معايير تصميم أندرويد. التطبيق <b>مفتوح المصدر، وبدون إعلانات</b>.
<b>الميزات</b>
- إنشاء ملفات/إدخالات وقواعد بيانات ومجموعات.
- دعم ملفات .kdb و .kdbx (الإصدار 1 إلى 4) باستخدام خوارزميات AES - Twofish - ChaCha20 - Argon2.
- متوافق مع غالبية البرامج البديلة (KeePass، KeePassXC، KeeWeb، ...).
- يسمح بفتح ونسخ حقول URI / URL بسرعة.
- التعرف البيومتري لفتح القفل بسرعة (بصمة الإصبع / فتح الوجه / ...).
- إدارة كلمات السر لمرة واحدة (HOTP / TOTP) للمصادقة الثنائية (2FA).
- تصميم Material مع سمات.
- التعبئة التلقائية والتكامل.
- لوحة مفاتيح لملء الحقول.
- قوالب ديناميكية.
- سجل لكل إدخال.
- إدارة دقيقة للإعدادات.
- الرمز مكتوب بلغات أصيلة (Kotlin / Java / JNI / C).
يمكنك التبرع أو شراء الإصدار الاحترافي للحصول على خدمة أفضل وتطوير سريع للميزات التي تريدها: <a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>
المشروع يتطور باستمرار. لا تتردد في التحقق من حالة تطوير التحديثات القادمة: <a href="https://github.com/Kunzisoft/KeePassDX/projects">https://github.com/Kunzisoft/KeePassDX/projects</a>
أرسل العلل إلى: <a href="https://github.com/Kunzisoft/KeePassDX/issues">https://github.com/Kunzisoft/KeePassDX/issues</a>

View File

@@ -0,0 +1 @@
مدير وحافظة كلمات سر آمنة ومفتوحة المصدر

View File

@@ -0,0 +1 @@
KeePassDX - مدير كلمات سر مفتوح المصدر

View File

@@ -0,0 +1,23 @@
KeePassDX je <b>trezor a správce hesel</b>, který umožňuje upravovat <b>šifrovaná data v jednom souboru</b> v otevřeném formátu KeePass a <b>bezpečně vyplňovat formuláře</b>, <b>nevyžaduje připojení k internetu</b> a integruje standardy designu systému Android. Aplikace je <b>open source a bez reklam</b>.
<b>Funkce</b>
- Vytváření databázových souborů / záznamů a skupin.
- Podpora souborů .kdb a .kdbx (verze 1 až 4) s algoritmy AES - Twofish - ChaCha20 - Argon2.
- Kompatibilní s většinou alternativních programů (KeePass, KeePassXC, KeeWeb, …).
- Umožňuje rychlé otevírání a kopírování polí URI / URL.
- Biometrické rozpoznávání pro rychlé odemykání (otisk prstu / rozpoznání obličeje / …).
- Správa jednorázových hesel (HOTP / TOTP) pro dvoufaktorové ověřování (2FA).
- Material Design s motivy.
- Automatické vyplňování a integrace.
- Klávesnice pro vyplňování polí.
- Dynamické šablony.
- Historie každého záznamu.
- Přesná správa nastavení.
- Kód napsaný v nativních jazycích (Kotlin / Java / JNI / C).
Můžete přispět nebo si koupit profesionální verzi pro lepší služby a rychlejší vývoj funkcí, které si přejete: <a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>
Projekt se neustále vyvíjí. Neváhejte zkontrolovat stav vývoje dalších aktualizací: <a href="https://github.com/Kunzisoft/KeePassDX/projects">https://github.com/Kunzisoft/KeePassDX/projects</a>
Problémy hlaste na: <a href="https://github.com/Kunzisoft/KeePassDX/issues">https://github.com/Kunzisoft/KeePassDX/issues</a>

View File

@@ -0,0 +1 @@
Bezpečný open-source trezor a správce hesel

View File

@@ -0,0 +1 @@
KeePassDX FOSS trezor hesel

View File

@@ -1,19 +1,20 @@
KeePassDX ist ein <b>Passwort-Safe und -Manager</b>, der es ermöglicht, verschlüsselte <b>Daten in einer einzigen Datei</b> im offenen KeePass-Format zu bearbeiten und <b>die Formulare auf sichere Weise auszufüllen</b>, <b>erfordert keine Internetverbindung</b> und integriert Android-Designstandards. Die App ist <b>Open Source, ohne Werbung</b>.
<b>Funktionalitäten</b>
- Erstellung von Datenbanken / Einträgen und Gruppen.
- Unterstützung von .kdb- und .kdbx-Dateien (Version 1 bis 4) mit AES - Twofish - ChaCha20 - Argon2 Algorithmus.
- Kompatibel mit den meisten alternativen Programmen (KeePass, KeePassXC, KeeWeb, ...).
- Ermöglicht das schnelle Kopieren von Feldern und das Öffnen von URIs /URLs.
- Biometrische Erkennung für eine schnelle Entsperrung (Fingerabdrücke / Entsperrung durch Gesicht / ...).
- Verwaltung von Einmalpasswörtern (One-Time Password HOTP / TOTP) für die Zwei-Faktor-Authentifizierung (2FA).
- Material Design mit Themen.
- Automatisches Ausfüllen von Feldern.
- Tastatur zum Ausfüllen von Feldern.
- Dynamische Schablonen.
- Historie jedes Eintrags.
- Präzise Verwaltung der Parameter.
- In nativen Sprachen geschriebener Code (Kotlin / Java / JNI / C).
- Erstellung von Datenbanken / Einträgen und Gruppen.
- Unterstützung von .kdb- und .kdbx-Dateien (Version 1 bis 4) mit AES - Twofish - ChaCha20 - Argon2 Algorithmus.
- Kompatibel mit den meisten alternativen Programmen (KeePass, KeePassXC, KeeWeb, ...).
- Ermöglicht das schnelle Kopieren von Feldern und das Öffnen von URIs /URLs.
- Biometrische Erkennung für eine schnelle Entsperrung (Fingerabdrücke / Entsperrung durch Gesicht / ...).
- Verwaltung von Einmalpasswörtern (One-Time Password HOTP / TOTP) für die Zwei-Faktor-Authentifizierung (2FA).
- Material Design mit Themen.
- Automatisches Ausfüllen von Feldern.
- Tastatur zum Ausfüllen von Feldern.
- Dynamische Schablonen.
- Historie jedes Eintrags.
- Präzise Verwaltung der Parameter.
- In nativen Sprachen geschriebener Code (Kotlin / Java / JNI / C).
Sie können spenden oder die Pro-Version kaufen, um einen besseren Service und eine schnelle Entwicklung der von Ihnen gewünschten Funktionen zu erhalten: <a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>

View File

@@ -1 +1 @@
Sicherer Open-Source-Passwort-Safe und -Manager
Sicherer Open-Source-Passwort-Tresor und -Manager

View File

@@ -0,0 +1 @@
KeePassDX FOSS-Passworttresor

View File

@@ -3,6 +3,6 @@
* Fix Group notes #2053
* Fix Dialog background #2005 #2004 (Thx @codokie)
* Fix OTP configuration #2042 #2065 (Thx @Dev-ClayP)
* Fix small UI elements #1987 #2007 (Thx @ymcx)
* Fix small UI elements #1987 #2007 (Thx @ymcx)
* RTL layout support #2021 (Thx @codokie)
* App Metadata to translation #1823

View File

@@ -0,0 +1,5 @@
* Fix Autofill Registration #2089
* Fix Biometric errors #2081
* Fixed timestamp in copy file #1981 #1983
* Fix Template Email #1986
* Fix Search #2096

View File

@@ -0,0 +1,3 @@
* Fix UnlockManager #2098 #2101
* Auto device unlock prompt #2105
* Small fixes ##2066

View File

@@ -1,19 +1,20 @@
KeePassDX is a <b>password safe and manager</b> allows editing <b>encrypted data in a single file</b> in the open KeePass format and <b>fill in the forms in a secure way</b>, requires <b>no Internet connection</b> and integrates Android design standards. The app is <b>open source, with no advertising</b>.
<b>Features</b>
- Create database files / entries and groups.
- Support for .kdb and .kdbx files (version 1 to 4) with AES - Twofish - ChaCha20 - Argon2 algorithm.
- Compatible with the majority of alternative programs (KeePass, KeePassXC, KeeWeb, …).
- Allows opening and copying URI / URL fields quickly.
- Biometric recognition for fast unlocking (fingerprint / face unlock / …).
- One-time password management (HOTP / TOTP) for two-factor authentication (2FA).
- Material design with themes.
- Auto-Fill and integration.
- Field filling keyboard.
- Dynamic templates.
- History of each entry.
- Precise management of settings.
- Code written in native languages (Kotlin / Java / JNI / C).
- Create database files / entries and groups.
- Support for .kdb and .kdbx files (version 1 to 4) with AES - Twofish - ChaCha20 - Argon2 algorithm.
- Compatible with the majority of alternative programs (KeePass, KeePassXC, KeeWeb, …).
- Allows opening and copying URI / URL fields quickly.
- Biometric recognition for fast unlocking (fingerprint / face unlock / …).
- One-time password management (HOTP / TOTP) for two-factor authentication (2FA).
- Material design with themes.
- Auto-Fill and integration.
- Field filling keyboard.
- Dynamic templates.
- History of each entry.
- Precise management of settings.
- Code written in native languages (Kotlin / Java / JNI / C).
You can donate or buy the pro version for better service and a quick development of features you want: <a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>

View File

@@ -0,0 +1,23 @@
KeePassDX es un <b> Administrador de contraseñas seguro </b> que permite editar <b>datos cifrados en un solo archivo</b> en el formato abierto de KeePass permite <b>completar los formularios de manera segura</b>, no requiere<b>conexión a internet</b> e integra los estándares de diseño de Android. La aplicación es <b> de código abierto, sin publicidad</b>.
<b>Funciones</b>
- Crea archivos de bases de datos / entradas y grupos.
- Compatible con archivos .kdb y .kdbx (versiones 1 a 4) con AES - Twofish - ChaCha20 y el algoritmo Argon2 ..
- Compatible con la mayoría de programas alternativos (KeePass, KeePassXC, KeeWeb, …).
- Permite abrir y copiar campos URI / URL rapidamente.
- Reconocimiento biometrico para desbloqueo rapido(huella digital/ reconocimiento facial/ …).
- Gestor de claves de un solo uso(HOTP / TOTP) autentificación de dos factores(2FA).
- Diseño Material con temas.
- Autocompletado (Auto-Fill) e integración.
- Teclado para llenado de campos.
- Plantillas dinámicas.
- Historial de cada entrada.
- Gestión precisa de configuraciones.
- Código escrito en lenguajes nativos (Kotlin / Java / JNI / C).
> Puedes donar o comprar la versión Pro para obtener un mejor servicio y un desarrollo más rápido de las funciones que deseas:<a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>
El proyecto está en constante evolución. No dudes en consultar el estado de desarrollo de las próximas actualizaciones: <a href="https://github.com/Kunzisoft/KeePassDX/projects">https://github.com/Kunzisoft/KeePassDX/projects</a>
Reporta problemas: <a href="https://github.com/Kunzisoft/KeePassDX/issues">https://github.com/Kunzisoft/KeePassDX/issues</a>

View File

@@ -0,0 +1 @@
Administrador de contraseñas seguro de código abierto

View File

@@ -0,0 +1 @@
KeePassDX - FOSS Administrador de contraseñas

View File

@@ -0,0 +1,23 @@
KeePassDX on <b>salasõnalaegas ja -haldur</b>, mis võimaldab <b>muuta krüptitud andmeid avatud KeePass vormingus ühes failis</b> ja <b>täita vorme turvaliselt</b>, ei vaja <b>internetiühendust</b> ning järgib Androidi kujundus-standardeid. Rakendus on <b>avatud lähtekoodiga ja reklaamivaba</b>.
<b>Funktsionaaldus</b>
- Andmebaasi failide, kirjete ja gruppide loomine.
- .kdb ja .kdbx failide tugi (versioonid 1 kuni 4) AES - Twofish - ChaCha20 - Argon2 algoritmidega.
- Ühildub enamiku alternatiivsete prakendustega (KeePass, KeePassXC, KeeWeb, …).
- Kiire võrguaadresside (URI / URL) väljade avamine ja kopeerimine.
- Biomeetriline autentimine kiireks lukustuse eemaldamiseks (sõrmejälg / näotuvastus / …).
- Ühekordsete slasõnade haldus (HOTP / TOTP) kahefaktorilise autentimise (2FA) jaoks.
- MD kujunduskeel koos kujundustega.
- Automaattäide ja lõiming.
- Klahvistik väljade täitmiseks.
- Dünaamilised mallid.
- Iga kirje ajalugu.
- Täpne seadistuste haldus.
- Lähtekood on kirjutatud platvormiomases keeltes (Kotlin / Java / JNI / C).
Parema teenuse ja kiirema arendustöö huvides saad sa annetada või osta pro versiooni: <a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>
Projekt areneb pidevalt. Ära kõhkle kontrollimast järgmiste arenduste olekut: <a href="https://github.com/Kunzisoft/KeePassDX/projects">https://github.com/Kunzisoft/KeePassDX/projects</a>
Veateated lisa palun siia: <a href="https://github.com/Kunzisoft/KeePassDX/issues">https://github.com/Kunzisoft/KeePassDX/issues</a>

View File

@@ -0,0 +1 @@
Turvaline avatud lähtekoodiga salasõnalaegas ja -haldur

View File

@@ -0,0 +1 @@
KeePassDX: sinu salasõnalaegas

View File

@@ -0,0 +1,5 @@
* Correction de l'enregistrement pour le remplissage automatique #2089
* Correction des erreurs biométriques #2081
* Correction du timestamp dans le fichier de copie #1981 #1983
* Correction des gabaris Email #1986
* Correction de la recherche #2096

View File

@@ -0,0 +1,3 @@
* Correction UnlockManager #2098 #2101
* Invite de déverrouillage automatique de l'appareil #2105
* Petites corrections ##2066

View File

@@ -2,19 +2,19 @@ KeePassDX est un <b>coffre-fort et un gestionnaire de mots de passe</b> qui perm
<b>Fonctionnalités</b>
- Création de bases de données / entrées et groupes.
- Support des fichiers .kdb et .kdbx (version 1 à 4) avec algorithme AES - Twofish - ChaCha20 - Argon2.
- Compatible avec la majorité des programmes alternatifs (KeePass, KeePassXC, KeeWeb, …)
- Permet la copie rapide de champs et l'ouverture d'URI /URL.
- Reconnaissance biométrique pour un déblocage rapide (Empreintes digitales / Déverouillage par visage / …).
- Gestion des mots de passe à usage unique (One-Time Password HOTP / TOTP) pour l'authentification à deux facteurs (2FA).
- Material design avec thèmes.
- Remplissage automatique de champs.
- Clavier de remplissage de champs.
- Gabarits dynamiques.
- Historique de chaque entrée.
- Gestion précise des paramètres.
- Code écrit en langages natifs (Kotlin / Java / JNI / C).
- Création de bases de données / entrées et groupes.
- Support des fichiers .kdb et .kdbx (version 1 à 4) avec algorithme AES - Twofish - ChaCha20 - Argon2.
- Compatible avec la majorité des programmes alternatifs (KeePass, KeePassXC, KeeWeb, …)
- Permet la copie rapide de champs et l'ouverture d'URI /URL.
- Reconnaissance biométrique pour un déblocage rapide (Empreintes digitales / Déverouillage par visage / …).
- Gestion des mots de passe à usage unique (One-Time Password HOTP / TOTP) pour l'authentification à deux facteurs (2FA).
- Material design avec thèmes.
- Remplissage automatique de champs.
- Clavier de remplissage de champs.
- Gabarits dynamiques.
- Historique de chaque entrée.
- Gestion précise des paramètres.
- Code écrit en langages natifs (Kotlin / Java / JNI / C).
Vous pouvez faire un don ou acheter la version pro pour un meilleur service et un développement rapide des fonctionnalités que vous souhaitez : <a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>

View File

@@ -1,19 +1,20 @@
KeePassDX は <b>オープンソース</b>かつ<b>広告なし</b>です。<b>複数の形式に対応する KeePass パスワード マネージャー</b>Android の設計基準が組み込まれており、パスワード、鍵、デジタル ID を安全な方法で保存して使用できます。<b>インターネットに接続する必要はありません</b>
KeePassDXは<b>パスワードセーフ兼マネージャー</b>で、オープンなKeePass形式で<b>暗号化されたデータを単一ファイルとして編集</b>したり、<b>安全な方法でフォームに入力</b>したりできます。<b>インターネット接続が不要</b>で、Androidの標準デザインへ準拠しています。このアプリは<b>オープンソースで広告がありません</b>
<b>機能</b>
- データベースファイル / エントリー・グループの作成
- .kdb、.kdbx ファイルバージョン1から4に対応。AES、Twofish、ChaCha20、Argon2 アルゴリズムが使用可能
- 主流の代替ソフトウェアKeePass、KeePassXC, KeeWeb など)との互換性あり
- URI / URL フィールドは開くのもコピーするのもすばやく行えます
- 生体認証を使った高速ロック解除 (指紋認証 / 顔認証 / …)
- 2 要素認証2FAのためのワンタイムパスワード管理HOTP / TOTP
- マテリアルデザインに準拠した複数のテーマ
- 自動入力機能の統合
- フィールド入力用のキーボード
- ダイナミックテンプレート
- エントリーごとの履歴
- 設定の細かな管理
- コードはネイティブ言語Kotlin / Java / JNI / Cで書かれています
- データベースファイル / エントリー・グループの作成
- .kdb、.kdbx ファイルバージョン1から4に対応。AES、Twofish、ChaCha20、Argon2 アルゴリズムが使用可能
- 主流の代替ソフトウェアKeePass、KeePassXC, KeeWeb など)との互換性あり
- URI / URL フィールドは開くのもコピーするのもすばやく行えます
- 生体認証を使った高速ロック解除 (指紋認証 / 顔認証 / …
- 2 要素認証2FAのためのワンタイムパスワード管理HOTP / TOTP
- マテリアルデザインに準拠した複数のテーマ
- 自動入力機能の統合
- フィールド入力用のキーボード
- ダイナミックテンプレート
- エントリーごとの履歴
- 設定の細かな管理
- コードはネイティブ言語Kotlin / Java / JNI / Cで書かれています
寄付または pro バージョンの購入はサービスの改善と必要な機能の迅速な開発につながります: <a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>

View File

@@ -0,0 +1 @@
KeePassDX - FOSS パスワードセーフ

View File

@@ -0,0 +1,23 @@
KeePassDX е <b>безбеден и менаџер за лозинки</b> кој овозможува уредување на <b>шифрирани податоци во една датотека</b> во отворен KeePass формат и <b>пополнување на формуларите на безбеден начин</b>, <b>не бара интернет конекција</b> и ги интегрира стандардите за дизајн на Android. Апликацијата е <b>отворен код, без рекламирање</b>.
<b>Карактеристики</b>
- Креирање датотеки / записи и групи во базата на податоци.
- Поддршка за .kdb и .kdbx датотеки (верзија од 1 до 4) со AES - Twofish - ChaCha20 - Argon2 алгоритам.
- Компатибилен со повеќето алтернативни програми (KeePass, KeePassXC, KeeWeb, …).
- Овозможува брзо отворање и копирање на URI / URL полиња.
- Биометриско препознавање за брзо отклучување (отпечаток од прст / отклучување со лице / …).
- Еднократно управување со лозинки (HOTP / TOTP) за двофакторска автентикација (2FA).
- Дизајн на материјали со теми.
- Автоматско пополнување и интеграција.
- Тастатура за пополнување полиња.
- Динамични шаблони.
- Историја на секој запис.
- Прецизно управување со поставките.
- Код напишан на мајчини јазици (Kotlin / Java / JNI / C).
Можете да донирате или купите про верзијата за подобра услуга и брз развој на функциите што ги сакате: <a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>
Проектот постојано се развива. Не двоумете се да го проверите статусот на развој на следните ажурирања: <a href="https://github.com/Kunzisoft/KeePassDX/projects">https://github.com/Kunzisoft/KeePassDX/projects</a>
Испратете ги проблемите до: <a href="https://github.com/Kunzisoft/KeePassDX/issues">https://github.com/Kunzisoft/KeePassDX/issues</a>

View File

@@ -0,0 +1 @@
Безбеден менаџер за лозинки со отворен код

View File

@@ -0,0 +1 @@
KeePassDX - Безбедносен софтвер за лозинки и е слободен софтвер

View File

@@ -0,0 +1,23 @@
KeePassDX is een <b>wachtwoordkluis en -manager</b> voor het bewerken van <b>versleutelde gegevens in één bestand</b> in het open KeePass-formaat en waarmee je <b>op een veilige manier inlogvelden vult</b>, <b>zonder internetverbinding</b> en geheel volgens de Android ontwerpstandaarden. De app is <b>open source, zonder advertenties</b>.
<b>Functies</b>
- Aanmaken van databasebestanden, -items en -groepen.
- Ondersteuning voor .kdb en .kdbx bestanden (version 1 t/m 4) met AES - Twofish - ChaCha20 - Argon2 algoritme.
- Compatibel met de meeste alternatieve programma's (KeePass, KeePassXC, KeeWeb, …).
- Snel openen en kopiëren van URI / URL-velden.
- Biometrische herkenning voor snelle ontgrendeling (vingerafdruk / gezichtsherkenning / …).
- Beheer van eenmalige wachtwoorden (HOTP / TOTP) voor twee-factor-authenticatie (2FA).
- Material design thema's.
- Automatisch invullen en integratie.
- Toestenbord voor invulling van velden.
- Dynamische sjablonen.
- Geschiedenis van elke invoer.
- Nauwkeurig beheer van instellingen.
- Code geschreven in moderne programmeertalen (Kotlin / Java / JNI / C).
Je kunt doneren of de pro-versie kopen voor betere ondersteuning en een snelle ontwikkeling van de functies die je wilt: <a href="https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro">https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro</a>
Het project is voortdurend in ontwikkeling. Bekijk gerust de ontwikkelingsstatus van de volgende updates op: <a href="https://github.com/Kunzisoft/KeePassDX/projects">https://github.com/Kunzisoft/KeePassDX/projects</a>
Meld problemen bij: <a href="https://github.com/Kunzisoft/KeePassDX/issues">https://github.com/Kunzisoft/KeePassDX/issues</a>

View File

@@ -0,0 +1 @@
Veilige open-source wachtwoordkluis en -beheer

Some files were not shown because too many files have changed in this diff Show More