Compare commits

...

1074 Commits

Author SHA1 Message Date
J-Jamet
955c9992d0 Merge branch 'release/2.5' 2020-05-10 19:32:37 +02:00
J-Jamet
87606818cd Rename ReadMe.md to README.md 2020-05-10 16:18:32 +02:00
J-Jamet
ffcd264bac Upgrade manually Readme with pull request #540 2020-05-10 16:17:41 +02:00
J-Jamet
04c70cf99d Update fastlane full description (En/Fr - Free/Pro) 2020-05-10 15:31:07 +02:00
J-Jamet
0e0b6fca07 Fix small issue 2020-05-10 11:48:56 +02:00
J-Jamet
b24a789352 Fix small string 2020-05-10 11:46:35 +02:00
J-Jamet
3d4f447037 Replace <strong> tags 2020-05-10 11:40:15 +02:00
J-Jamet
7aa52ca00a Merge branch 'master' of https://hosted.weblate.org/projects/keepass-dx/strings into translations 2020-05-10 11:27:58 +02:00
J-Jamet
a94c9050c5 Update CHANGELOG and full description 2020-05-10 11:05:23 +02:00
J-Jamet
9b18e71e12 Upgrade CHANGELOGS 2020-05-10 10:14:37 +02:00
J-Jamet
d5ff048bd0 Remove allow_copy_password_key from application preferences 2020-05-10 10:12:18 +02:00
Allan Nordhøy
76f44f2573 Translated using Weblate (Romanian)
Currently translated at 91.6% (396 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ro/
2020-05-10 02:13:06 +02:00
Allan Nordhøy
9988f1a76f Translated using Weblate (Norwegian Bokmål)
Currently translated at 77.7% (336 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2020-05-10 02:13:05 +02:00
Xiang Xu
1559376bc2 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-05-10 02:13:05 +02:00
ihor_ck
4d0f252acd Translated using Weblate (Ukrainian)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/uk/
2020-05-10 02:13:05 +02:00
Allan Nordhøy
d5a84e66ad Translated using Weblate (Swedish)
Currently translated at 76.8% (332 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sv/
2020-05-10 02:12:59 +02:00
Retrial
1dfe3453f4 Translated using Weblate (Greek)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/el/
2020-05-10 02:12:59 +02:00
ihor_ck
a7dc9f31b9 Translated using Weblate (Ukrainian)
Currently translated at 53.7% (232 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/uk/
2020-05-07 09:36:51 +02:00
76c72cfe
d275f18b3c Translated using Weblate (Swedish)
Currently translated at 76.8% (332 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sv/
2020-05-07 09:36:48 +02:00
WaldiS
d770429401 Translated using Weblate (Polish)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2020-05-07 09:36:47 +02:00
Retrial
501f0be99f Translated using Weblate (Greek)
Currently translated at 88.1% (381 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/el/
2020-05-07 09:36:47 +02:00
J-Jamet
a70122f873 Fix small error 2020-05-06 18:19:07 +02:00
J-Jamet
1e1bd15a06 Replace vararg for non-compatible devices 2020-05-06 18:14:41 +02:00
J-Jamet
164fb1f4f5 Fix small warning 2020-05-06 17:45:56 +02:00
J-Jamet
0206514bdb Rollback stylish super implementation 2020-05-06 17:33:12 +02:00
J-Jamet
ecf2b42fa4 Fix field reference 2020-05-06 16:47:47 +02:00
ihor_ck
df683aeea9 Translated using Weblate (Ukrainian)
Currently translated at 32.6% (141 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/uk/
2020-05-05 21:12:01 +02:00
Anonymous
cdd05958f7 Translated using Weblate (Ukrainian)
Currently translated at 32.6% (141 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/uk/
2020-05-05 21:11:59 +02:00
solokot
4ec5fc05fb Translated using Weblate (Russian)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-05-05 21:11:59 +02:00
Lucas Nunes
4019cafac1 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_BR/
2020-05-05 21:11:58 +02:00
J. Lavoie
4333e179d1 Translated using Weblate (Italian)
Currently translated at 95.3% (412 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-05-05 21:11:56 +02:00
Balázs Meskó
87d67be428 Translated using Weblate (Hungarian)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/
2020-05-05 21:11:55 +02:00
J. Lavoie
350863adff Translated using Weblate (French)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-05-05 21:11:55 +02:00
J. Lavoie
d125fc46a0 Translated using Weblate (German)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-05-05 21:11:55 +02:00
jan madsen
5f4c079071 Translated using Weblate (Danish)
Currently translated at 98.1% (424 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2020-05-05 21:11:55 +02:00
zeritti
5ec576a76e Translated using Weblate (Czech)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2020-05-05 21:11:54 +02:00
Kunzisoft
9edb00ebbb Translated using Weblate (French)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-05-04 20:29:35 +02:00
ihor_ck
59843e5464 Translated using Weblate (Ukrainian)
Currently translated at 15.0% (65 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/uk/
2020-05-04 02:22:13 +02:00
solokot
4fe0ccf807 Translated using Weblate (Russian)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-05-02 20:32:07 +02:00
Anonymous
2ee012d3fe Translated using Weblate (Hungarian)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/
2020-05-02 16:11:02 +02:00
Balázs Meskó
161b4cb5f5 Translated using Weblate (Hungarian)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/
2020-05-02 16:11:02 +02:00
Anonymous
f9bee68d71 Translated using Weblate (Hungarian)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/
2020-05-02 16:10:34 +02:00
Balázs Meskó
e0d98aca1b Translated using Weblate (Hungarian)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/
2020-05-02 16:08:07 +02:00
sshshsjsjaja
d47518c00c Translated using Weblate (German)
Currently translated at 100.0% (432 of 432 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-05-02 15:55:55 +02:00
J-Jamet
58f97dc3ab Upgrade to versionCode 33 2020-05-01 13:48:54 +02:00
J-Jamet
c7706cb80c Merge tag '2.5RC2' into develop
2.5RC2
2020-05-01 13:29:00 +02:00
J-Jamet
49bcc877ef Merge branch 'release/2.5RC2' 2020-05-01 13:28:50 +02:00
J-Jamet
4b81dd552e Update screens 2020-05-01 13:11:31 +02:00
J-Jamet
ff63aaf3f3 Remove unused file types 2020-04-30 21:14:15 +02:00
J-Jamet
39cbd1477b Upgrade CHANGELOG 2020-04-30 18:19:37 +02:00
J-Jamet
3741cc558a Remove field reference URL 2020-04-30 18:02:12 +02:00
J-Jamet
2a314ca3c1 Merge branch 'translations' into develop 2020-04-30 17:51:50 +02:00
J-Jamet
10f9564825 Remove unused comment 2020-04-30 17:51:23 +02:00
J-Jamet
1ae87c9b18 Fix small string issues 2020-04-30 17:50:10 +02:00
J-Jamet
24301ba462 Fix string strong tag 2020-04-30 17:43:19 +02:00
J-Jamet
ff6481274f Fix small string bug 2020-04-30 17:36:43 +02:00
J-Jamet
866df585a2 Merge branch 'master' of https://hosted.weblate.org/projects/keepass-dx/strings into translations 2020-04-30 17:24:48 +02:00
J-Jamet
416bec04a1 Fix context 2020-04-30 17:07:52 +02:00
J-Jamet
f747d6725e Add flavor dimension 2020-04-30 17:03:43 +02:00
J-Jamet
8846918e55 Fix file manager dialog 2020-04-30 16:30:52 +02:00
J-Jamet
e347aefcd9 Manual merge of strings #460 2020-04-30 16:04:41 +02:00
J-Jamet
0a0676af3a Android backup management 2020-04-30 15:45:30 +02:00
Mik
7a69b63b4f Translated using Weblate (Italian)
Currently translated at 97.0% (421 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-30 03:32:39 +02:00
J-Jamet
fab5626741 Fix error message in password generator 2020-04-29 17:46:33 +02:00
J-Jamet
c6832d6478 Check permission in onResume 2020-04-29 16:56:47 +02:00
J-Jamet
f451597746 Add thread sleep 2020-04-29 16:35:17 +02:00
J-Jamet
a92e5b5156 Remove dialog task listener (issue dialog still in view) 2020-04-29 15:16:58 +02:00
J-Jamet
786b3b26ea Merge branch 'develop' of github.com:Kunzisoft/KeePassDX into develop 2020-04-28 21:42:40 +02:00
J-Jamet
2e9fd2fd79 Merge branch 'feature/fix_opening' into develop 2020-04-28 21:42:18 +02:00
J-Jamet
3be2b9893b Move onActionFinish?.invoke(actionTask, result) 2020-04-26 20:00:47 +02:00
J-Jamet
7f6bed4f5f Replace EditText by TextInputEditText in InputLayout 2020-04-26 10:51:16 +02:00
J-Jamet
37322f284a Merge branch 'develop' into feature/fix_opening 2020-04-26 10:17:46 +02:00
J-Jamet
4106bb1792 Fix lock loop 2020-04-26 10:16:56 +02:00
J-Jamet
229db1242a Listener for task dialog 2020-04-26 09:10:18 +02:00
J-Jamet
8201b42135 Fix progress task dialog 2020-04-25 15:41:33 +02:00
J-Jamet
24818575dc Finish database task only if a binder is attach (to fix too fast opening) 2020-04-25 14:19:50 +02:00
J-Jamet
213050e7f2 Better task listener implementation 2020-04-24 22:09:46 +02:00
J-Jamet
1efaf4e3ea Kotlinized VariantDictionnary and fix database creation 2020-04-24 20:35:36 +02:00
J-Jamet
5532147992 Update CHANGELOG 2020-04-24 18:32:11 +02:00
J-Jamet
550b43094d Fix default username in existing entry #535 2020-04-24 18:30:17 +02:00
J-Jamet
5e831837c8 Update gradle kotlin 2020-04-24 18:20:07 +02:00
J-Jamet
480e88a088 Update gradle 2020-04-24 18:19:35 +02:00
J-Jamet
81cbcbe8af Upgrade TapTargetView lib to 1.13.0 2020-04-24 18:15:17 +02:00
J-Jamet
5649634809 Fix toolbar icon color after theme change 2020-04-24 17:55:44 +02:00
J-Jamet
69a253f738 Remove unused preference theme 2020-04-23 22:39:04 +02:00
J-Jamet
fbc8cfddb8 Fix purple style 2020-04-23 21:59:32 +02:00
J-Jamet
3e1024804c Try to fix preference icons 2020-04-23 21:37:24 +02:00
J-Jamet
cf0b51be00 Fix style purple 2020-04-23 17:53:44 +02:00
J-Jamet
5b295a2a8f Fix pref icon color for KitKat 2020-04-23 15:23:23 +02:00
Andrew
ebb3d7a149 Translated using Weblate (Russian)
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-04-23 15:11:22 +02:00
J-Jamet
73ab348d11 Fix keyfile name view 2020-04-23 14:59:21 +02:00
J-Jamet
8ce17086f8 Fix show lock button in setting orientation change 2020-04-23 14:51:32 +02:00
J-Jamet
45cffc93b1 Try to fix theme issue #509 2020-04-23 14:32:59 +02:00
J-Jamet
71cf4d5a34 Fix requireContext, requireArguments and import 2020-04-23 13:43:57 +02:00
J-Jamet
b79a4154af Fix deprecated FragmentManager 2020-04-23 13:35:42 +02:00
J-Jamet
ff818f8472 Upgrade Preference lib 2020-04-23 12:37:12 +02:00
Kunzisoft
b540d32ca3 Added translation using Weblate (Bulgarian) 2020-04-23 09:45:08 +02:00
J-Jamet
895a11d45f Fix opening database wait 2020-04-22 21:07:56 +02:00
J-Jamet
bdafae6132 Fix small warning 2020-04-22 20:50:59 +02:00
J-Jamet
f45d9ce6da Merge branch 'feature/UnsignedValues' into develop 2020-04-22 20:33:35 +02:00
J-Jamet
7456e2c8f5 Refactor key encryption methods and add unit tests 2020-04-22 20:23:23 +02:00
J-Jamet
fc51b50fe6 Fix download_progression string 2020-04-22 18:57:55 +02:00
J-Jamet
b31aa26975 Update CHANGELOG 2020-04-22 18:51:32 +02:00
J-Jamet
49801e1b14 Merge branch 'feature/KeeWeb_Database' into develop 2020-04-22 18:49:50 +02:00
J-Jamet
1453464cbd Remove unused code 2020-04-22 17:46:00 +02:00
J-Jamet
b9be8ff13d Fix KDBX header reader for KeeWeb database #533 2020-04-22 17:30:20 +02:00
J-Jamet
9663c3cadd Add UnsignedInt and UnsignedLong 2020-04-22 15:55:21 +02:00
J-Jamet
da5490bc0a Remove unused import 2020-04-22 15:37:40 +02:00
anonymous
df9b1b9fbb Translated using Weblate (Russian)
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-04-22 14:33:57 +02:00
zeritti
6a0a3ded13 Translated using Weblate (Czech)
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2020-04-21 19:11:21 +02:00
J-Jamet
ac7f4a08c3 Replace empty recycle bin icon 2020-04-20 19:12:16 +02:00
J-Jamet
e5ff5e3364 Update CHANGELOG 2020-04-20 19:09:24 +02:00
J-Jamet
f0ac19fcc1 Fix "Go" button #523 2020-04-20 19:07:09 +02:00
J-Jamet
ce23d34923 Fix #530 2020-04-20 18:41:57 +02:00
WaldiS
bdbba87f5a Translated using Weblate (Polish)
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2020-04-20 04:11:21 +02:00
Filippo De Bortoli
fa8ef0477b Translated using Weblate (Italian)
Currently translated at 89.1% (387 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-20 04:11:21 +02:00
Jeannette L
3e9ec4cfb6 Translated using Weblate (Italian)
Currently translated at 89.1% (387 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-20 04:11:20 +02:00
J-Jamet
ad1c9d8129 Merge branch 'feature/SearchParameters' into develop 2020-04-18 13:09:39 +02:00
J-Jamet
7aaac4c13c Encapsulate search and UUID hex string 2020-04-18 13:09:17 +02:00
J-Jamet
a6c71c3d54 StringUtil refactoring 2020-04-18 11:29:51 +02:00
J-Jamet
05714f38dc Add search in url for search info 2020-04-18 11:23:20 +02:00
J-Jamet
4ef5e4d8ae Refactor search parameters and Autofill search 2020-04-17 16:15:55 +02:00
Oğuz Ersen
5fd0e1d6fe Translated using Weblate (Turkish)
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-16 13:11:25 +02:00
Allan Nordhøy
97faf5bef3 Translated using Weblate (Norwegian Bokmål)
Currently translated at 79.0% (343 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2020-04-16 13:11:25 +02:00
Destiny Li
bd77128733 Translated using Weblate (English)
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2020-04-16 13:11:24 +02:00
Destiny Li
935debc6bf Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-04-16 13:11:24 +02:00
solokot
699ed19b6a Translated using Weblate (Russian)
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-04-16 13:11:24 +02:00
Jeannette L
126e5a94c2 Translated using Weblate (French)
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-04-16 13:11:23 +02:00
sshshsjsjaja
792bd36ea7 Translated using Weblate (German)
Currently translated at 100.0% (434 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-04-16 13:11:23 +02:00
jan madsen
04dfee1b43 Translated using Weblate (Danish)
Currently translated at 99.0% (430 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2020-04-16 13:11:23 +02:00
Carles Sadurní
6a08cfeea9 Translated using Weblate (Catalan)
Currently translated at 61.0% (265 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ca/
2020-04-16 13:11:22 +02:00
J-Jamet
29a0f6c9f6 Small amelioration 2020-04-15 20:06:14 +02:00
J-Jamet
e2bdf26d82 Fix autofill crash #520 2020-04-15 14:44:13 +02:00
Kunzisoft
1643949110 Translated using Weblate (French)
Currently translated at 99.7% (433 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-04-15 12:50:29 +02:00
J-Jamet
577dabd727 Better HashedBlockOutputStream implementation 2020-04-15 12:11:45 +02:00
J-Jamet
ed4bd0693f Fix small warning 2020-04-15 12:02:51 +02:00
J-Jamet
0f2982b34d Try to fix bad icon exception 2020-04-15 11:52:22 +02:00
C. Rüdinger
0954094800 Translated using Weblate (German)
Currently translated at 98.6% (428 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-04-14 23:35:52 +02:00
sshshsjsjaja
22dcfafc62 Translated using Weblate (German)
Currently translated at 98.6% (428 of 434 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-04-14 23:35:51 +02:00
J-Jamet
dd34051426 Update CHANGELOG 2020-04-14 20:28:25 +02:00
J-Jamet
6feaf2cb8a Update Autofill compatibility 2020-04-14 20:27:08 +02:00
J-Jamet
603f64ea92 Check TODOs 2020-04-14 20:23:50 +02:00
J-Jamet
568f9ab0d0 Check TODOs 2020-04-14 19:54:02 +02:00
J-Jamet
a2ff1c33e6 Update CHANGELOG 2020-04-14 19:33:29 +02:00
J-Jamet
84452e9fc0 Spongy Castle by Bouncy Castle replacement 2020-04-13 13:39:45 +02:00
J-Jamet
5a6ae453cf Upgrade strings comments 2020-04-13 10:03:49 +02:00
Hosted Weblate
cde3e3361d Merge branch 'origin/master' into Weblate. 2020-04-13 08:58:48 +02:00
Oğuz Ersen
25f01192a4 Translated using Weblate (Turkish)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-13 08:58:47 +02:00
Carles Sadurní
bacb08ec65 Translated using Weblate (Catalan)
Currently translated at 16.7% (72 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ca/
2020-04-13 08:58:47 +02:00
anonymous
36c905ee1c Translated using Weblate (Catalan)
Currently translated at 18.1% (78 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ca/
2020-04-12 22:44:08 +02:00
Carles Sadurní
6b9280534a Translated using Weblate (Catalan)
Currently translated at 18.1% (78 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ca/
2020-04-12 22:44:08 +02:00
J-Jamet
f94c6e850f Add History of each entry in description 2020-04-12 21:20:54 +02:00
J-Jamet
f4cecf6906 Upgrade to version 2.5 2020-04-12 20:51:02 +02:00
J-Jamet
a81d3766f8 Merge tag '2.5RC1' into develop
2.5RC1
2020-04-12 20:32:12 +02:00
J-Jamet
7182c2e66d Merge branch 'release/2.5RC1' 2020-04-12 20:32:03 +02:00
J-Jamet
503316bc70 Fix navigation bar color for light theme 2020-04-12 20:08:39 +02:00
J-Jamet
d5af59f2c7 Merge branch 'translations' into release/2.5RC1 2020-04-12 20:03:44 +02:00
J-Jamet
cb3ac1ad3a version 2.5RC1 2020-04-12 19:36:51 +02:00
J-Jamet
e161080e4c Fix special button 2020-04-11 17:36:24 +02:00
J-Jamet
ceab7f917b Upgrade CHANGELOG 2020-04-11 16:21:27 +02:00
J-Jamet
41d8066c4c Fix autofill bad recognition for key-value format #513 2020-04-11 16:16:50 +02:00
J-Jamet
e373cbd776 File manager explanation link 2020-04-11 12:23:46 +02:00
J-Jamet
05ea6b6b10 Change background color and fix bottom button 2020-04-10 20:37:56 +02:00
J-Jamet
28eed3ae71 New file selection layout 2020-04-10 20:15:08 +02:00
Mert Sezer
535c67ac9b Translated using Weblate (Turkish)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-10 16:09:45 +02:00
J-Jamet
68027a6e15 Linkfy notes #426 2020-04-10 13:22:26 +02:00
J-Jamet
12dea6b499 Add new autofill hint for username 2020-04-10 12:32:38 +02:00
J-Jamet
d8bd078a02 Fix custom fields duplication #257 2020-04-10 11:32:23 +02:00
J-Jamet
439bc109b0 Update CHANGELOG 2020-04-09 21:42:16 +02:00
J-Jamet
b1ec93ceb5 Add navigation bar color #510 2020-04-09 21:38:32 +02:00
J-Jamet
44b9aa0e48 Style black as separate file 2020-04-09 21:31:48 +02:00
J-Jamet
0a59063027 Add scrollbar in nodes list 2020-04-09 20:33:12 +02:00
Oğuz Ersen
5db4608abd Translated using Weblate (Turkish)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-09 06:45:37 +02:00
J-Jamet
6ece2aa6cb Add autofill web domain and app id 2020-04-08 20:50:28 +02:00
J-Jamet
95ee45f666 Merge branch 'feature/Autofill_Domain' into develop 2020-04-08 17:55:42 +02:00
J-Jamet
556e90b8d8 Fix autofill response 2020-04-08 17:17:29 +02:00
J-Jamet
42841e6247 Setting for autofill automatic search 2020-04-08 16:55:30 +02:00
J-Jamet
324c82248a Refactor magikeyboard settings and add autofill settings 2020-04-08 15:44:14 +02:00
J-Jamet
94f5a47918 Catch exception during autofill parse 2020-04-08 14:11:18 +02:00
J-Jamet
3f70990956 Better autofill workflow and add autofill icon for each entry 2020-04-08 13:02:15 +02:00
J-Jamet
d77635e572 Fix autofill auto search 2020-04-07 21:01:26 +02:00
solokot
0f26f1b751 Translated using Weblate (Russian)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-04-07 01:36:50 +02:00
J-Jamet
456bc22138 Encapsulate search methods and search without UI 2020-04-06 21:43:55 +02:00
J-Jamet
b6fe91e396 Fix small warning 2020-04-06 21:36:50 +02:00
J-Jamet
0fb4d26949 Add auto search info 2020-04-06 17:36:51 +02:00
J-Jamet
3b3583a416 Retrieve application id and domain 2020-04-06 11:41:16 +02:00
J-Jamet
cac1d576c8 Upgrade Autofill algorithm 2020-04-06 10:33:44 +02:00
J-Jamet
9cba0d0a48 Message to indicate file revoked 2020-04-06 10:12:09 +02:00
J-Jamet
3bf11e9dc0 Remove unused import 2020-04-06 09:41:02 +02:00
Fabio Iacovino
edbb160ac6 Translated using Weblate (Italian)
Currently translated at 90.4% (388 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-06 00:22:25 +02:00
WaldiS
ee111dc63c Translated using Weblate (Polish)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2020-04-05 17:58:26 +02:00
Filippo De Bortoli
b06edb756a Translated using Weblate (Italian)
Currently translated at 89.7% (385 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-05 17:13:40 +02:00
anonymous
fd745494e0 Translated using Weblate (Italian)
Currently translated at 89.7% (385 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-05 17:13:40 +02:00
Fabio Iacovino
702bf3f479 Translated using Weblate (Italian)
Currently translated at 89.7% (385 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-05 17:13:40 +02:00
Fabio Iacovino
6adc02a91f Translated using Weblate (Italian)
Currently translated at 85.3% (366 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-05 17:07:29 +02:00
anonymous
55bae5a130 Translated using Weblate (Italian)
Currently translated at 85.3% (366 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-05 17:07:29 +02:00
Filippo De Bortoli
1aae817b17 Translated using Weblate (Italian)
Currently translated at 85.3% (366 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-04-05 17:07:29 +02:00
Oğuz Ersen
8afc8c23fb Translated using Weblate (Turkish)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-05 03:01:39 +02:00
Allan Nordhøy
36e473b139 Translated using Weblate (Norwegian Bokmål)
Currently translated at 76.6% (329 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2020-04-05 03:01:38 +02:00
Allan Nordhøy
e529723f86 Translated using Weblate (English)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2020-04-05 03:01:38 +02:00
Dwhite Reeves
6c5112c142 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-04-05 03:01:38 +02:00
Allan Nordhøy
77ac68a603 Translated using Weblate (Dutch)
Currently translated at 77.6% (333 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nl/
2020-04-05 03:01:37 +02:00
Éfrit
e816f40872 Translated using Weblate (French)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-04-05 03:01:37 +02:00
zeritti
65313f114b Translated using Weblate (Czech)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2020-04-05 03:01:37 +02:00
J-Jamet
ef1f1342f5 Upgrade CHANGELOG 2020-04-04 18:49:22 +02:00
J-Jamet
9205fe6c08 Merge branch 'feature/Lock_Button' into develop 2020-04-04 18:43:03 +02:00
J-Jamet
75df3e81fe Fix style 2020-04-04 18:42:35 +02:00
J-Jamet
47fffbadb5 Setting to show the lock button 2020-04-04 18:25:48 +02:00
J-Jamet
78354473fa Fix lock button color and education screen 2020-04-04 17:53:04 +02:00
J-Jamet
8d7efb44b5 Change lock button color 2020-04-03 22:21:50 +02:00
J-Jamet
cab00b3d8c Change lock button color 2020-04-03 22:18:33 +02:00
Mert Sezer
8efab01336 Translated using Weblate (Turkish)
Currently translated at 99.5% (427 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-03 21:41:26 +02:00
Oğuz Ersen
66feb8beb4 Translated using Weblate (Turkish)
Currently translated at 99.5% (427 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-03 21:41:26 +02:00
Mert Sezer
ca4cccffeb Translated using Weblate (Turkish)
Currently translated at 99.3% (426 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-03 21:40:34 +02:00
Oğuz Ersen
51b1760c50 Translated using Weblate (Turkish)
Currently translated at 99.3% (426 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-03 21:40:33 +02:00
J-Jamet
42e1bda365 Change lock button color 2020-04-03 21:32:32 +02:00
J-Jamet
194b6b557a Add lock button elevation and border 2020-04-03 21:17:38 +02:00
Dwhite Reeves
e7bc439997 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-04-03 15:44:41 +02:00
Allan Nordhøy
ef76cce0ac Translated using Weblate (Romanian)
Currently translated at 95.3% (409 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ro/
2020-04-03 13:26:34 +02:00
Allan Nordhøy
63eec6d969 Translated using Weblate (Turkish)
Currently translated at 96.9% (416 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-03 13:26:34 +02:00
WaldiS
719ae74c06 Translated using Weblate (Polish)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2020-04-03 13:26:33 +02:00
Mert Sezer
37cf424eb8 Translated using Weblate (Turkish)
Currently translated at 96.9% (416 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-01 17:46:51 +02:00
anonymous
3a87f7ba9d Translated using Weblate (Turkish)
Currently translated at 96.9% (416 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-04-01 17:46:50 +02:00
J-Jamet
275428d825 First commit for new lock button 2020-04-01 14:57:31 +02:00
Pavel Borecki
7b9dac86ca Translated using Weblate (Czech)
Currently translated at 96.7% (415 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2020-04-01 03:28:42 +02:00
J-Jamet
46496ee2cc Merge branch 'damoasda-translation-update' into develop 2020-03-31 21:58:21 +02:00
J-Jamet
110cc402cc Add keyfile selection view 2020-03-31 21:48:51 +02:00
J-Jamet
587f006259 Long click to use GET_CONTENT 2020-03-31 19:46:43 +02:00
Moasda
ec45c0df81 Translated using Weblate (German)
Currently translated at 99.7% (428 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-30 22:24:51 +02:00
jan madsen
dc575aeca4 Translated using Weblate (Danish)
Currently translated at 98.3% (422 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2020-03-30 22:24:51 +02:00
Herbert Reiter
a3f790f000 Fix inconsistencies with translation arguments (e.g. %1$s), escape % character 2020-03-30 22:08:50 +02:00
Herbert Reiter
4e9e188d02 Add and fix German translations 2020-03-30 22:03:09 +02:00
J-Jamet
abb17efae4 Merge branch 'feature/File_Manager_Access' into develop 2020-03-30 18:17:24 +02:00
Hosted Weblate
16be990502 Merge branch 'origin/master' into Weblate. 2020-03-30 08:48:35 +02:00
anonymous
d1a496f9a3 Translated using Weblate (German)
Currently translated at 99.7% (428 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-30 03:17:18 +02:00
Allan Nordhøy
cd730fcfef Translated using Weblate (Norwegian Bokmål)
Currently translated at 76.9% (330 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2020-03-29 23:06:21 +02:00
Moasda
1b65bf665b Translated using Weblate (German)
Currently translated at 99.7% (428 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-29 23:06:21 +02:00
J-Jamet
6f4735790c Fix autofill for Android app #502 2020-03-29 22:00:24 +02:00
J-Jamet
040666f89d Merge branch 'develop' of github.com:Kunzisoft/KeePassDX into develop 2020-03-29 20:19:42 +02:00
J-Jamet
227cb800c3 Merge branch 'damoasda-readme-fix' 2020-03-29 16:15:44 +02:00
Herbert Reiter
0052569a14 Fix FAQ link to wiki page 2020-03-29 15:41:49 +02:00
J-Jamet
f7561c4888 Upgrade to version 2.5beta31 2020-03-29 15:22:34 +02:00
Destiny Li
92200f19e7 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-03-29 14:09:36 +02:00
Éfrit
4c01f18a62 Translated using Weblate (French)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-03-29 14:09:36 +02:00
anonymous
a6ce3e49fe Translated using Weblate (German)
Currently translated at 99.5% (427 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-29 14:09:36 +02:00
Oliver
1b3a5d1bf6 Translated using Weblate (German)
Currently translated at 99.5% (427 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-29 14:09:35 +02:00
J-Jamet
db7bdc63c8 Fix #501 2020-03-28 18:13:09 +01:00
J-Jamet
7aca550f02 Merge branch 'develop' into feature/File_Manager_Access 2020-03-28 17:47:14 +01:00
J-Jamet
b60980b3fd Upgrade to 2.5beta31 2020-03-28 17:46:50 +01:00
J-Jamet
b578c2c584 Add write permission 2020-03-28 17:30:57 +01:00
solokot
fc45bd624e Translated using Weblate (Russian)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-03-27 20:06:43 +01:00
anonymous
17be3d9d2c Translated using Weblate (Russian)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-03-27 20:03:48 +01:00
solokot
5b3a38a7bc Translated using Weblate (Russian)
Currently translated at 100.0% (429 of 429 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-03-27 20:03:47 +01:00
Hosted Weblate
4403835d50 Merge branch 'origin/master' into Weblate. 2020-03-27 18:36:55 +01:00
C. Rüdinger
b7a3d3eb46 Translated using Weblate (German)
Currently translated at 99.5% (424 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-27 18:36:54 +01:00
J-Jamet
29846b22fe Merge tag '2.5beta30' into develop
2.5beta30
2020-03-27 18:13:28 +01:00
J-Jamet
32d235e8c7 Merge branch 'release/2.5beta30' 2020-03-27 18:13:21 +01:00
J-Jamet
cb982b3513 Fix strong tag 2020-03-27 17:53:42 +01:00
J-Jamet
d7ed6c26dd Merge branch 'develop' into translations 2020-03-27 17:49:29 +01:00
J-Jamet
8ff19f7e68 First string pass according to #460 2020-03-27 17:43:31 +01:00
J-Jamet
729e062c3a Add error when create database file 2020-03-27 17:12:54 +01:00
J-Jamet
7d0340ac07 Upgrade CHANGELOG 2020-03-27 16:57:35 +01:00
J-Jamet
01960e74c1 Fix check file ANR #494 2020-03-27 16:36:57 +01:00
anonymous
8e40250985 Translated using Weblate (German)
Currently translated at 96.4% (411 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-27 00:46:37 +01:00
C. Rüdinger
8ae2edb61a Translated using Weblate (German)
Currently translated at 96.4% (411 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-27 00:46:37 +01:00
J-Jamet
0baa7bcbf1 Change discard button 2020-03-26 16:36:17 +01:00
J-Jamet
fffee48918 Upgrade version to 2.5beta30 2020-03-26 14:50:20 +01:00
J-Jamet
515abb6e14 Better UUID view 2020-03-26 14:45:51 +01:00
J-Jamet
6c1c3ff87f New method to wait 1.5 seconds after screen turns off 2020-03-26 13:38:20 +01:00
J-Jamet
5b65575c7a Add comment to ignore autocomplete off 2020-03-26 12:57:24 +01:00
J-Jamet
0f258fc5f8 Upgrade autofill algorithm 2020-03-26 12:47:32 +01:00
anonymous
206bc661dc Translated using Weblate (German)
Currently translated at 96.2% (410 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-25 23:44:32 +01:00
J-Jamet
d0e35b109e Revert : Fix Nextcloud File Upload conflict #497 2020-03-25 22:48:15 +01:00
J-Jamet
61769c4f20 Fix Nextcloud File Upload conflict #497 2020-03-25 21:07:36 +01:00
Hosted Weblate
95778ee5f4 Merge branch 'origin/master' into Weblate. 2020-03-25 12:21:35 +01:00
anonymous
155030fdca Translated using Weblate (Italian)
Currently translated at 79.5% (339 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-03-25 12:21:35 +01:00
Filippo De Bortoli
98237ef76c Translated using Weblate (Italian)
Currently translated at 79.5% (339 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-03-25 12:21:34 +01:00
J-Jamet
f0a7b38199 Merge tag '2.5beta29' into develop
2.5beta29
2020-03-25 12:06:20 +01:00
J-Jamet
9fc5e6751b Merge branch 'release/2.5beta29' 2020-03-25 12:06:08 +01:00
J-Jamet
4c1630312b Add UUID color 2020-03-25 11:46:43 +01:00
J-Jamet
d397c5c996 Add UUID ref for entry 2020-03-25 11:22:19 +01:00
J-Jamet
f6c41b5a60 Replace the strong nodes 2020-03-24 20:49:33 +01:00
J-Jamet
06eb5c01c3 Update versions 2020-03-24 20:44:50 +01:00
J-Jamet
7df49f91e8 Merge branch 'release/2.5beta29' of github.com:Kunzisoft/KeePassDX into release/2.5beta29 2020-03-24 20:10:27 +01:00
J-Jamet
96dcbb0ce7 Merge branch 'develop' into release/2.5beta29 2020-03-24 20:09:47 +01:00
J-Jamet
5f828fb986 Fix setting 2020-03-24 20:09:30 +01:00
J-Jamet
533d663938 Merge branch 'translations' into develop 2020-03-24 19:44:38 +01:00
J-Jamet
ae788503a9 Fix html_about licence 2020-03-24 19:44:15 +01:00
anonymous
cf0acd9c73 Translated using Weblate (Italian)
Currently translated at 78.4% (334 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-03-24 11:42:45 +01:00
Filippo De Bortoli
0857f2f1cf Translated using Weblate (Italian)
Currently translated at 78.4% (334 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-03-24 11:42:43 +01:00
Ema Panz
c05d412bdb Translated using Weblate (Italian)
Currently translated at 71.1% (303 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-03-24 11:18:14 +01:00
anonymous
c8e0ce717d Translated using Weblate (Italian)
Currently translated at 71.1% (303 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-03-24 11:18:14 +01:00
J-Jamet
cc3485b201 Update Readme 2020-03-23 21:10:28 +01:00
J-Jamet
81ea7080c2 Show UUID as KeePass UUID 2020-03-21 20:25:37 +01:00
J-Jamet
76ff6f5ae0 Fix reference loop 2020-03-21 19:01:05 +01:00
J-Jamet
0c0d0b7a6f Encapsulate readonly for launch method 2020-03-21 17:23:17 +01:00
J-Jamet
ec8cf1f6b7 Move autofill setting up 2020-03-21 17:16:49 +01:00
J-Jamet
da44310d1b Fix focus validation button 2020-03-21 17:03:36 +01:00
J-Jamet
4bd9c84bb0 Add dialog to validate discard entry changes 2020-03-21 16:52:50 +01:00
J-Jamet
3b1269a770 Fix bind listeners 2020-03-21 16:12:07 +01:00
J-Jamet
7c986ccee8 Fix card_view_margin issue 2020-03-21 16:08:23 +01:00
J-Jamet
903bad8f36 Fix launch of open db service exception 2020-03-21 14:36:33 +01:00
J-Jamet
4b9577437c Hide add button when nodes are selected 2020-03-21 12:27:18 +01:00
J-Jamet
c316011fbc Show toast on invalid key exception 2020-03-21 12:16:45 +01:00
J-Jamet
3fb1f18c22 Education screen for OTP and entry edit menu 2020-03-19 19:47:51 +01:00
J-Jamet
53935058f5 Add OTP icon 2020-03-19 19:16:38 +01:00
J-Jamet
a3860c9581 Generate password icon as dice 2020-03-19 18:34:14 +01:00
J-Jamet
dc20899d26 Update CHANGELOG 2020-03-19 17:48:56 +01:00
J-Jamet
62ac3ddb75 Merge branch 'feature/Autofill_Improvement' into develop 2020-03-19 17:23:24 +01:00
J-Jamet
b792a61bf9 Improve hint "on" and "off" recognition 2020-03-19 15:18:06 +01:00
J-Jamet
aae9f9e1cb Warning when cancel autofill 2020-03-19 14:59:33 +01:00
J-Jamet
d098bf5e6a Upgrade Autofill algorithm 2020-03-19 14:35:22 +01:00
J-Jamet
b0e8a3ecd9 Add expiration datetime 2020-03-18 12:03:12 +01:00
J-Jamet
4efa684022 Merge branch 'feature/Edit_Expired_Date' into develop 2020-03-18 11:53:46 +01:00
J-Jamet
f2c8082990 Fix date picker kitkat issue 2020-03-18 11:53:21 +01:00
J-Jamet
1abba80045 Fix dialog theme 2020-03-18 11:45:52 +01:00
J-Jamet
68564a2b75 Merge branch 'develop' into feature/Edit_Expired_Date 2020-03-18 09:46:28 +01:00
J-Jamet
385b701b38 Fix cardview margin 2020-03-18 09:42:36 +01:00
Éfrit
11c9a1d707 Translated using Weblate (French)
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-03-17 00:42:55 +01:00
J-Jamet
c5aef6b561 New entry edit tool menu style 2020-03-15 12:21:26 +01:00
J-Jamet
dfcf73cfd0 Merge branch 'develop' into feature/Edit_Expired_Date 2020-03-14 21:15:58 +01:00
J-Jamet
7fc9389700 Always show menu 2020-03-14 21:15:37 +01:00
J-Jamet
d1af7349bc Edit button as validation 2020-03-14 20:51:47 +01:00
J-Jamet
0fd955197d Expires selection 2020-03-14 20:16:36 +01:00
Destiny Li
cbf33507d1 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-03-14 17:37:29 +01:00
WaldiS
bc60a5d97e Translated using Weblate (Polish)
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2020-03-14 17:37:27 +01:00
zeritti
57596b2991 Translated using Weblate (Czech)
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2020-03-14 17:37:23 +01:00
solokot
43b3602a52 Translated using Weblate (Russian)
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-03-14 17:37:22 +01:00
J-Jamet
c09ec961b8 Merge branch 'feature/Entry_Edit_BottomBar' into develop 2020-03-14 11:00:47 +01:00
J-Jamet
d140b453b2 Merge branch 'feature/Entry_Edit_BottomBar' into develop 2020-03-14 10:36:52 +01:00
J-Jamet
9eb42636ec Change read icons 2020-03-13 21:58:32 +01:00
J-Jamet
ee2d663fce New icons for entry edit tools 2020-03-13 21:33:38 +01:00
J-Jamet
8e83615a22 Add Edit Toolbar menu on top 2020-03-13 17:06:09 +01:00
J-Jamet
0ff129c5ca Edit Entry with Card View 2020-03-13 11:06:56 +01:00
WaldiS
e49858439f Translated using Weblate (Polish)
Currently translated at 99.5% (424 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2020-03-12 20:33:30 +01:00
zeritti
3df07f7f47 Translated using Weblate (Czech)
Currently translated at 99.5% (424 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2020-03-12 20:33:30 +01:00
Aurel F
ff9e179593 Translated using Weblate (Romanian)
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ro/
2020-03-12 20:33:28 +01:00
J-Jamet
7539fee04b Add BottomBar in EntryEdit 2020-03-12 19:15:03 +01:00
J-Jamet
71a339a58f Suppress small warning 2020-03-12 17:15:17 +01:00
J-Jamet
9ef2ea016b Suppress deprecation for Keyboard 2020-03-12 17:14:46 +01:00
J-Jamet
de4936a16d Set nullable getSystemService 2020-03-12 17:05:53 +01:00
J-Jamet
574d2b8904 Wait 3 seconds before the lock after the screen turns off #59 2020-03-12 16:56:46 +01:00
J-Jamet
1f3f7634e7 Allow empty title in entries #423 2020-03-12 14:41:02 +01:00
J-Jamet
3c0725baff Fix remember key file option 2020-03-12 14:24:33 +01:00
J-Jamet
b0e1411012 Update CHANGELOG 2020-03-12 14:20:48 +01:00
J-Jamet
39daf4714d Merge branch 'feature/Delete_Registered_Keyfile_New_Credentials' into develop 2020-03-12 14:17:35 +01:00
J-Jamet
4706afa823 Fix credentials options 2020-03-12 14:16:30 +01:00
J-Jamet
25977d389d Add DAO command to delete registered keyfile 2020-03-12 13:18:36 +01:00
J-Jamet
f760110569 default_database_path_key in strings.xml 2020-03-12 09:28:46 +01:00
J-Jamet
133e78fe97 Update CHANGELOG 2020-03-12 09:20:29 +01:00
J-Jamet
d92e0c8620 Fix magikeyboard lock 2020-03-11 19:17:51 +01:00
J-Jamet
62fdb69d6b Fix small element 2020-03-11 18:56:45 +01:00
J-Jamet
def57c9fb2 Merge branch 'feature/Lock_Database' issue 483 2020-03-11 18:42:34 +01:00
J-Jamet
21c9c898c3 Add lock timer in service, notification remains lock to capture the broadcast 2020-03-11 18:30:11 +01:00
J-Jamet
1f03c922c2 Encapsulate lock broadcast 2020-03-11 16:46:44 +01:00
J-Jamet
3f6ae6bdac Fix node update #487 2020-03-11 12:42:18 +01:00
jan madsen
60615ee1eb Translated using Weblate (Danish)
Currently translated at 98.5% (420 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2020-03-10 20:23:08 +01:00
J-Jamet
92b0d1bfa9 Upgrade to version 2.5beta29 2020-03-10 19:02:37 +01:00
J-Jamet
237988dc1f Merge tag '2.5beta28' into develop
2.5beta28
2020-03-10 19:00:08 +01:00
J-Jamet
a846ec29ca Merge branch 'release/2.5beta28' 2020-03-10 18:59:59 +01:00
J-Jamet
4533e96bff Upgrade CHANGELOG 2020-03-10 18:48:57 +01:00
J-Jamet
0a401c3ac9 Fix strings 2020-03-10 18:41:35 +01:00
J-Jamet
468abaf077 Fix strings 2020-03-10 18:29:19 +01:00
J-Jamet
4ccf2f641c Merge branch 'master' of https://hosted.weblate.org/projects/keepass-dx/strings into develop 2020-03-10 18:14:14 +01:00
Dominik Baláž
34eb2785cf Translated using Weblate (Slovak)
Currently translated at 21.5% (92 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sk/
2020-03-10 17:21:26 +01:00
anonymous
09dbfe323e Translated using Weblate (Slovak)
Currently translated at 21.5% (92 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sk/
2020-03-10 17:21:26 +01:00
Destiny Li
1f06c5b425 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-03-10 11:33:21 +01:00
Destiny Li
b98e089f7a Translated using Weblate (Chinese (Traditional))
Currently translated at 48.3% (206 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hant/
2020-03-10 11:33:20 +01:00
Aurel F
a0ad06ed0a Translated using Weblate (Romanian)
Currently translated at 40.8% (174 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ro/
2020-03-10 11:33:18 +01:00
solokot
ec63365429 Translated using Weblate (Russian)
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-03-10 11:33:10 +01:00
J-Jamet
2cb85e4346 Update items after appearance change #452 2020-03-10 08:58:22 +01:00
J-Jamet
0d7c479c51 Merge branch 'feature/Migration_SDK_29' into develop 2020-03-10 08:40:53 +01:00
J-Jamet
5a6c21e662 Try to fix read only bug #480 2020-03-10 08:38:49 +01:00
J-Jamet
d6cadac98f Fix small warning 2020-03-08 13:22:11 +01:00
J-Jamet
dac2fc2c37 Use PreferenceManager from AndroidX 2020-03-08 13:10:16 +01:00
J-Jamet
0fb45cef0d Encapsulate preferences 2020-03-08 13:04:25 +01:00
anonymous
5ebdbd4003 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-03-08 12:08:21 +01:00
Destiny Li
b30f1023cb Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-03-08 12:08:21 +01:00
J-Jamet
e5f65a4d1e Fix % issue in string 2020-03-08 11:48:47 +01:00
J-Jamet
ab42a65aa4 Fix null issues 2020-03-08 11:41:13 +01:00
J-Jamet
e351456bfe First pass migration SDK 29 2020-03-08 11:03:21 +01:00
J-Jamet
452e68b08f Fix C cast warning 2020-03-08 10:52:29 +01:00
J-Jamet
d65beed7a1 Fix small warnings 2020-03-08 10:49:06 +01:00
J-Jamet
f5a5a0e8cb Add generated JSON for database 2020-03-08 10:41:52 +01:00
solokot
98380a0906 Translated using Weblate (Russian)
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-03-08 10:20:37 +01:00
J-Jamet
22fe7508f3 Fix doForEachChild warning 2020-03-08 10:16:31 +01:00
J-Jamet
c8e241fc76 Fix Room incremental warning 2020-03-08 09:50:32 +01:00
J-Jamet
2c943e00d0 Fix writeEnum warning 2020-03-08 09:50:07 +01:00
J-Jamet
7ddb83b72d Fix DateFormatter warning 2020-03-08 09:36:45 +01:00
Kunzisoft
50bac01699 Translated using Weblate (French)
Currently translated at 99.7% (425 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-03-08 09:33:23 +01:00
Aurel F
e0a92dfadd Translated using Weblate (Romanian)
Currently translated at 12.2% (52 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ro/
2020-03-08 09:33:22 +01:00
solokot
b50c951091 Translated using Weblate (Russian)
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-03-08 09:33:17 +01:00
Kunzisoft
2f589a95a9 Translated using Weblate (English)
Currently translated at 100.0% (426 of 426 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2020-03-08 09:33:15 +01:00
J-Jamet
53eac86a95 Update AndroidX dependencies and fix small warning 2020-03-08 09:17:18 +01:00
J-Jamet
9412f8955e Update kotlin to 1.3.61 2020-03-08 09:07:14 +01:00
J-Jamet
71c98d82b1 Update gradle to 5.6.4 2020-03-08 09:00:56 +01:00
J-Jamet
42c1a925b4 Upgrade app to 2.54beta28 2020-03-08 08:59:41 +01:00
J-Jamet
1e84534ffd Merge tag '2.5beta27' into develop
2.5beta27
2020-03-07 14:41:37 +01:00
J-Jamet
b087733e37 Merge branch 'release/2.5beta27' 2020-03-07 14:41:29 +01:00
J-Jamet
675efe29ac Fix unused string 2020-03-07 14:31:08 +01:00
J-Jamet
9833af8225 Update french translations 2020-03-07 14:13:10 +01:00
J-Jamet
790c624571 Fix strong html 2020-03-07 14:06:50 +01:00
J-Jamet
67a70a8453 Merge branch 'translations' into develop 2020-03-07 13:57:47 +01:00
J-Jamet
64bb05e2dd Update CHANGELOG 2020-03-07 13:29:09 +01:00
J-Jamet
c111db6e73 Rename preference keys 2020-03-07 13:25:53 +01:00
J-Jamet
24c5915bd3 Setting to request a search when opening a database #220 2020-03-07 13:18:45 +01:00
J-Jamet
04c3717618 Remove screenshot from bug report 2020-03-07 12:41:57 +01:00
J-Jamet
78275a0984 Fix search empty list 2020-03-07 12:37:46 +01:00
J-Jamet
bd908ed10d Update CHANGELOG 2020-03-07 12:01:03 +01:00
J-Jamet
5473deec95 Show URL before username when title is empty #463 2020-03-07 11:58:47 +01:00
Ettore Atalan
653258afd2 Translated using Weblate (German)
Currently translated at 99.7% (418 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-03-07 03:33:06 +01:00
J-Jamet
00a2210eea Fix button menu color 2020-03-06 17:36:02 +01:00
J-Jamet
ec5688a013 Remove autofill setting for incompatible devices 2020-03-06 17:29:48 +01:00
J-Jamet
fc0c7b5708 Update CHANGELOG 2020-03-06 15:54:02 +01:00
J-Jamet
464f7ac486 Sorted list and comparator improvement 2020-03-06 15:46:08 +01:00
solokot
fb2457146c Translated using Weblate (Russian)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-02-29 19:33:06 +01:00
solokot
d90870838e Translated using Weblate (Russian)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-02-28 19:15:38 +01:00
Andrew
8c45f23291 Translated using Weblate (Russian)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-02-28 19:15:37 +01:00
Kunzisoft
315a3efa52 Added translation using Weblate (Romanian) 2020-02-27 14:01:57 +01:00
J-Jamet
28abb5ae6f Delete password from starting event #461 2020-02-27 13:26:41 +01:00
J-Jamet
bb78d89682 Try to fix aliased text #414 2020-02-27 13:17:01 +01:00
J-Jamet
fd36e19168 Update CHANGELOG 2020-02-27 12:57:31 +01:00
J-Jamet
58db516e44 Fix refresh appearance settings #452 #451 2020-02-27 12:54:47 +01:00
J-Jamet
2b875e94dc Add GZip string to attachment view if database is compressed 2020-02-27 12:38:16 +01:00
J-Jamet
0ba2447f55 Change about page 2020-02-27 10:16:43 +01:00
solokot
8e684d0d3a Translated using Weblate (Russian)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-02-25 23:32:46 +01:00
MrEnderfall
ce3c1d4685 Translated using Weblate (Russian)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-02-25 23:32:46 +01:00
J-Jamet
2cd77d47eb Move up remember keyfile locations setting 2020-02-23 13:43:09 +01:00
J-Jamet
ac9366e351 Update CHANGELOGS 2020-02-23 13:39:57 +01:00
J-Jamet
af356586f8 Merge branch 'feature/Check_Broken_Link' into develop 2020-02-23 13:33:56 +01:00
J-Jamet
7650db81a4 Hide broken database links 2020-02-23 13:15:09 +01:00
J-Jamet
8925c86afd Fix remember key file after press back 2020-02-23 12:49:40 +01:00
J-Jamet
8f1836009e Fix database and keyfile locations settings 2020-02-23 12:39:39 +01:00
J-Jamet
5fcf3f9b95 Fix erasing file 2020-02-23 10:47:28 +01:00
Kunzisoft
1c466d9e40 Translated using Weblate (French)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-02-22 17:33:01 +01:00
WaldiS
b97c2d9cbc Translated using Weblate (Polish)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2020-02-22 17:33:01 +01:00
zeritti
3d970e4967 Translated using Weblate (Czech)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2020-02-22 17:33:01 +01:00
J-Jamet
765c8f53dd Remove permission variable and rename preference 2020-02-20 19:53:49 +01:00
J-Jamet
4e81caeadf Remove unused external storage permission for Sdk > 18 2020-02-20 19:41:48 +01:00
J-Jamet
08b10e4d58 Update string 2020-02-20 19:35:52 +01:00
J-Jamet
f8594f72e8 Fix small elements 2020-02-20 19:31:44 +01:00
J-Jamet
24ebee07cd Fix small elements 2020-02-20 19:26:03 +01:00
J-Jamet
a06ebb0991 Force read only when file is not writable 2020-02-20 19:20:00 +01:00
J-Jamet
cd5d4498e7 Fix small element 2020-02-20 16:11:50 +01:00
J-Jamet
0bd62780c6 Check if uri is broken 2020-02-20 14:13:11 +01:00
J-Jamet
896f9327d6 Fix error messages #431 2020-02-20 11:58:00 +01:00
J-Jamet
cc83a99efe Try to fix #465 2020-02-20 09:06:11 +01:00
J Smith
b6da20fef7 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2020-02-19 23:53:44 +01:00
Kunzisoft
2912678559 Translated using Weblate (French)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-02-19 23:53:38 +01:00
WaldiS
409e870ef0 Translated using Weblate (Polish)
Currently translated at 99.5% (417 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2020-02-19 23:53:38 +01:00
Kunzisoft
3d84074a0a Translated using Weblate (English)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2020-02-19 23:53:37 +01:00
Oğuz Ersen
a717fdfed4 Translated using Weblate (Turkish)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-02-19 23:53:36 +01:00
J-Jamet
be1f68015b Merge branch 'master' into develop 2020-02-17 19:05:23 +01:00
WaldiS
2c29dcf1f6 Translated using Weblate (Polish)
Currently translated at 99.0% (415 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2020-02-17 00:50:57 +01:00
Filip Miletic
2b1173177f Translated using Weblate (Croatian)
Currently translated at 79.4% (333 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hr/
2020-02-17 00:50:55 +01:00
jan madsen
3c0f7dc79c Translated using Weblate (Danish)
Currently translated at 98.8% (414 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2020-02-17 00:50:42 +01:00
marzzzello
39d813bf3a Translated using Weblate (German)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-02-17 00:50:40 +01:00
Filip Miletic
5e2bc0d05b Translated using Weblate (Croatian)
Currently translated at 51.3% (215 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hr/
2020-02-14 04:18:25 +01:00
iLocIT!
ec751159ae Translated using Weblate (German)
Currently translated at 89.5% (375 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2020-02-14 04:18:02 +01:00
Filip Miletic
dda8b95f83 Translated using Weblate (Croatian)
Currently translated at 11.0% (46 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hr/
2020-02-13 19:31:33 +01:00
Filip Miletic
286012fe2a Added translation using Weblate (Croatian) 2020-02-13 11:04:02 +01:00
Jérémy JAMET
ab27299789 Update description for other KeePass app 2020-02-12 17:09:14 +01:00
Éfrit
dfcdd5aa88 Translated using Weblate (French)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-02-11 21:50:45 +01:00
Allan Nordhøy
a05ea52689 Translated using Weblate (Italian)
Currently translated at 72.8% (305 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2020-02-11 21:50:45 +01:00
zeritti
de12b5de5b Translated using Weblate (Czech)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2020-02-11 21:50:44 +01:00
solokot
57b03eaca4 Translated using Weblate (Russian)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-02-11 21:50:43 +01:00
Retrial
36bd00b760 Translated using Weblate (Greek)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/el/
2020-02-11 21:50:42 +01:00
Oğuz Ersen
4578a9974a Translated using Weblate (Turkish)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-02-11 21:50:33 +01:00
J-Jamet
d0371f58c6 Capture exception to avoid crash after clear clipboard 2020-02-08 17:39:08 +01:00
solokot
9f80457351 Translated using Weblate (Russian)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-02-08 16:39:00 +01:00
J-Jamet
56daf6f676 Remove JVMOverload in ClipboardHelper 2020-02-08 12:09:12 +01:00
J-Jamet
7f7b8d423b Merge branch 'translations' into develop 2020-02-08 11:34:35 +01:00
J-Jamet
031afc80cb Fix contribution strings 2020-02-08 11:32:56 +01:00
J-Jamet
b77e28b04d Merge branch 'master' of https://hosted.weblate.org/projects/keepass-dx/strings into translations 2020-02-08 11:27:36 +01:00
Allan Nordhøy
2f8c3fdcfe Translated using Weblate (Norwegian Bokmål)
Currently translated at 81.4% (341 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2020-02-08 11:27:15 +01:00
solokot
f501a87099 Translated using Weblate (Russian)
Currently translated at 99.8% (418 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-02-08 11:27:14 +01:00
Kunzisoft
d0ec5f26dd Translated using Weblate (English)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2020-02-08 11:27:13 +01:00
Mesut Akcan
97776e9329 Translated using Weblate (Turkish)
Currently translated at 98.3% (412 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2020-02-08 11:27:11 +01:00
Kunzisoft
a19356c49e Translated using Weblate (French)
Currently translated at 100.0% (419 of 419 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2020-02-08 11:21:45 +01:00
J-Jamet
4f16918cf0 Remove unused strings 2020-02-08 11:19:45 +01:00
J-Jamet
1af9761144 Merge branch 'master' into develop 2020-02-07 22:14:20 +01:00
J-Jamet
d74e814c79 Fix fastlane data 2020-02-07 22:05:58 +01:00
J-Jamet
16d09bca6c Merge tag '2.5.0.0beta26' into develop
2.5.0.0beta26
2020-02-07 21:45:51 +01:00
J-Jamet
462c29b769 Merge branch 'release/2.5.0.0beta26' 2020-02-07 21:45:40 +01:00
J-Jamet
3939276d58 Fix translations 2020-02-07 19:44:21 +01:00
J-Jamet
ff85f18c4c Update ReadMe.md 2020-02-07 19:24:12 +01:00
J-Jamet
dd14fe4123 Fix exception after changing theme 2020-02-07 19:20:43 +01:00
J-Jamet
d804659ee2 Change blue color of classic dark style 2020-02-07 19:10:48 +01:00
J-Jamet
e3152cf901 Remove AMOLED string for Black theme 2020-02-07 18:57:07 +01:00
J-Jamet
de236f321f Upgrade CHANGELOG 2020-02-07 18:26:27 +01:00
J-Jamet
66f4611866 Restore and delete entry history #335 2020-02-07 18:22:37 +01:00
heta1121
6a6ef052af Translated using Weblate (Swedish)
Currently translated at 94.9% (388 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sv/
2020-02-06 19:07:41 +01:00
J-Jamet
4e4606dabd Fix strings #436 2020-01-31 20:14:53 +01:00
J-Jamet
0ce11103ab Update CHANGELOG 2020-01-31 19:33:24 +01:00
J-Jamet
899d0e0557 Fix expired entries views 2020-01-31 19:29:40 +01:00
J-Jamet
4aefeff41f Hide expired entries from quick search when setting is activated #453 2020-01-31 17:26:36 +01:00
J-Jamet
08d59e50e8 Add android:resizeableActivity="true" for DeX mode 2020-01-31 16:48:15 +01:00
J-Jamet
e6c06aba8c Merge branch 'qtwyeuritoiy-feature/amoled_black' into develop 2020-01-31 13:12:37 +01:00
J-Jamet
e5c2a04922 Merge branch 'feature/amoled_black' of git://github.com/qtwyeuritoiy/KeePassDX into qtwyeuritoiy-feature/amoled_black 2020-01-31 13:12:18 +01:00
J-Jamet
c134ccf8d9 Encapsulate filters 2020-01-29 20:20:39 +01:00
J-Jamet
4f959d1ff6 Merge branch 'feature/hide_expired_entries' of git://github.com/qtwyeuritoiy/KeePassDX into qtwyeuritoiy-feature/hide_expired_entries 2020-01-29 17:22:40 +01:00
J-Jamet
1c06d93951 Fix custom fields in Magikeyboard 2020-01-29 17:10:26 +01:00
J-Jamet
b2dff29baa Auto performed "Go" key in Magikeyboard 2020-01-29 14:13:13 +01:00
J-Jamet
72633e9472 Change KEYCODE_ENTER by IME_ACTION_GO #369 2020-01-29 13:34:32 +01:00
J-Jamet
898a88f7d8 Fix app description 2020-01-27 12:52:46 +01:00
J-Jamet
c2f09f6666 Change "KeePass DX" name to "KeePassDX" and fix small issues #411 2020-01-27 12:43:45 +01:00
J-Jamet
d45bcbc347 Fix empty space in entry content 2020-01-27 11:18:54 +01:00
Jihoon Kim
1f834567f8 [Feature/HideExpiredEntries] Move 'Hide Expired Entries' Switch to 'Other' Category
Signed-off-by: Jihoon Kim <imsesaok@tuta.io>
2020-01-27 17:39:26 +08:00
Jihoon Kim
0b62c04867 [Feature/AmoledBlack] Add AMOLED Black Theme
Signed-off-by: Jihoon Kim <imsesaok@tuta.io>
2020-01-27 17:00:24 +08:00
J-Jamet
c71bb2a570 Password display box multiline #225 2020-01-26 18:02:11 +01:00
J-Jamet
0a575b5bf8 Prevent capture for all screens 2020-01-26 15:47:50 +01:00
J-Jamet
6f2bf903c5 Fix storage permission 2020-01-26 13:26:09 +01:00
Rodrigo Saldaña
210ad4b8db Translated using Weblate (Spanish)
Currently translated at 88.5% (362 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/es/
2020-01-25 19:21:30 +01:00
Allan Nordhøy
b42cf0e204 Translated using Weblate (Norwegian Bokmål)
Currently translated at 87.5% (358 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2020-01-23 09:21:33 +01:00
solokot
2045d3aab8 Translated using Weblate (Russian)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-01-23 09:21:32 +01:00
Allan Nordhøy
e20cb9431d Translated using Weblate (Danish)
Currently translated at 95.6% (391 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2020-01-23 09:21:31 +01:00
J-Jamet
7161eaea8b Allow write permission for specific file managers 2020-01-22 16:37:38 +01:00
J-Jamet
b786da52f5 Add error snackbar for error in entry edit view #431 2020-01-22 13:01:15 +01:00
J-Jamet
ccbfec838d Fix disclaimer formal string 2020-01-22 12:18:19 +01:00
J-Jamet
f5c1872225 Fix show read only warning 2020-01-22 10:53:24 +01:00
heta1121
3001013bad Translated using Weblate (Swedish)
Currently translated at 85.6% (350 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sv/
2020-01-21 17:21:30 +01:00
Allan Nordhøy
1280803e5e Translated using Weblate (Norwegian Bokmål)
Currently translated at 83.9% (343 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2020-01-19 15:22:00 +01:00
heta1121
f84fc07fe0 Translated using Weblate (Swedish)
Currently translated at 85.1% (348 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sv/
2020-01-19 15:22:00 +01:00
J-Jamet
d83e53f589 Fix read only for recycle bin 2020-01-19 12:17:27 +01:00
J-Jamet
52ba2c53f1 Fix progress for Android < 5 2020-01-18 15:23:51 +01:00
J-Jamet
f266e1de4c Add string to open the attachment downloaded 2020-01-18 12:24:21 +01:00
J-Jamet
5a0aafd3ce Update CHANGELOG 2020-01-18 12:19:56 +01:00
J-Jamet
c7ce07a43c Change file size format 2020-01-18 12:15:14 +01:00
J-Jamet
a73644c285 Merge branch 'feature/Stream_Binaries' into develop 2020-01-18 12:03:55 +01:00
J-Jamet
e28f1ffc99 Fix attachment orientation change 2020-01-18 11:33:17 +01:00
Deleted User
afc691b2f4 Translated using Weblate (Norwegian Bokmål)
Currently translated at 83.9% (343 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2020-01-16 06:27:50 +01:00
J-Jamet
00f6eb83c3 Fix close notification 2020-01-15 05:52:32 +01:00
J-Jamet
bbf51ebbec Multiple attachments download and add file compression 2020-01-14 19:44:10 +01:00
Jihoon Kim
7816c8f16e [Feature/HideExpiredEntries] Apply 'Hide Expired Entries' Setting And Adjust Item Count Accordingly
Signed-off-by: Jihoon Kim <imsesaok@tuta.io>
2020-01-14 17:28:27 +08:00
Jihoon Kim
fdbcba2412 [Feature/HideExpiredEntries] Add Preference Entry for 'Hide Expired Entries'
Signed-off-by: Jihoon Kim <imsesaok@tuta.io>
2020-01-14 17:28:27 +08:00
J-Jamet
50dc6cb0aa Fix download attachment 2020-01-13 10:05:34 +01:00
J-Jamet
e413198d12 Download attachment dynamic string 2020-01-10 19:40:00 +01:00
solokot
72592772f9 Translated using Weblate (Russian)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-01-09 09:21:27 +01:00
solokot
f918206bcd Translated using Weblate (Russian)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2020-01-07 22:27:33 +01:00
J-Jamet
6b34d67da8 Remove WRITE_EXTERNAL_STORAGE for Android > 18
Download attachment as service
2020-01-07 19:43:41 +01:00
Retrial
04fb093d4d Translated using Weblate (Greek)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/el/
2020-01-04 18:21:28 +01:00
J-Jamet
7de0e0cc4a Add Attachment view 2019-12-31 14:34:51 +01:00
J-Jamet
5cf7688e38 Merge branch 'develop' into feature/Stream_Binaries 2019-12-31 12:05:48 +01:00
J-Jamet
97cd06b52b Merge branch 'feature/Duplicate_UUID_Database1' into develop 2019-12-31 11:23:44 +01:00
J-Jamet
6d522ead1d Fix illegal argument exception and refactor KDB parser code 2019-12-31 08:26:15 +01:00
J-Jamet
8e0fae62f3 Merge branch 'develop' into feature/Stream_Binaries 2019-12-30 14:09:05 +01:00
J-Jamet
af72098d60 Refactor bytes utility methods 2019-12-30 12:59:50 +01:00
Taeko Hasegawa
62da582f4e Translated using Weblate (Japanese)
Currently translated at 36.4% (149 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ja/
2019-12-23 14:21:24 +01:00
J-Jamet
f3efa6eddc Add parameter to fix duplicate UUID in DatabaseV1 2019-12-22 18:26:35 +01:00
J-Jamet
c85fb7bd0a Better element output KDB implementation 2019-12-22 18:13:26 +01:00
J-Jamet
fc7b183461 Update CHANGELOG 2019-12-22 17:51:48 +01:00
J-Jamet
a378810f88 Improvement code for ClipboardHelper 2019-12-21 21:36:33 +01:00
J-Jamet
2f5b322fad Fix crash during clean clipboard in Android 9 2019-12-21 21:31:12 +01:00
J-Jamet
efb9b50f85 Fix issue #127 KDBX binaries 2019-12-21 21:18:58 +01:00
J-Jamet
84fdef8eb6 Merge branch 'develop' into feature/Stream_Binaries 2019-12-21 19:46:05 +01:00
J-Jamet
76050a261b Update to version 2.5.0.0beta26 2019-12-21 19:16:30 +01:00
Éfrit
2667361450 Translated using Weblate (French)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-12-19 17:18:38 +01:00
Éfrit
a2b3313cc0 Translated using Weblate (French)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-12-18 22:50:52 +01:00
J-Jamet
d343446235 Check file size to not corrupt a database v1 if too high 2019-12-18 18:58:26 +01:00
J-Jamet
cbce70f8a4 Save KDB binaries as file 2019-12-18 18:46:50 +01:00
WaldiS
4573d6b6fb Translated using Weblate (Polish)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-12-18 14:21:21 +01:00
Chen Wang
973305d13c Translated using Weblate (Chinese (Simplified))
Currently translated at 99.3% (406 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2019-12-16 08:21:13 +01:00
solokot
4637016372 Translated using Weblate (Russian)
Currently translated at 99.8% (408 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2019-12-16 08:21:12 +01:00
Chen Wang
698ba4f7f1 Translated using Weblate (English)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2019-12-16 08:21:11 +01:00
Oğuz Ersen
502ebabf1f Translated using Weblate (Turkish)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-12-16 08:21:10 +01:00
WaldiS
13f88626ca Translated using Weblate (Polish)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-12-08 15:13:26 +01:00
zeritti
a39f58f7b5 Translated using Weblate (Czech)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2019-12-08 15:13:25 +01:00
nautilusx
aeb36468ce Translated using Weblate (German)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-12-08 15:13:23 +01:00
J-Jamet
fdd196526d Better InputStream / OutputStream close 2019-12-05 19:28:54 +01:00
Mattias Münster
ecca892b16 Translated using Weblate (Swedish)
Currently translated at 25.9% (106 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sv/
2019-12-04 02:05:37 +01:00
Rudah Ximenes Alvarenga
03343d0301 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_BR/
2019-12-04 02:05:36 +01:00
Allan Nordhøy
a61c8b0cb6 Translated using Weblate (Norwegian Bokmål)
Currently translated at 83.6% (342 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2019-12-02 03:33:26 +01:00
Kunzisoft
a92129da00 Translated using Weblate (French)
Currently translated at 99.5% (407 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-12-02 03:33:24 +01:00
Allan Nordhøy
0b783d6390 Translated using Weblate (English)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2019-12-02 03:33:23 +01:00
jan madsen
9aa1a2e30e Translated using Weblate (Danish)
Currently translated at 95.8% (392 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2019-12-02 03:33:23 +01:00
C. Rüdinger
15b3c69514 Translated using Weblate (German)
Currently translated at 99.5% (407 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-12-02 03:33:21 +01:00
Oğuz Ersen
f2722e09ec Translated using Weblate (Turkish)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-12-02 03:33:21 +01:00
Deleted User
b442859be0 Translated using Weblate (German)
Currently translated at 100.0% (409 of 409 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-12-01 17:45:31 +01:00
J-Jamet
1496a2efb1 Fix database decryption with attachment 2019-11-30 19:26:26 +01:00
J-Jamet
a0edb111f0 Merge branch 'develop' into feature/Stream_Binaries 2019-11-30 18:27:21 +01:00
J-Jamet
6bcf54fe29 Merge tag '2.5.0.0beta25' into develop
2.5.0.0beta25
2019-11-30 17:43:19 +01:00
J-Jamet
3d7e24816a Merge branch 'release/2.5.0.0beta25' 2019-11-30 17:43:05 +01:00
J-Jamet
f5e02ec63f Merge branch 'translations' into develop 2019-11-30 17:27:39 +01:00
J-Jamet
ed1f4ebace Merge branch 'master' of https://hosted.weblate.org/projects/keepass-dx/strings into translations 2019-11-30 17:27:20 +01:00
J-Jamet
eb0e496cfd Try to decrypt DatabaseV1 by InputStream 2019-11-30 17:02:09 +01:00
J-Jamet
8b9fc30a6d Upgrade description 2019-11-30 10:12:43 +01:00
J-Jamet
12c2a6e99c Refactor bytes utility methods 2019-11-29 14:19:13 +01:00
J-Jamet
714433b62d Refactor stream methods 2019-11-28 16:32:16 +01:00
J-Jamet
e42933d786 Move SortNodeEnum 2019-11-27 22:49:59 +01:00
J-Jamet
e9531e4edd Merge branch 'feature/Refactor_Class' into develop 2019-11-27 19:21:36 +01:00
J-Jamet
0cd9cd294d Move database elements in packages 2019-11-27 19:19:42 +01:00
J-Jamet
b03fb12fca Refactor Input and Output 2019-11-27 18:59:09 +01:00
J-Jamet
b7b2e8dc4e Change class by KDB and KDBX 2019-11-27 18:51:28 +01:00
J-Jamet
96568abc51 Update CHANGELOG 2019-11-27 17:36:40 +01:00
J-Jamet
a180688858 Merge branch 'feature/OOM' into develop 2019-11-27 17:34:44 +01:00
J-Jamet
2590067558 Fix exception message 2019-11-27 17:34:26 +01:00
J-Jamet
b5499238f7 Change binary attachment compression after change compression setting 2019-11-27 16:46:14 +01:00
J-Jamet
cc5b96f539 Fix OOM by stream implementation and add KDBX version for DatabaseV2 2019-11-26 17:21:33 +01:00
J-Jamet
32343dc937 Refactor binary class 2019-11-26 11:07:29 +01:00
J-Jamet
1e71dd3031 Better binary reader 2019-11-25 21:14:39 +01:00
J-Jamet
ebf6f6a52a Replace Base64 lib 2019-11-25 19:25:08 +01:00
J-Jamet
6bf6d661af New databaseV2 XML write methods 2019-11-25 18:20:02 +01:00
J-Jamet
fe16879f35 New method to clear the clipboard 2019-11-25 15:12:27 +01:00
J-Jamet
ead384d1bb Update invalid credentials error message 2019-11-25 14:20:28 +01:00
J-Jamet
1ebdc0bacd Fix copy and move entry crash in root for DatabaseV1 2019-11-25 14:17:07 +01:00
zeritti
8eca8cdd53 Translated using Weblate (Czech)
Currently translated at 100.0% (399 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2019-11-25 14:04:59 +01:00
J-Jamet
24c61b1b37 Partial fix of Database V1 node deletion #394 2019-11-25 13:59:32 +01:00
J-Jamet
ea18cc7166 Remove unused element 2019-11-21 18:09:54 +01:00
J-Jamet
387c499829 Fix expires for new entry in DatabaseV1 2019-11-21 17:01:57 +01:00
J-Jamet
339470dd6e Fix open button 2019-11-21 16:15:21 +01:00
J-Jamet
02d1089dbc Better biometric state management 2019-11-21 13:12:38 +01:00
J-Jamet
1bc0932cec Fix fingerprint animation 2019-11-21 12:13:08 +01:00
J-Jamet
76cc2739c8 Change biometric prompt messages 2019-11-21 11:58:59 +01:00
J-Jamet
b23908aec2 Change biometric error 2019-11-21 10:50:49 +01:00
J-Jamet
6b1b8c0f7b Add biometric error message 2019-11-21 10:44:22 +01:00
J-Jamet
06320a7eba Add sw600dp-port dimen 2019-11-21 10:13:04 +01:00
WaldiS
fc02145d0c Translated using Weblate (Polish)
Currently translated at 99.7% (398 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-11-21 10:04:56 +01:00
Gaurav Sehar
5360738775 Translated using Weblate (Hindi)
Currently translated at 23.3% (93 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hi/
2019-11-21 10:04:54 +01:00
J-Jamet
0957df752a Fix creating database 2019-11-21 10:04:34 +01:00
J-Jamet
fe56d06b5d Merge branch 'develop' of github.com:Kunzisoft/KeePassDX into develop 2019-11-21 09:16:34 +01:00
J-Jamet
f4955b16cd Remove specific ndk version to avoid error on FDroid 2019-11-20 23:10:56 +01:00
J-Jamet
07692ba73d Implicit confirmation for biometric #390 2019-11-20 19:36:05 +01:00
J-Jamet
64ac3e8f32 Change reader header and data equals 2019-11-20 16:06:59 +01:00
J-Jamet
8013d3109a Small code change 2019-11-20 12:55:14 +01:00
J-Jamet
be6f01dc99 Fix PWDate and blocks 2019-11-20 12:40:56 +01:00
Mattias Münster
30f7779ec6 Translated using Weblate (Swedish)
Currently translated at 26.8% (107 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sv/
2019-11-20 00:04:47 +01:00
solokot
6b5823ca70 Translated using Weblate (Russian)
Currently translated at 100.0% (399 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2019-11-20 00:04:45 +01:00
J-Jamet
d4a09ed569 Merge branch 'feature/Manually_Save_Satabase' into develop 2019-11-19 19:18:00 +01:00
J-Jamet
84fe409c4b Nested fragment for general and database settings 2019-11-18 21:18:34 +01:00
J-Jamet
51d90891ad Fix auto save database 2019-11-18 19:59:50 +01:00
J-Jamet
b1fa06099c Refactor action database and add new execution command string 2019-11-18 19:55:17 +01:00
J-Jamet
fa9d8ad6ad Refactor progress dialog thread / save database menu 2019-11-18 17:54:13 +01:00
Azurak
6703d7b451 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (399 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2019-11-17 02:04:55 +01:00
Azurak
73afd44d9c Translated using Weblate (Chinese (Traditional))
Currently translated at 48.4% (193 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hant/
2019-11-17 02:04:53 +01:00
Stephan Paternotte
93cb93bb9b Translated using Weblate (Dutch)
Currently translated at 99.2% (396 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nl/
2019-11-17 02:04:49 +01:00
WaldiS
82902cff71 Translated using Weblate (Polish)
Currently translated at 95.7% (382 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-11-17 02:04:46 +01:00
Azurak
3657f7e54c Translated using Weblate (English)
Currently translated at 100.0% (399 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2019-11-17 02:04:44 +01:00
Kunzisoft
a57a2f738d Translated using Weblate (English)
Currently translated at 100.0% (399 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2019-11-17 02:04:44 +01:00
jan madsen
b93592d703 Translated using Weblate (Danish)
Currently translated at 96.0% (383 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2019-11-17 02:04:44 +01:00
C. Rüdinger
fd288e624b Translated using Weblate (German)
Currently translated at 99.7% (398 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-11-17 02:04:42 +01:00
Rudah Ximenes Alvarenga
095fa3f6ef Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (399 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_BR/
2019-11-17 02:04:41 +01:00
Oğuz Ersen
b1f6c578ad Translated using Weblate (Turkish)
Currently translated at 100.0% (399 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-11-17 02:04:37 +01:00
Mesut Akcan
1bc991bfcb Translated using Weblate (Turkish)
Currently translated at 100.0% (399 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-11-17 02:04:32 +01:00
J-Jamet
02feb478e8 Add autosave database setting 2019-11-16 20:18:07 +01:00
J-Jamet
c2df79f0c9 Fix save database in read only mode 2019-11-16 19:36:29 +01:00
J-Jamet
ef13965747 Fix save database in read only mode 2019-11-16 18:34:39 +01:00
Kunzisoft
13421601de Translated using Weblate (French)
Currently translated at 99.7% (398 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-11-16 11:53:24 +01:00
Azurak
5a9b46c4b5 Translated using Weblate (Chinese (Traditional))
Currently translated at 33.3% (133 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hant/
2019-11-15 18:31:47 +01:00
Thomas
6268642097 Translated using Weblate (Chinese (Traditional))
Currently translated at 33.3% (133 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hant/
2019-11-15 18:31:45 +01:00
J-Jamet
12eadbc092 Update CHANGELOG 2019-11-14 16:35:23 +01:00
J-Jamet
c9475d1dc2 Fix concurrent exception when update group 2019-11-14 16:28:44 +01:00
J-Jamet
56805defb6 Merge branch 'feature/RecyclerBin' into develop 2019-11-14 15:57:09 +01:00
J-Jamet
477a784201 Empty recycle bin 2019-11-14 15:56:30 +01:00
J-Jamet
f54bac15c9 Add recycle bin setting and fix delete node 2019-11-14 13:14:02 +01:00
J-Jamet
ae28ebe701 Add dialog when permanently delete nodes 2019-11-14 09:59:55 +01:00
oskamuelller4fs
f16adf00da Translated using Weblate (German)
Currently translated at 100.0% (399 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-11-14 03:20:56 +01:00
C. Rüdinger
d17699f6f7 Translated using Weblate (German)
Currently translated at 96.2% (384 of 399 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-11-14 01:43:58 +01:00
J-Jamet
8afcf043ee Fix database name 2019-11-13 20:06:44 +01:00
J-Jamet
dda9d034aa Same feature as KeePass2 for entry history 2019-11-13 19:17:36 +01:00
J-Jamet
652bad51b4 Fix infinite dialog after entry is save #387 2019-11-13 18:54:04 +01:00
J-Jamet
4d2ccc0789 Remove unused onPreferenceClick 2019-11-13 17:42:50 +01:00
J-Jamet
0e1c21e0f4 Small code change 2019-11-13 17:31:38 +01:00
J-Jamet
8d3f58b2cc Merge branch 'feature/RefactorImporter' into develop 2019-11-13 17:29:30 +01:00
J-Jamet
be78905d85 Rename input output utility class for db 2019-11-13 17:04:41 +01:00
J-Jamet
b3f232c840 Upgrade Importer V3 2019-11-13 15:57:52 +01:00
J-Jamet
075a16c1c3 Remove OTP menu from DatabaseV1 2019-11-13 12:58:10 +01:00
J-Jamet
18a13e6266 Fix OTP base32 length and remove spaces 2019-11-13 12:46:52 +01:00
Jérémy JAMET
7149bdbc3a Update bug_report.md 2019-11-12 20:03:19 +01:00
J-Jamet
9a4c4aa9bf Fix string bugs 2019-11-12 17:38:08 +01:00
Hosted Weblate
2b32cab9d1 Merge branch 'origin/master' into Weblate. 2019-11-12 17:06:11 +01:00
J-Jamet
66611db261 Fix TOTP secret uppercase #381 2019-11-12 15:45:40 +01:00
J-Jamet
fdc2095b42 Remove access view 2019-11-12 15:38:12 +01:00
J-Jamet
2f9cab0da2 Upgrade to 2.5.0.0beta25 2019-11-12 13:21:21 +01:00
J-Jamet
bd0d474751 Upgrade Pro description 2019-11-12 13:08:47 +01:00
J-Jamet
cca0ab2669 Merge tag '2.5.0.0beta24' into develop
2.5.0.0beta24
2019-11-12 12:35:28 +01:00
J-Jamet
cb5ca575d5 Merge branch 'release/2.5.0.0beta24' 2019-11-12 12:35:16 +01:00
J-Jamet
f4caaad9ee Fix invalid_db_same_uuid error 2019-11-12 12:09:58 +01:00
J-Jamet
b9cfb32a20 Fix OTP dialog 2019-11-12 11:56:19 +01:00
J-Jamet
095e5e5dd6 Add FLAG_GRANT_PREFIX_URI_PERMISSION flag 2019-11-12 10:43:32 +01:00
J-Jamet
ffc58688d8 Upgrade Gradle 2019-11-12 09:38:36 +01:00
Allan Nordhøy
6a69a7f416 Translated using Weblate (Norwegian Bokmål)
Currently translated at 94.5% (360 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2019-11-11 23:04:19 +01:00
J-Jamet
b4188b4712 Fix small bugs 2019-11-11 16:50:05 +01:00
J-Jamet
4a22c28df4 Merge branch 'translations' into develop 2019-11-11 16:34:39 +01:00
J-Jamet
76e9a25b1a Merge branch 'master' of https://hosted.weblate.org/projects/keepass-dx/strings into translations 2019-11-11 16:33:54 +01:00
J-Jamet
1928d0823e Fix small french translations 2019-11-11 14:47:57 +01:00
J-Jamet
c183d22412 Upgrade Biometric library 2019-11-11 14:44:38 +01:00
J-Jamet
b684353721 Upgrade CHANGELOG and Readme.md 2019-11-11 14:19:29 +01:00
J-Jamet
72f0e871c7 Fix close clipboard notification 2019-11-11 14:09:41 +01:00
J-Jamet
9a63962903 Fix clipboard notification with auto generated field 2019-11-11 14:00:30 +01:00
J-Jamet
938de28b49 Minimized hidden text #373 2019-11-10 16:30:22 +01:00
J-Jamet
20fc094d71 Steam OTP on closed and full version 2019-11-10 15:26:52 +01:00
J-Jamet
40180d5883 Merge branch 'feature/TOTP' into develop 2019-11-10 14:33:35 +01:00
J-Jamet
59e5865318 Fix OTP errors 2019-11-10 14:31:21 +01:00
J-Jamet
f63d6bdc1d Fix OTP orientation change listener 2019-11-10 14:10:36 +01:00
J-Jamet
fe33c0ae7d Fix init OTP elements 2019-11-10 13:20:19 +01:00
J-Jamet
ca4ad1c1fd Validate OTP dialog only if no error 2019-11-10 12:19:06 +01:00
J-Jamet
adf5382804 Add min and max values for OTP 2019-11-10 11:25:34 +01:00
J-Jamet
7f5406ac98 OTP errors implementation 2019-11-09 15:19:14 +01:00
J-Jamet
23b21ea154 Fix OTP dialog 2019-11-08 19:36:59 +01:00
Antonio F
49d4d0421a Translated using Weblate (Spanish)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/es/
2019-11-08 18:04:10 +01:00
J-Jamet
23859a61bb Build OTP url 2019-11-07 19:28:54 +01:00
J-Jamet
221f81f51e Change ObjectNameResource and add OTP Dialog 2019-11-07 15:18:57 +01:00
J-Jamet
6e7c0d5073 Upgrade NDK version 2019-11-07 12:31:39 +01:00
Rudah Ximenes Alvarenga
e8e3d53685 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_BR/
2019-11-06 21:04:12 +01:00
J-Jamet
e6d9df2b98 Add OTP auto generate field for Magikeyboard 2019-11-06 13:39:19 +01:00
J-Jamet
477a8f2e38 Update OTP code with AndOTP and kotlinized OtpEntryFields 2019-11-05 19:57:20 +01:00
J-Jamet
5e66697b8b Fix TOTP retrievement and add HOTP fields 2019-11-05 17:30:27 +01:00
J-Jamet
16320abb7d Global OTP variables, add progress bar, remove seconds 2019-11-05 16:04:30 +01:00
J-Jamet
f122c2832c Add OTP and fix TOTP generation 2019-11-05 13:42:51 +01:00
J-Jamet
d84c561f44 Merge branch 'studio315b-totpReadMode' into feature/TOTP 2019-11-04 18:23:17 +01:00
J-Jamet
da49c9c045 Merge branch 'totpReadMode' of git://github.com/studio315b/KeePassDX into studio315b-totpReadMode 2019-11-04 17:52:14 +01:00
J-Jamet
553920e37c Add setting to enable persistent notification 2019-11-03 11:29:28 +01:00
J-Jamet
450d2d113b Fix stop notification when edit entry 2019-11-03 10:55:47 +01:00
WaldiS
744c80e34d Translated using Weblate (Polish)
Currently translated at 99.7% (380 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-11-01 21:03:53 +01:00
J-Jamet
c0f8cca7c6 Fix history copy 2019-11-01 15:43:08 +01:00
J-Jamet
b129f220f7 Move copy button to the top #374 2019-11-01 15:09:41 +01:00
J-Jamet
7a3df02e38 Add explanation for settings 2019-10-30 21:40:53 +01:00
J-Jamet
befd29c396 Remove Magikeyboard explanation 2019-10-30 21:07:07 +01:00
J-Jamet
b8245621ea Remove fingerprint references 2019-10-30 20:19:33 +01:00
J-Jamet
ecda25a743 Fix biometric during orientation change 2019-10-30 19:36:52 +01:00
J-Jamet
d97a85b997 Remove biometric link if change credential 2019-10-30 15:21:50 +01:00
J-Jamet
8c0d7ab9ed Better Action runnable implementation 2019-10-30 14:49:40 +01:00
Allan Nordhøy
f3fa73ea34 Translated using Weblate (Norwegian Bokmål)
Currently translated at 94.5% (360 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2019-10-30 11:03:44 +01:00
Philipp Fischbeck
788734ccad Translated using Weblate (German)
Currently translated at 98.4% (375 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-10-30 11:03:43 +01:00
J-Jamet
e088f4a4ad Fix opening database after register biometric 2019-10-30 10:16:27 +01:00
J-Jamet
86bd018e4e Validate preference dialog with virtual done keyboard button 2019-10-29 11:25:20 +01:00
J-Jamet
283145034d Fix preference number 2019-10-29 10:50:09 +01:00
J-Jamet
163162497e Update libraries 2019-10-29 10:34:02 +01:00
J-Jamet
56911fb58f Refresh number of children after an action 2019-10-29 10:29:06 +01:00
J-Jamet
dae6481aff Update CHANGELOG 2019-10-29 10:16:31 +01:00
J-Jamet
6b2eb5e4f6 Merge branch 'feature/Database_Notification' into develop 2019-10-29 10:13:23 +01:00
J-Jamet
c563787f73 Change notification icons 2019-10-29 10:09:46 +01:00
J-Jamet
2737755b85 Change notification icons 2019-10-29 09:38:01 +01:00
Trygve John
fd9486ca77 Translated using Weblate (Norwegian Bokmål)
Currently translated at 94.5% (360 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2019-10-29 09:03:19 +01:00
J-Jamet
14020ec0b5 Fix clipboard notification timeout 2019-10-28 12:20:22 +01:00
J-Jamet
5a6c466ebd Add notification for opened database and use compat notification manager 2019-10-28 11:50:06 +01:00
WaldiS
76fcd5fe19 Translated using Weblate (Polish)
Currently translated at 99.2% (378 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-10-26 19:53:28 +02:00
J-Jamet
3732ff1ebc Fix clean clipboard after killing app by swap 2019-10-26 08:58:36 +02:00
Aykut ÖZDEMİR
22dd09954b Translated using Weblate (Turkish)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-10-25 13:53:16 +02:00
J-Jamet
ef7387f2f3 Remove entry notifications after killing app (by swap) 2019-10-25 10:08:39 +02:00
J-Jamet
f774298587 Update CHANGELOG 2019-10-24 15:23:52 +02:00
J-Jamet
f8134307f6 Fix long click in paste mode 2019-10-24 13:10:52 +02:00
J-Jamet
fe461f2e7c Fix action error 2019-10-24 12:36:22 +02:00
J-Jamet
023c841747 Better code 2019-10-24 11:49:23 +02:00
J-Jamet
af95c0903a Better node error implementation 2019-10-24 11:29:43 +02:00
J-Jamet
0d756db8aa Beter code 2019-10-24 11:07:58 +02:00
J-Jamet
2c5dcc9b11 Fix back for action selection mode 2019-10-24 10:25:21 +02:00
Éfrit
21c6ea73b2 Translated using Weblate (French)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-10-22 23:53:07 +02:00
C. Rüdinger
51dc302bb0 Translated using Weblate (German)
Currently translated at 97.9% (373 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-10-22 23:53:06 +02:00
J-Jamet
87760ab4f6 Fix save database and finish 2019-10-22 15:02:51 +02:00
J-Jamet
88ebe58a88 Fix action node 2019-10-22 14:46:13 +02:00
villabunterkunt
fb023b81b5 Translated using Weblate (German)
Currently translated at 97.9% (373 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-10-21 21:02:16 +02:00
J-Jamet
2de6bbc6c0 Fix selected color 2019-10-21 17:12:40 +02:00
J-Jamet
4ef436629d Merge branch 'feature/Loading_Database' into develop 2019-10-21 11:09:37 +02:00
Allan Nordhøy
9fd342f1e7 Translated using Weblate (Norwegian Bokmål)
Currently translated at 89.5% (341 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2019-10-21 02:53:03 +02:00
abidin toumi
8988f17765 Translated using Weblate (Arabic)
Currently translated at 71.1% (271 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/
2019-10-19 10:52:58 +02:00
J-Jamet
9d160db281 Add setting updates for dialog thread 2019-10-18 13:44:28 +02:00
J-Jamet
2e58c2f1b3 Fix duplication during deletion 2019-10-17 20:02:49 +02:00
J-Jamet
d1d2b99e09 Fix autofill selection 2019-10-17 15:17:18 +02:00
J-Jamet
def9744f75 Fix launch group activity when database loaded 2019-10-17 14:54:34 +02:00
J-Jamet
214e2cf109 Better database loading code 2019-10-17 14:07:39 +02:00
J-Jamet
b25180c617 Show update message and fix dialog retrievment 2019-10-17 11:29:30 +02:00
J-Jamet
6a5263df77 Fix biometric recognition error with new loading process 2019-10-17 10:28:24 +02:00
J-Jamet
2982f67717 Fix biometric recognition with new loading process 2019-10-17 10:24:12 +02:00
J-Jamet
b559670dff Fix result security by binder 2019-10-17 09:53:00 +02:00
J-Jamet
891d3142d2 Better code for bind callback 2019-10-16 18:49:15 +02:00
J-Jamet
2637788429 Fix dialog update 2019-10-16 15:31:07 +02:00
J-Jamet
a21de3b892 Fix update group 2019-10-16 11:48:35 +02:00
J-Jamet
e087e19120 Fix update entry 2019-10-16 10:46:56 +02:00
J-Jamet
721d61dda7 Fix update nodes 2019-10-15 18:05:33 +02:00
J-Jamet
e0e7e431cf Best update and nodeId type implementation 2019-10-15 17:02:59 +02:00
J-Jamet
93948e7c61 First commit for async loading 2019-10-15 16:01:50 +02:00
J-Jamet
b150c718a0 Fix edit notes field limited to 4 lines 2019-10-14 10:17:48 +02:00
J-Jamet
a71e4c3902 Add language parameter for issue template 2019-10-14 09:57:06 +02:00
Adolfo Jayme Barrientos
b7dc13d863 Translated using Weblate (Spanish)
Currently translated at 89.8% (342 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/es/
2019-10-12 16:52:53 +02:00
J-Jamet
1e3c58e359 Fix clipboard LOCK_DATABASE signal #366 2019-10-11 12:52:43 +02:00
J-Jamet
5f75599e9f Replace notification icons by PNG #364 2019-10-11 11:50:16 +02:00
J-Jamet
b602f9b77d Add icon for paste action 2019-10-11 10:58:21 +02:00
J-Jamet
aa948c1ece Remove null for BiometricUnlockCallback 2019-10-11 10:55:05 +02:00
J-Jamet
e599a51152 Refactor prompt build code 2019-10-11 10:41:45 +02:00
J-Jamet
ee6052f4d1 Update CHANGELOGS 2019-10-11 10:21:04 +02:00
J-Jamet
eba527f477 Add unrecoverable key exception 2019-10-11 10:18:28 +02:00
J-Jamet
09e0d6d3cc Update biometric and room lib 2019-10-11 10:03:44 +02:00
J-Jamet
9aefc984be Merge branch 'feature/Multiple_Node_Action' into develop #275 2019-10-10 19:19:08 +02:00
J-Jamet
2ce3b21f1b Add number of selected action nodes 2019-10-10 19:17:38 +02:00
J-Jamet
4d2f3cb4b1 Remove unused key for orientation change 2019-10-10 19:04:24 +02:00
J-Jamet
e62b46c4c0 Fix open and edit action bar 2019-10-10 18:38:15 +02:00
J-Jamet
6472601170 Add Toolbar action animation 2019-10-10 18:34:05 +02:00
J-Jamet
89dd7bfefb Fix finish node action when exit activity 2019-10-10 17:59:23 +02:00
J-Jamet
fb2ea4c0ed Multiple action node for Move Copy and Delete 2019-10-10 17:53:35 +02:00
J-Jamet
8d84358d48 Refactor code 2019-10-10 12:49:16 +02:00
J-Jamet
1d8661c633 Better node action refresh 2019-10-09 21:19:10 +02:00
J-Jamet
48130eee45 Change node action menu order 2019-10-09 20:23:22 +02:00
J-Jamet
2cf83962fe Merge branch 'develop' into feature/Multiple_Node_Action 2019-10-09 20:20:25 +02:00
J-Jamet
aecf7c0c39 Fix remove node at 2019-10-09 20:20:10 +02:00
J-Jamet
39606e2676 Custom Toolbar action 2019-10-09 20:16:11 +02:00
Ema Panz
6e00fa2d01 Translated using Weblate (Italian)
Currently translated at 95.5% (364 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2019-10-09 15:58:47 +02:00
J-Jamet
f79aa339e9 Selection by Contextual Menu 2019-10-09 15:58:00 +02:00
J-Jamet
f412fce912 Add safeNextText method to XmlPullParser 2019-10-07 15:51:44 +02:00
J-Jamet
cc20b7503c Refactor exceptions 2019-10-07 15:19:13 +02:00
Jérémy JAMET
2573434763 Update Readme.md
update FAQ
2019-10-07 09:33:21 +02:00
Jérémy JAMET
f153c26fef Set theme jekyll-theme-cayman 2019-10-07 09:15:48 +02:00
Ldm Public
125f461cbe Translated using Weblate (French)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-10-06 16:56:46 +02:00
WaldiS
b705b4b712 Translated using Weblate (Polish)
Currently translated at 99.2% (378 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-10-06 16:56:45 +02:00
Retrial
c67b0bb858 Translated using Weblate (Greek)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/el/
2019-10-06 16:56:45 +02:00
J-Jamet
ab1fc8c5d5 Remove CONTRIBUTORS file no longer updated, see https://github.com/Kunzisoft/KeePassDX/graphs/contributors 2019-10-06 11:46:35 +02:00
J-Jamet
8477f4ba08 Fix education in PasswordActivity 2019-10-06 11:15:56 +02:00
J-Jamet
e6518ffdc8 Upgrade ISSUE_TEMPLATE 2019-10-06 10:39:30 +02:00
J-Jamet
99917c7f28 Try to prevent XXE #200 2019-10-06 10:25:13 +02:00
J-Jamet
fcc29f67a3 Upgrade CHANGELOGS 2019-10-05 15:46:18 +02:00
J-Jamet
7dd49f050c Highlight expires entries 2019-10-05 15:43:45 +02:00
J-Jamet
5f96de84b0 Manage expires 2019-10-05 15:32:04 +02:00
J-Jamet
54c2f5a61f Fix Minor Accessibility Issues #334 2019-10-05 12:56:09 +02:00
J-Jamet
921c6f88aa Merge branch 'feature/OPEN_DOCUMENT' into develop 2019-10-05 12:43:40 +02:00
J-Jamet
a0cb579df4 Merge branch 'develop' into feature/OPEN_DOCUMENT 2019-10-05 11:47:27 +02:00
J-Jamet
d6a7c34ff3 Update gradle 2019-10-05 11:47:12 +02:00
J-Jamet
bf2e61f149 Merge branch 'develop' into feature/OPEN_DOCUMENT 2019-10-05 11:45:54 +02:00
J-Jamet
eaf5dc5988 Show number of children in toolbar #282 2019-10-05 11:27:34 +02:00
J-Jamet
879ee013db Fix multiple biometric menu #332 2019-10-05 10:12:45 +02:00
J-Jamet
e13d53eae4 Unable default username and custom color to Database V2 2019-10-05 09:22:41 +02:00
J-Jamet
d72c8184c9 Fix databaseV1 settings 2019-10-04 19:00:18 +02:00
Kunzisoft
c4d3c8cbfb Translated using Weblate (French)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-10-04 18:37:57 +02:00
J-Jamet
02a3d85f80 Try to fix "Unable to launch Activity from MagiKeyboard" #348 2019-10-04 16:53:20 +02:00
J-Jamet
19b0722f1f Add KeePass DX version in ISSUE_TEMPLATE 2019-10-04 16:36:20 +02:00
J-Jamet
f14222b192 Merge branch 'develop' into Bug_Feature_Template 2019-10-04 16:22:37 +02:00
J-Jamet
4f4f6d30d9 Change menu biometric order 2019-10-04 16:22:08 +02:00
J-Jamet
fdd329e982 Fix unchecked "Use as default database" doesn't work #354 2019-10-04 13:20:30 +02:00
J-Jamet
55a4d388b3 Fix V1 locked with keyFile and no password #353 2019-10-04 13:15:10 +02:00
J-Jamet
5c6be448ec Upgrade Changelogs 2019-10-04 12:00:22 +02:00
J-Jamet
3e79ddcc21 Merge branch 'feature/Color_Preference' into develop 2019-10-04 11:50:24 +02:00
J-Jamet
5362758424 Disable custom database color 2019-10-04 11:50:11 +02:00
J-Jamet
c10e3df2a7 Enable settings as switch 2019-10-04 11:47:33 +02:00
J-Jamet
166784021a Fix color setting orientation change 2019-10-04 11:44:16 +02:00
Jérémy JAMET
5615c31e08 Update issue templates 2019-10-03 12:04:38 +02:00
J-Jamet
fb60dd5921 Fix Disable color 2019-10-02 19:27:35 +02:00
J-Jamet
ff4c1b779b First code for color preference 2019-10-02 15:38:03 +02:00
J-Jamet
53a7b99567 Fix package for preference 2019-10-01 18:00:41 +02:00
J-Jamet
a57103bafb Add default username and better setter database data implementation 2019-10-01 17:05:46 +02:00
J-Jamet
2540f32dbf Add NDK version 2019-10-01 15:52:46 +02:00
J-Jamet
499ccd6b7c #297 Duplicate UUID 2019-10-01 15:00:35 +02:00
J-Jamet
a4359560b9 Change duplicate UUID dialog message 2019-10-01 14:42:32 +02:00
J-Jamet
149483cc2d Fix duplicate UUID dialog orientation change 2019-10-01 14:33:50 +02:00
J-Jamet
a1d2022492 Fix index for duplicate UUID 2019-10-01 14:15:11 +02:00
J-Jamet
891036c35c Fix database after dialog positive click 2019-10-01 12:35:03 +02:00
J Smith
4e16ba5f56 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2019-10-01 11:56:16 +02:00
abidin toumi
7137a2fadb Translated using Weblate (Arabic)
Currently translated at 71.1% (271 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/
2019-10-01 11:56:14 +02:00
ButterflyOfFire
9d90d0eaba Translated using Weblate (Arabic)
Currently translated at 71.1% (271 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/
2019-10-01 11:56:12 +02:00
J-Jamet
94a9942db5 Show duplicate UUID message and fix cancel string 2019-10-01 11:38:46 +02:00
J-Jamet
5f347fe106 Show same UUID entries 2019-09-29 22:26:47 +02:00
J-Jamet
a34a84ae16 Fix refactoring 2019-09-26 20:57:10 +02:00
J-Jamet
40b0982298 Refactor load database 2019-09-26 20:29:33 +02:00
J-Jamet
4100258476 Remove unused import 2019-09-26 16:26:41 +02:00
J-Jamet
5f3f6661b7 Remove URI verification 2019-09-26 16:24:41 +02:00
J-Jamet
75af97e0ae Better code to open document 2019-09-26 16:04:05 +02:00
J-Jamet
58f158c457 Fix java.lang.IllegalStateException for CoordinatorLayout 2019-09-26 14:02:59 +02:00
J-Jamet
ce27eae1f0 Upgrade biometric lib 2019-09-26 13:20:00 +02:00
WaldiS
0aa0b3e993 Translated using Weblate (Polish)
Currently translated at 96.1% (366 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-09-25 20:28:03 +02:00
J-Jamet
1cc5a08236 Better delete implementation 2019-09-25 15:45:12 +02:00
J-Jamet
4c587eeb03 Remove unused open link, and expand view lib. Redo toolbar paste animation 2019-09-25 15:25:58 +02:00
J-Jamet
ab70c2d014 Add settings 2019-09-24 15:42:57 +02:00
J-Jamet
8413160ac5 Rename compression algorithm 2019-09-24 13:10:49 +02:00
J-Jamet
5abc403171 Add compression setting 2019-09-24 12:52:07 +02:00
J-Jamet
9b891013b8 Add database settings 2019-09-24 11:52:01 +02:00
J-Jamet
9413987355 Update CHANGELOG 2019-09-23 18:01:40 +02:00
J-Jamet
f95b514b41 Fix orientation change during entry edit 2019-09-23 17:40:53 +02:00
WaldiS
f6985c8944 Translated using Weblate (Polish)
Currently translated at 96.1% (366 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-09-23 15:28:06 +02:00
zeritti
4388d56c52 Translated using Weblate (Czech)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2019-09-23 15:28:05 +02:00
solokot
a70fe24c97 Translated using Weblate (Russian)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2019-09-23 15:28:02 +02:00
Avgerinos Panagiotis
8e0392753c Translated using Weblate (Greek)
Currently translated at 19.7% (75 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/el/
2019-09-23 15:28:01 +02:00
J-Jamet
9c9980bba6 Change TimeOut default to 5 minutes 2019-09-23 11:11:53 +02:00
J-Jamet
2226c15d29 Merge branch 'feature/Entry_History' into develop 2019-09-23 10:30:20 +02:00
J-Jamet
82a859bd9c Better KDF implementation 2019-09-22 20:23:59 +02:00
J-Jamet
83873fab81 Fix custom fields orientation change #337 2019-09-22 18:27:32 +02:00
J-Jamet
31f2be7b91 Hide history preference with database V3 2019-09-22 14:34:52 +02:00
J-Jamet
16458e6646 Add description for max history preferences 2019-09-22 14:12:58 +02:00
J-Jamet
2b9678707d Separate recycle bin preference 2019-09-22 13:54:26 +02:00
J-Jamet
cdbb23d7f1 Separate recycle bin preference 2019-09-22 13:52:30 +02:00
J-Jamet
23fd1b83f4 Better preference implementation 2019-09-22 13:46:10 +02:00
J-Jamet
40b0ebe49b Fix preferences 2019-09-22 13:14:47 +02:00
J-Jamet
7cd8682544 Better Preference implementation 2019-09-20 16:30:05 +02:00
J-Jamet
d0dd478ac8 Add max history preferences 2019-09-19 17:53:32 +02:00
J-Jamet
ffb547c452 Change history string 2019-09-19 15:37:07 +02:00
J-Jamet
bd829f129f Better condition to show history 2019-09-19 14:29:24 +02:00
J-Jamet
5ad3f62de5 Add history after each entry update for DatabaseV4 2019-09-19 14:20:28 +02:00
solokot
b0ec4942bc Translated using Weblate (Russian)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2019-09-19 04:51:14 +02:00
tinect
2cbc9675f6 Translated using Weblate (German)
Currently translated at 97.1% (370 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-09-19 04:51:11 +02:00
J-Jamet
116643a45a Merge branch 'develop' into feature/Entry_History 2019-09-18 22:33:06 +02:00
J-Jamet
2f0eb283ed Fix selection mode color 2019-09-18 22:30:29 +02:00
J-Jamet
6d46fccdcd Show history by click 2019-09-18 22:14:50 +02:00
J-Jamet
f5dc94bfec Add date string for PwDate and new list view entry history 2019-09-18 12:57:11 +02:00
solokot
94bdb0e3da Translated using Weblate (Russian)
Currently translated at 99.7% (380 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2019-09-17 15:41:33 +02:00
Ldm Public
65360c2a1e Translated using Weblate (French)
Currently translated at 97.6% (372 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-09-16 22:24:14 +02:00
jan madsen
70d30bdbe6 Translated using Weblate (Danish)
Currently translated at 96.6% (368 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2019-09-16 22:24:13 +02:00
Mesut Akcan
66f7e6d1b1 Translated using Weblate (Turkish)
Currently translated at 100.0% (381 of 381 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-09-16 22:24:11 +02:00
J-Jamet
a8ccb67a87 Entry history first code 2019-09-15 17:56:25 +02:00
J-Jamet
66051382f1 Upgrade version 2019-09-15 15:56:54 +02:00
J-Jamet
0fb3028c91 Fix group copy 2019-09-15 10:42:10 +02:00
Hosted Weblate
5e5baa4892 Merge branch 'origin/master' into Weblate. 2019-09-15 10:17:14 +02:00
WaldiS
9d1257ed9d Translated using Weblate (Polish)
Currently translated at 97.6% (368 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-09-15 10:17:08 +02:00
Mesut Akcan
9d7546053d Translated using Weblate (Turkish)
Currently translated at 100.0% (377 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-09-15 10:17:07 +02:00
J-Jamet
a1b692abe5 Update fastlane 2019-09-14 12:47:42 +02:00
J-Jamet
4e06842d0f Fix #331 Crash if "Save keyfile" setting is unchecked 2019-09-14 12:47:04 +02:00
J-Jamet
f04c2ee1da Merge tag '2.5.0.0beta23' into develop
2.5.0.0beta23
2019-09-14 11:31:37 +02:00
J-Jamet
9700dbcc3f Merge branch 'release/2.5.0.0beta23' 2019-09-14 11:31:30 +02:00
J-Jamet
4e344458b2 Fix warnings 2019-09-14 11:17:16 +02:00
J-Jamet
6f95cc7296 Merge branch 'translations' into develop 2019-09-14 10:39:55 +02:00
J-Jamet
cd66f8f57e Merge branch 'master' of https://hosted.weblate.org/projects/keepass-dx/strings into translations 2019-09-14 10:39:24 +02:00
J-Jamet
83f0eb9a33 Fix database preferences 2019-09-13 22:20:56 +02:00
J-Jamet
ca89bba768 Update screen image 2019-09-13 21:50:37 +02:00
J-Jamet
2d2bd5013e Upgrade Store Screens 2019-09-13 21:45:55 +02:00
J-Jamet
b93d7bbf41 Better message view for fingerprint problem #114 2019-09-13 19:49:53 +02:00
J-Jamet
000277705a Fix #316 Search stop selection mode 2019-09-13 17:04:33 +02:00
J-Jamet
088712e784 Small log change 2019-09-13 16:02:48 +02:00
J-Jamet
117592387e Change SDK version for icon packs 2019-09-13 15:30:34 +02:00
J-Jamet
76bb1a369c Fix switch to previous keyboard deprecation 2019-09-13 14:39:34 +02:00
J-Jamet
9331c281fe Fix small variables names 2019-09-13 13:44:12 +02:00
J-Jamet
f2666316e1 Better UriUtil methods 2019-09-12 17:07:42 +02:00
Kwangho Kim
7c2ff5067d Translated using Weblate (Korean)
Currently translated at 43.5% (164 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ko/
2019-09-12 16:24:01 +02:00
zeritti
5fed641c7c Translated using Weblate (Czech)
Currently translated at 99.7% (376 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2019-09-12 16:24:01 +02:00
Allan Nordhøy
18bd62ee5a Translated using Weblate (English)
Currently translated at 99.7% (376 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2019-09-12 16:23:56 +02:00
Allan Nordhøy
bc57e6e257 Translated using Weblate (German)
Currently translated at 98.7% (372 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-09-12 16:23:56 +02:00
C. Rüdinger
69b1aba218 Translated using Weblate (German)
Currently translated at 98.7% (372 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-09-12 16:23:56 +02:00
zeritti
df04d998c2 Translated using Weblate (German)
Currently translated at 98.7% (372 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-09-12 16:23:55 +02:00
Mesut Akcan
e986fe5f60 Translated using Weblate (Turkish)
Currently translated at 99.7% (376 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-09-12 16:23:54 +02:00
J-Jamet
e4ac0ee258 Fix seekbar value 2019-09-12 14:39:27 +02:00
J-Jamet
426aa0e7da Better StringUtil code 2019-09-12 14:19:14 +02:00
J-Jamet
6c0a48af48 Remove unused Tests 2019-09-12 14:15:34 +02:00
J-Jamet
d865da1613 Fix small issue 2019-09-12 13:49:14 +02:00
J-Jamet
6fa4c1e06e Fix warnings 2019-09-12 12:39:45 +02:00
J-Jamet
52a6b3e046 Upgrade Preference library 2019-09-12 12:39:27 +02:00
J-Jamet
fa26d2f938 Fix mini fab color 2019-09-12 11:52:43 +02:00
J-Jamet
4777cdc7ae Fix file_modification view 2019-09-12 11:28:12 +02:00
J-Jamet
cd8d3cbf6a Fix margins 2019-09-12 10:52:04 +02:00
J-Jamet
e6a6feb5c0 Fix margins 2019-09-12 10:46:35 +02:00
J-Jamet
98d6fb9214 Remove file_manager links 2019-09-12 10:41:31 +02:00
J-Jamet
94244cd15b Fix #242 Null AutoType 2019-09-11 18:03:03 +02:00
J-Jamet
c5e2ca9907 Fix #327 save to previous database version when conditions are required 2019-09-11 16:54:58 +02:00
Kwangho Kim
e4fef44caf Translated using Weblate (English)
Currently translated at 99.7% (376 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2019-09-11 15:59:08 +02:00
J-Jamet
5bd9da9bb1 Small change in ic_launcher 2019-09-10 16:31:37 +02:00
J-Jamet
5f0e899679 Add TODO for history 2019-09-10 16:26:17 +02:00
J-Jamet
4b1806900b Fix Broadcast Receiver don't working in Android 9 2019-09-10 16:26:02 +02:00
J-Jamet
30e2912885 Remove unused code 2019-09-10 16:19:52 +02:00
J-Jamet
fc3608ff69 Refactor startActivityForEntrySelection 2019-09-10 16:11:20 +02:00
J-Jamet
6de47ec9b2 Fix small bug 2019-09-10 16:06:43 +02:00
J-Jamet
f7253764a2 Multiline to password generator and up to 128 chars max 2019-09-10 12:53:03 +02:00
J-Jamet
c54b134c31 Masked password in generator #324 2019-09-10 12:43:29 +02:00
J-Jamet
a6bdca52be Fix Search shows Backup entries #320 2019-09-10 12:33:59 +02:00
J-Jamet
29eec05f8f Fix warning with parcelable 2019-09-09 13:35:29 +02:00
J-Jamet
250aef9738 ImageView Key Generator as Button 2019-09-09 12:30:58 +02:00
J-Jamet
50097914a2 Let Copy UUID 2019-09-09 11:59:26 +02:00
J-Jamet
ecff4fb2c5 Update CHANGELOG 2019-09-09 11:00:19 +02:00
J-Jamet
ab3d17f352 Fix OOM #256 2019-09-09 10:55:34 +02:00
J-Jamet
a75d237e53 Kotlinized MemUtil 2019-09-09 10:11:16 +02:00
Allan Nordhøy
a0c7786e1b Translated using Weblate (Norwegian Bokmål)
Currently translated at 93.6% (353 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2019-09-06 21:24:59 +02:00
J-Jamet
0d32c38c79 Fix custom fields hidden 2019-09-06 14:06:51 +02:00
J-Jamet
b846eda410 Update CHANGELOG 2019-09-06 12:44:06 +02:00
J-Jamet
7c33c9ec02 Better custom fields implementation, fix references 2019-09-06 12:39:27 +02:00
J-Jamet
74c08340a6 Fix field references #61 2019-09-06 11:10:04 +02:00
J-Jamet
82161536be Add DE translations 2019-09-05 15:42:18 +02:00
J-Jamet
752dbca356 Update CHANGELOG 2019-09-05 15:40:34 +02:00
J-Jamet
9ef56f6fd8 Register fingerprint only if database is open #322 2019-09-05 14:56:07 +02:00
J-Jamet
0239f115ae Fix show biometric view issue 2019-09-05 13:11:48 +02:00
J-Jamet
9775e09221 Fix small issue 2019-09-04 14:46:02 +02:00
J-Jamet
c975a1bfc0 Fix small issues 2019-09-04 14:33:31 +02:00
J-Jamet
c263536078 Change icon background for custom image 2019-09-04 14:02:55 +02:00
J-Jamet
b559eeaad0 Merge branch 'feature/Biometric' into develop 2019-09-04 13:17:59 +02:00
J-Jamet
63373083ab Store biometric cipher values in database 2019-09-04 13:17:33 +02:00
Swann Martinet
bf525807b0 Translated using Weblate (French)
Currently translated at 100.0% (377 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-09-04 07:25:10 +02:00
Swann Martinet
921021078b Translated using Weblate (Italian)
Currently translated at 99.2% (374 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2019-09-04 07:25:10 +02:00
J-Jamet
e5b60a8413 Small changes 2019-09-02 20:08:17 +02:00
J-Jamet
e01402f2fa Remove unused comments 2019-09-02 20:04:15 +02:00
J-Jamet
27f92e1bb5 Remove unused dependencies 2019-09-02 19:54:27 +02:00
J-Jamet
63db6de30b Links as buttons in description dialog 2019-09-02 19:42:28 +02:00
J-Jamet
7a038126cf Replace fingerprint remove icon and fix small issue 2019-09-02 19:11:51 +02:00
J-Jamet
edcfa8cf7b Better biometric key implementation 2019-09-02 18:46:55 +02:00
J-Jamet
c9594948a2 Fix small biometric issues 2019-09-02 18:30:21 +02:00
J-Jamet
66988ecb66 Better biometric view message implementation 2019-09-02 17:36:14 +02:00
J-Jamet
186ca30be8 Add preference to auto open biometric prompt 2019-09-02 14:47:03 +02:00
J-Jamet
027d581dcc Small enhancements 2019-09-02 14:04:05 +02:00
J-Jamet
adcc1c745a Fix small issues 2019-09-02 13:37:07 +02:00
J-Jamet
8682856c01 Add explanation dialog and fix store bug 2019-09-02 13:26:18 +02:00
J-Jamet
9ec976d246 Add preferences for advanced unlock 2019-09-02 12:31:56 +02:00
J Smith
9d17b49586 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (377 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2019-09-02 12:25:09 +02:00
Stephan Paternotte
698496d37c Translated using Weblate (Dutch)
Currently translated at 98.4% (371 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nl/
2019-09-02 12:25:01 +02:00
Allan Nordhøy
2af8c4f3c8 Translated using Weblate (English)
Currently translated at 99.7% (376 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2019-09-02 12:24:54 +02:00
kee
b164099b6d Translated using Weblate (German)
Currently translated at 95.0% (358 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/
2019-09-02 12:24:53 +02:00
Mesut Akcan
c19357605f Translated using Weblate (Turkish)
Currently translated at 100.0% (377 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-09-02 12:24:51 +02:00
J-Jamet
5a08fa0088 New state mode for biometric unlock 2019-09-02 12:01:36 +02:00
J-Jamet
bff87d16b1 Start biometric feature 2019-09-01 17:54:01 +02:00
J-Jamet
5b0afa447c Text size by percent 2019-09-01 17:45:39 +02:00
J-Jamet
5a882a954f Text size by percent 2019-09-01 17:44:36 +02:00
J-Jamet
47f340d576 Fix null inheritance 2019-09-01 15:49:49 +02:00
WaldiS
41df139c17 Translated using Weblate (Polish)
Currently translated at 96.6% (364 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-08-31 11:25:06 +02:00
underlineGalaxy
469c267161 Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.4% (371 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_BR/
2019-08-31 11:25:05 +02:00
Mesut Akcan
f336d4fe58 Translated using Weblate (Turkish)
Currently translated at 100.0% (377 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-08-31 11:25:05 +02:00
J-Jamet
e1d997cc91 Merge branch 'develop' of github.com:Kunzisoft/KeePassDX into develop 2019-08-30 13:18:36 +02:00
J-Jamet
53cf4bba1b Merge branch 'feature/AndroidX' into develop 2019-08-30 10:08:40 +02:00
J-Jamet
030417dbe1 Better education methods 2019-08-30 10:07:49 +02:00
J-Jamet
59abcb115c Add .cxx to gitignore 2019-08-29 17:36:39 +02:00
J-Jamet
598dbd3794 Migrate to AndroidX and remove unused libs 2019-08-29 17:13:14 +02:00
J-Jamet
2c4a7e5576 Migrate to API 28 2019-08-29 13:58:59 +02:00
J-Jamet
02429d5790 Change lock back default value to false 2019-08-29 13:36:55 +02:00
J-Jamet
d990cb24ea Fix #314 magikeyboard notification 2019-08-29 13:35:30 +02:00
J-Jamet
e549b16dce Update CHANGELOG 2019-08-29 13:29:24 +02:00
J-Jamet
e11864a64f Remove long press in history list 2019-08-29 13:23:55 +02:00
J-Jamet
a3e74f8ee5 Fix text size in list 2019-08-29 13:15:59 +02:00
J-Jamet
36cb683404 Better file history listener implementation 2019-08-29 11:13:29 +02:00
ssantos
f7f0028033 Translated using Weblate (Portuguese (Portugal))
Currently translated at 100.0% (377 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_PT/
2019-08-27 21:24:47 +02:00
WaldiS
f06088fa12 Translated using Weblate (Polish)
Currently translated at 96.0% (362 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2019-08-27 21:24:29 +02:00
J-Jamet
b62ef8a2ed Update database alias 2019-08-27 13:13:31 +02:00
J-Jamet
622ba65841 Prevent information to be closed 2019-08-26 15:07:22 +02:00
J-Jamet
3a48d20d12 Change list history views 2019-08-26 14:53:23 +02:00
J-Jamet
8d6db78f55 Add file database history info as sub part of recyclerview element 2019-08-26 13:32:33 +02:00
J-Jamet
f3ba6e800a Rename OpenFileHelper 2019-08-26 10:35:18 +02:00
J-Jamet
1c4aaf9807 Encapsulate URI methods 2019-08-26 09:46:17 +02:00
J-Jamet
c65ed41efd Move error in init uri method 2019-08-26 08:03:16 +02:00
J-Jamet
1965336077 Remove WeakReference to retrieve FileDatabaseHistory 2019-08-25 12:51:32 +02:00
J-Jamet
9b7095ad4c Change App database package and name 2019-08-25 12:28:13 +02:00
J-Jamet
33767c2bf9 Add room and fully manage database file history by SQL 2019-08-25 12:04:10 +02:00
Kunzisoft
82f7e861e7 Translated using Weblate (French)
Currently translated at 100.0% (377 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-08-23 23:24:20 +02:00
J-Jamet
846b5fa449 Add octet-stream as mimetype 2019-08-22 14:08:53 +02:00
J-Jamet
baf4e676eb Updates intent-filter to recognize more .kdbx files 2019-08-22 13:59:16 +02:00
J-Jamet
a9bf3e83c4 Fix activity_password layout 2019-08-22 13:14:55 +02:00
J-Jamet
710a1b0996 Better password generation errors 2019-08-22 12:11:15 +02:00
J-Jamet
c4671b84a0 Better error management for AssignMasterKey 2019-08-22 11:53:33 +02:00
J-Jamet
c0c98d0299 Remove unused Toast and strings 2019-08-21 18:18:56 +02:00
J-Jamet
ffdec77d11 Better error and url management 2019-08-21 17:56:49 +02:00
Ldm Public
868bbe2e70 Translated using Weblate (French)
Currently translated at 96.6% (364 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-08-21 16:24:18 +02:00
jan madsen
4aac655a5d Translated using Weblate (Danish)
Currently translated at 95.5% (360 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2019-08-21 16:24:17 +02:00
Wilker Santana da Silva
510244aa70 Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.1% (370 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_BR/
2019-08-21 16:24:16 +02:00
Mesut Akcan
50840b04b4 Translated using Weblate (Turkish)
Currently translated at 96.3% (363 of 377 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2019-08-21 16:24:14 +02:00
J-Jamet
4d5962f5ca Merge branch 'feature/Intent_Create_File' into develop 2019-08-21 14:04:57 +02:00
J-Jamet
c9456c771c Fix fist time clipboard warning and better views implementation 2019-08-21 14:04:34 +02:00
J-Jamet
41a7a583d4 Upgrade CHANGELOG 2019-08-21 12:33:01 +02:00
J-Jamet
2e5220fa8a Better file manager installation description 2019-08-21 12:23:44 +02:00
J-Jamet
b75475d785 Fix master key creation during orientation change 2019-08-21 11:50:06 +02:00
J-Jamet
362ef06bb5 Merge branch 'develop' of github.com:Kunzisoft/KeePassDX into develop 2019-08-20 21:41:44 +02:00
J-Jamet
4a80c9f9f9 Remove unused permission dispatcher 2019-08-20 14:07:10 +02:00
J-Jamet
f1fdb9fc84 Remove SAF setting and fix education when create button is gone 2019-08-20 13:55:57 +02:00
J-Jamet
5bb9168c29 Simplify database file creation 2019-08-20 13:12:44 +02:00
J-Jamet
0245dcd8e8 Merge branch 'develop' into feature/Intent_Create_File 2019-08-20 10:33:47 +02:00
J-Jamet
b31bfa1d4f Upgrade version to 2.5.0.0beta23 2019-08-20 10:33:02 +02:00
J-Jamet
0778f22b68 Create database with additional button 2019-08-19 20:01:05 +02:00
J-Jamet
4808696398 Remove unused create file verification 2019-08-19 19:28:08 +02:00
J-Jamet
0ea7b5b25f Merge branch 'develop' into feature/Intent_Create_File 2019-08-19 19:10:13 +02:00
Hosted Weblate
995785de9f Merge branch 'origin/master' into Weblate. 2019-08-19 18:01:01 +02:00
Marco Scaglioni
5deef427c0 Translated using Weblate (Italian)
Currently translated at 100.0% (356 of 356 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2019-08-19 18:01:01 +02:00
J-Jamet
024c6631d8 Merge tag '2.5.0.0beta22' into develop
2.5.0.0beta22
2019-08-19 17:58:00 +02:00
J-Jamet
78354e4736 Merge branch 'release/2.5.0.0beta22' 2019-08-19 17:57:42 +02:00
J-Jamet
b9a792e6bd Upgrade default fastfile 2019-08-19 17:48:56 +02:00
J-Jamet
c533d21250 Fix small view warning 2019-08-19 17:30:56 +02:00
J-Jamet
74572c8102 Upgrade version and CHANGELOGS 2019-08-19 17:13:28 +02:00
J-Jamet
b8de64fab0 New icon background 2019-08-19 17:06:38 +02:00
J-Jamet
877b909205 Merge branch 'translations' into develop 2019-08-19 16:43:51 +02:00
J-Jamet
1b1dcc0f45 Merge branch 'master' of https://hosted.weblate.org/projects/keepass-dx/strings into translations 2019-08-19 16:43:23 +02:00
J-Jamet
94e5988794 Fix remove entry for databaseV1 2019-08-19 15:17:00 +02:00
J-Jamet
b22333fda5 Show empty title in entry content view if title is empty 2019-08-19 15:00:33 +02:00
J-Jamet
400c6bef78 Disable recycle bin at bottom option if natural order 2019-08-19 14:44:56 +02:00
J-Jamet
edf6c2ff07 Fix remove action for natural order 2019-08-19 14:16:29 +02:00
J-Jamet
5eec1a276c Fix recyclebin update 2019-08-19 01:42:23 +02:00
J-Jamet
f707fd7649 Fix move and copy entry in database V1 root 2019-08-19 01:03:09 +02:00
J-Jamet
75b028daf3 Fix actions duplicate entries 2019-08-19 00:07:14 +02:00
J-Jamet
c6f259d18f Fix move action 2019-08-18 22:57:44 +02:00
J-Jamet
954d522341 Add UUID in Entry View 2019-08-18 22:32:33 +02:00
J-Jamet
1f8d17d27e Merge tag '2.5.0.0beta21' into develop
2.5.0.0beta21
2019-08-18 19:18:36 +02:00
J-Jamet
b737501d4d Start change database file creation 2019-08-16 11:24:00 +02:00
Allan Nordhøy
c303ffafb5 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.3% (343 of 356 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2019-08-16 01:23:47 +02:00
Kunzisoft
6f513b4920 Translated using Weblate (French)
Currently translated at 99.4% (354 of 356 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2019-08-16 01:23:46 +02:00
Wilker Santana da Silva
a83c60583f Translated using Weblate (English)
Currently translated at 99.7% (355 of 356 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2019-08-16 01:23:45 +02:00
Kunzisoft
cde8950257 Translated using Weblate (English)
Currently translated at 99.7% (355 of 356 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/
2019-08-16 01:23:45 +02:00
jan madsen
0b4dd1e909 Translated using Weblate (Danish)
Currently translated at 96.6% (344 of 356 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2019-08-16 01:23:45 +02:00
Wilker Santana da Silva
28e2600271 Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.4% (354 of 356 strings)

Translation: KeePass DX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_BR/
2019-08-16 01:23:44 +02:00
J-Jamet
53cc4f74c8 Add logo for Google Play Store guidelines 2019-08-15 13:28:02 +02:00
somkun
9558fcaf21 Add Read support for TOTP Tokens 2018-09-16 22:11:23 -07:00
727 changed files with 37599 additions and 25141 deletions

41
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,41 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
** Keepass Database **
- Created with: [e.g Windows KeePass 2.42]
- Version: [e.g. 2]
- Location: [e.g. Remote file retrieved with GDrive app]
- Size: [e.g. 150Mo]
- Contains attachment: [e.g. Yes]
**KeePassDX (please complete the following information):**
- Version: [e.g. 2.5.0.0beta23]
- Build: [e.g. Free]
- Language: [e.g. French]
**Android (please complete the following information):**
- Device: [e.g. GalaxyS8]
- Version: [e.g. 8.1]
**Additional context**
Add any other context about the problem here.
- Browser for Autofill: [e.g. Chrome version X]

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

7
.gitignore vendored
View File

@@ -38,6 +38,13 @@ proguard/
# Android Studio captures folder # Android Studio captures folder
captures/ captures/
# Eclipse/VS Code
.project
.settings/*
*/.project
*/.classpath
*/.settings/*
# Intellij # Intellij
*.iml *.iml
.idea/workspace.xml .idea/workspace.xml

148
CHANGELOG
View File

@@ -1,4 +1,102 @@
KeepassDX (2.5.0.0beta21) KeePassDX(2.5)
* First stable version of KeePassDX
* Fork completely rewritten from the KeePassDroid project
* Fix small issues from the last Release Candidate
KeePassDX(2.5RC2)
* Replacement of Spongy Castle by Bouncy Castle
* Update Autofill compatibility
* Fix Magikeyboard "Go" action
* Fix KeeWeb database opening
* Fix default username
* Fix themes
* Fix small issues
KeePassDX(2.5RC1)
* Add write permission to keep compatibility with old file managers
* Fix autofill for apps
* Auto search for autofill
* New keyfile input
* Icon to hide keyfile input
* New lock button
* Setting to hide lock button in user interface
* Clickable links in notes
* Fix autofill for key-value pairs
KeePassDX(2.5beta30)
* Fix Lock after screen off (wait 1.5 seconds)
* Upgrade autofill algorithm
* Fix ANR during file verifications
KeePassDX(2.5beta29)
* Upgrade autofill algorithm
* Delete registered KeyFile after save new credentials
* Fix title and username entry view refresh after an update
* Fix database lock request (open notification always active)
* Allow empty title in entries
* Add expiration datetime
KeePassDX(2.5beta28)
* Fix read only database
* Upgrade to Android SDK 29
KeePassDX (2.5beta27)
* New setting to hide broken links
* Show URL when title is empty
* Setting to open search field at database opening
* Fix settings for database locations
* Fix error message when database file not writable
* Fix appearance refresh settings
* Sort optimization
KeePassDX (2.5.0.0beta26)
* Download attachments
* Change file size string format
* Prevent screenshot for all screen
* Auto performed "Go" key in Magikeyboard
* Restore and delete entry history
* Setting to hide expired entries
* New Black theme
* Fix crash when clearing clipboard
* Fix attachments compressions
* Fix dates
* Fix UUID message for Database v1
KeePassDX (2.5.0.0beta25)
* Setting for Recycle Bin
* Fix Recycle bin issues
* Fix TOTP
* Fix infinite save
* Fix update group
* Fix OOM
KeePassDX (2.5.0.0beta24)
* Add OTP (HOTP / TOTP)
* Add settings (Color, Security, Master Key)
* Show history of each entry
* Auto repair database for nodes with same UUID
* Management of expired nodes
* Multi-selection for actions (Cut - Copy - Delete)
* Open/Save database as service / Add persistent notification
* Fix settings / edit group / small bugs
KeePassDX (2.5.0.0beta23)
* New, more secure database creation workflow
* Recognize more database files
* Add alias for history files (WARNING: history is erased)
* New Biometric unlock (Fingerprint with new API)
* Fix entry references
* Fix OOM with KeyFile
* Fix small issues
KeePassDX (2.5.0.0beta22)
* Rebuild code for actions
* Add UUID as entry view
* Fix bug with natural order
* Fix number of entries in databaseV1
* New entry views
KeePassDX (2.5.0.0beta21)
* Fix nested groups no longer visible in V1 databases * Fix nested groups no longer visible in V1 databases
* Improved data import algorithm for V1 databases * Improved data import algorithm for V1 databases
* Add natural database sort * Add natural database sort
@@ -6,10 +104,10 @@ KeepassDX (2.5.0.0beta21)
* Fix button disabled with only KeyFile * Fix button disabled with only KeyFile
* Show the number of entries in a group * Show the number of entries in a group
KeepassDX (2.5.0.0beta20) KeePassDX (2.5.0.0beta20)
* Fix a major bug that displays an entry history * Fix a major bug that displays an entry history
KeepassDX (2.5.0.0beta19) KeePassDX (2.5.0.0beta19)
* Add lock button always visible * Add lock button always visible
* New connection workflow * New connection workflow
* Code refactored in Kotlin * Code refactored in Kotlin
@@ -20,7 +118,7 @@ KeepassDX (2.5.0.0beta19)
* Fix memory when load database * Fix memory when load database
* Fix small bugs * Fix small bugs
KeepassDX (2.5.0.0beta18) KeePassDX (2.5.0.0beta18)
* New recent databases views * New recent databases views
* New information dialog * New information dialog
* Custom fields for the Magikeyboard * Custom fields for the Magikeyboard
@@ -29,10 +127,10 @@ KeepassDX (2.5.0.0beta18)
* Fix memory when opening the database * Fix memory when opening the database
* Memory management for attachments * Memory management for attachments
KeepassDX (2.5.0.0beta17) KeePassDX (2.5.0.0beta17)
* Fix font and search * Fix font and search
KeepassDX (2.5.0.0beta16) KeePassDX (2.5.0.0beta16)
* New search in a single fragment * New search in a single fragment
* Search suggestions * Search suggestions
* Added the display of usernames * Added the display of usernames
@@ -40,20 +138,20 @@ KeepassDX (2.5.0.0beta16)
* Fix read-only mode * Fix read-only mode
* Fix parcelable / toolbar / back * Fix parcelable / toolbar / back
KeepassDX (2.5.0.0beta15) KeePassDX (2.5.0.0beta15)
* Read only mode * Read only mode
* Best group recovery for the navigation fragment * Best group recovery for the navigation fragment
* Fix copies in notifications * Fix copies in notifications
* Fix orientation * Fix orientation
* Added translations * Added translations
KeepassDX (2.5.0.0beta14) KeePassDX (2.5.0.0beta14)
* Optimize all the memory with parcelables / fix search * Optimize all the memory with parcelables / fix search
KeepassDX (2.5.0.0beta13) KeePassDX (2.5.0.0beta13)
* Fix memory issue with parcelable (crash in beta12 version) * Fix memory issue with parcelable (crash in beta12 version)
KeepassDX (2.5.0.0beta12) KeePassDX (2.5.0.0beta12)
* Added the Magikeyboard to fill the forms (settings still in development) * Added the Magikeyboard to fill the forms (settings still in development)
* Added move and copy for groups and entries * Added move and copy for groups and entries
* New navigation in a single screen / new animations between activities * New navigation in a single screen / new animations between activities
@@ -66,10 +164,10 @@ KeepassDX (2.5.0.0beta12)
* Fix the fingerprint recognition (WARNING : The keystore is reinit, you must delete the old keys) * Fix the fingerprint recognition (WARNING : The keystore is reinit, you must delete the old keys)
* Fix small bugs * Fix small bugs
KeepassDX (2.5.0.0beta11) KeePassDX (2.5.0.0beta11)
* Fix crash in beta10 version * Fix crash in beta10 version
KeepassDX (2.5.0.0beta10) KeePassDX (2.5.0.0beta10)
* Dynamically change Algorithm and Key Derivation Function in settings * Dynamically change Algorithm and Key Derivation Function in settings
* Upgrade translations * Upgrade translations
* New red volcano theme, fix classic dark theme * New red volcano theme, fix classic dark theme
@@ -77,7 +175,7 @@ KeepassDX (2.5.0.0beta10)
* Update fingerprint state with checkbox * Update fingerprint state with checkbox
* Fix bugs * Fix bugs
KeepassDX (2.5.0.0beta9) KeePassDX (2.5.0.0beta9)
* Education Screens to learn how to use the app * Education Screens to learn how to use the app
* New designs * New designs
* New custom font for character visibility * New custom font for character visibility
@@ -86,9 +184,9 @@ KeepassDX (2.5.0.0beta9)
* Change setting organisation * Change setting organisation
* Pro version * Pro version
KeepassDX (2.5.0.0beta8) KeePassDX (2.5.0.0beta8)
* Hide custom entries protected * Hide custom entries protected
* Best management of field references (https://keepass.info/help/base/fieldrefs.html) * Best management of field references (https://KeePass.info/help/base/fieldrefs.html)
* Change database / default settings * Change database / default settings
* Add Autofill for search * Add Autofill for search
* Add sorting by last access and by creation time * Add sorting by last access and by creation time
@@ -96,7 +194,7 @@ KeepassDX (2.5.0.0beta8)
* Refactor old code * Refactor old code
* Fix bugs * Fix bugs
KeepassDX (2.5.0.0beta7) KeePassDX (2.5.0.0beta7)
* Rebuild Notifications * Rebuild Notifications
* Change links to https * Change links to https
* Add extended Ascii (ñæËÌÂÝÜ...) * Add extended Ascii (ñæËÌÂÝÜ...)
@@ -105,10 +203,10 @@ KeepassDX (2.5.0.0beta7)
* Add setting to prevent the password copy * Add setting to prevent the password copy
* Fix bugs * Fix bugs
KeepassDX (2.5.0.0beta6) KeePassDX (2.5.0.0beta6)
* Fix crash * Fix crash
KeepassDX (2.5.0.0beta5) KeePassDX (2.5.0.0beta5)
* Autofill (Android O) * Autofill (Android O)
* Deletion for group * Deletion for group
* New sorts with (Asc/Dsc, Groups before or after) * New sorts with (Asc/Dsc, Groups before or after)
@@ -129,7 +227,7 @@ KeepassDX (2.5.0.0beta5)
* Fix many small bugs * Fix many small bugs
* Add recycle bin setting (not yet accessible) * Add recycle bin setting (not yet accessible)
KeepassDX (2.5.0.0beta4) KeePassDX (2.5.0.0beta4)
* Show only file name * Show only file name
* Setting for full path * Setting for full path
* Add information for each database file * Add information for each database file
@@ -138,7 +236,7 @@ KeepassDX (2.5.0.0beta4)
* Delete view assignment for fingerprint opening * Delete view assignment for fingerprint opening
* Merge KeePassDroid 2.2.1 * Merge KeePassDroid 2.2.1
KeepassDX (2.5.0.0beta3) KeePassDX (2.5.0.0beta3)
* New database workflow with new screens and folder selection * New database workflow with new screens and folder selection
* Settings for default password generation * Settings for default password generation
* Fingerprint dialog for explanations * Fingerprint dialog for explanations
@@ -149,17 +247,17 @@ KeepassDX (2.5.0.0beta3)
* Merge KeePassDroid 2.2.0.9 * Merge KeePassDroid 2.2.0.9
* Add corruption fix mode * Add corruption fix mode
KeepassDX (2.5.0.0beta2) KeePassDX (2.5.0.0beta2)
* Remove libs for F-Droid * Remove libs for F-Droid
KeepassDX (2.5.0.0beta1) KeePassDX (2.5.0.0beta1)
* Fork KeepassDroid * Fork KeePassDroid
* Add Material Design * Add Material Design
* Add Light and Night theme * Add Light and Night theme
* Min API is 14 * Min API is 14
* Solve bug for fingerprint * Solve bug for fingerprint
* Update French translation * Update French translation
* Change donation (see KeepassDroid to contribute on both projects) * Change donation (see KeePassDroid to contribute on both projects)
KeePassDroid (2.2.1) KeePassDroid (2.2.1)
* Fix kdbx4 date corruption * Fix kdbx4 date corruption
@@ -420,7 +518,7 @@ KeePassDroid (1.9.10)
KeePassDroid (1.9.9) KeePassDroid (1.9.9)
* Go back to explicitly storing blank fields in the database * Go back to explicitly storing blank fields in the database
(works around bug in keepassx) (works around bug in KeePassx)
* Add support for native code on MIPS architectures * Add support for native code on MIPS architectures
* Adding Vibrate permission. On some devices notifications fail * Adding Vibrate permission. On some devices notifications fail
without the vibrate permission. without the vibrate permission.

View File

@@ -1,45 +0,0 @@
Original author:
Brian Pellin
Achim Weimert
Johan Berts - search patches
Mike Mohr - Better native code for aes and sha
Tobias Selig - icon support
Tolga Onbay, Dirk Bergstrom - password generator
Space Cowboy - holo theme
josefwells
Nicholas FitzRoy-Dale - auto launch intents
yulin2 - responsiveness improvements
Tadashi Saito
vhschlenker
bumper314 - Samsung multiwindow support
Hans Cappelle - fingerprint sensor integration
Jeremy Jamet - Keepass DX Material Design - Patches
Translations:
Diego Pierotto - Italian
Laurent, Norman Obry, Nam, Bruno Parmentier, Credomo - French
Maciej Bieniek, cod3r - Polish
Максим Сёмочкин, i.nedoboy, filimonic, bboa - Russian
MaWi, rvs2008, meviox, MaDill, EdlerProgrammierer, Jan Thomas - German
yslandro - Norwegian Nynorsk
王科峰 - Chinese
Typhoon - Slovak
Masahiro Inamura - Japanese
Matsuu Takuto - Japanese
Carlos Schlyter - Portugese (Brazil)
YSmhXQDd6Z - Portugese (Portugal)
andriykopanytsia - Ukranian
intel, Zoltán Antal - Hungarian
H Vanek - Czech
jipanos - Spanish
Erik Fdevriendt, Erik Jan Meijer - Dutch
Frederik Svarre - Danish
Oriol Garrote - Catalan
Mika Takala - Finnish
Niclas Burgren - Swedish
Raimonds - Latvian
dgarciabad - Basque
Arthur Zamarin - Hebrew
RaptorTFX - Greek
zygimantus - Lithuanian

View File

@@ -1,6 +1,6 @@
--- ---
KeePass DX is free software: you can redistribute it and/or modify KeePassDX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
@@ -13,7 +13,7 @@ KeePass DX is free software: you can redistribute it and/or modify
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
The KeePass DX icon was created by Jeremy JAMET and is licensed under the terms of GPLv3. The KeePassDX icon was created by Jeremy JAMET and is licensed under the terms of GPLv3.
--- ---

View File

@@ -0,0 +1,177 @@
Terms of Service
This is the terms of service for the Android Backup Service.
1. Your relationship with Google
1.1 Your use of the Android Backup Service (referred to as the "Service" in this document) is subject to the terms of a legal agreement between you and Google. "Google" means Google LLC, whose principal place of business is at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States. This document explains how the agreement is made up, and sets out some of the terms of that agreement.
1.2 Unless otherwise agreed in writing with Google, your agreement with Google will always include, at a minimum, the terms and conditions set out in this document. These are referred to below as the "Terms".
1.3 The Terms form a legally binding agreement between you and Google in relation to your use of the Service. It is important that you take the time to read them carefully.
2. Accepting the Terms
2.1 In order to use the Service, you must first agree to the Terms. You may not use the Service if you do not accept the Terms.
2.2 You can accept the Terms by clicking to accept or agree to the Terms, where this option is made available to you by Google.
2.3 You may not use the Service and may not accept the Terms if you are not of legal age to form a binding contract with Google.
2.4 You represent that you have full power, capacity and authority to accept these Terms. If you are accepting on behalf of your employer or another entity, you represent that you have full legal authority to bind your employer or such entity to these Terms. If you don't have the legal authority to bind, please ensure that an authorized person from your entity consents to and accepts these Terms.
3. Provision of the Service by Google
3.1 Google has subsidiaries and affiliated legal entities around the world ("Subsidiaries and Affiliates"). Sometimes, these companies will be providing the Service to you on behalf of Google itself. You acknowledge and agree that Subsidiaries and Affiliates will be entitled to provide the Service to you.
3.2 Google is constantly innovating in order to provide the best possible experience for its users. You acknowledge and agree that the form and nature of the Service which Google provides may change from time to time without prior notice to you.
3.3 As part of this continuing innovation, you acknowledge and agree that Google may stop (permanently or temporarily) providing the Service (or any features within the Service) to you or to users generally at Google's sole discretion, without prior notice to you. You may stop using the Service at any time. You do not need to specifically inform Google when you stop using the Service.
3.4 You acknowledge and agree that if Google disables your Backup Service Key, you and the Android application(s) you developed ("Application(s)") may be prevented from accessing the Service and any content that is stored with the Service.
3.5 You acknowledge and agree that Google may set a fixed upper limit on the number of backup transmissions you may send or receive through the Service or on the amount of storage space used for the provision of the Service at any time, at Google's discretion. You agree to abide by any such fixed upper limits.
4. Use of the Service by you
4.1 In order to access the Service, you must have a unique application identifier ("Package Name") for your Application as described in the documentation for the Service.
4.2 After supplying Google with the Package Name and accepting the Terms, you will be issued an alphanumeric key ("Backup Service Key") assigned to you by Google that is uniquely associated with your Application. Your Application must include this Backup Service Key as described in the documentation for the Service.
4.3 There is currently no limit to the number of Backup Service Keys you may obtain in this manner provided that you use a different Package Name for each Backup Service Key you obtain. You agree that each Backup Service Key is only valid for Applications with the corresponding Package Name. You agree that Google may, in its sole discretion, impose a limit on the number of Backup Service Keys that may be obtained in the future. You agree that your continued use of any of the Backup Service Keys assigned by Google, or distribution of any Applications using such Backup Service Keys, constitutes your continued agreement to these Terms.
4.4 You agree to use the Service only for purposes that are permitted by (a) the Terms and (b) any applicable law, regulation, third-party terms of service, or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries).
4.5 You agree not to access (or attempt to access) any of the Service by any means other than through the interfaces, methods, and APIs that are provided by Google, unless you have been specifically allowed to do so in a separate agreement with Google.
4.6 You agree that you will not engage in any activity that interferes with or disrupts the Service (or the servers and networks which are connected to the Service), or the servers or networks of any third-party.
4.7 You agree that your use of the Service will be in compliance with any documentation guidelines provided by Google and that failure to comply with the documentation guidelines may result in the disabling of the Backup Service Key(s) for your Application(s).
4.8 Unless you have been specifically permitted to do so in a separate agreement with Google, you agree that you will not reproduce, duplicate, copy, sell, trade or resell (a) use of the Service, or (b) access to the Service.
4.9 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) your and your Application's use of the Service, any breach of your obligations under the Terms, and for the consequences (including any loss or damage which Google may suffer) of any such breach.
4.10 You agree that in your use of the Service, you and your Applications will protect the privacy and legal rights of users. You must provide legally adequate privacy notice and protection for users whose data your Applications back up to the Service. Further, your Application may only use that information for the limited purpose of backing up the data to the Service unless the user has given you permission for further use. If the user has not given you permission to back up information to the Service, you may not transmit such information to the Service.
4.11 You agree that you and your Applications will not transmit or store sensitive user information, such as user names, passwords, or credit card numbers, through the Service.
5. Security
5.1 You agree and understand that you are responsible for maintaining the security associated with any information you provide to access the Service as well as of the Backup Service Key(s) assigned to you by Google. You agree that only you are authorized to use the Backup Service Key(s) assigned to you.
5.2 Accordingly, you agree that you will be solely responsible to Google for all activities that occur in connection with your access to the Service, as well as the Backup Service Key.
5.3 If you become aware of any unauthorized use of your Backup Service Key(s) you agree to notify Google immediately.
6. Privacy and your personal information
6.1 For information about Google's data protection practices, please read Google's privacy policy at http://www.google.com/privacy.html. This policy explains how Google treats your personal information when you use the Service.
6.2 You agree to the use of your data in accordance with Google's privacy policies.
7. Content in the Service
7.1 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any Content that you or your Applications transmit or store through the Service and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so. You agree that you are solely responsible for (A) any Content that is transmitted through the Service by your Applications, and (B) any Content that Devices retrieve from the Service by virtue of your Applications. For purposes of the Terms, "Content" means information such as data, messages, settings information, written text, computer software, music, audio files or other sounds, photographs, videos or other images. "Device(s)" means device(s) powered by the Android operating system.
7.2 You agree that you will not transmit any Content through the Service that is copyrighted, protected by trade secret or otherwise subject to third party proprietary rights, including patent, privacy and publicity rights, unless you are the owner of such rights or have permission from their rightful owner to transmit the Content through the Service.
8. Proprietary rights
8.1 You acknowledge and agree that Google (or Google's licensors) own all legal right, title and interest in and to the Service, including any intellectual property rights which subsist in the Service (whether those rights happen to be registered or not, and wherever in the world those rights may exist).
8.2 Unless you have agreed otherwise in writing with Google, nothing in the Terms gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, and other distinctive brand features.
8.3 If you have been given an explicit right to use any of these brand features in a separate written agreement with Google, then you agree that your use of such features shall be in compliance with that agreement, any applicable provisions of the Terms, and Google's brand feature use guidelines as updated from time to time. These guidelines can be viewed online at http://www.google.com/permissions/ guidelines.html (or such other URL as Google may provide for this purpose from time to time).
8.4 You agree that you shall not remove, obscure, or alter any proprietary rights notices (including copyright, trade mark notices) which may be affixed to or contained within the Service.
9. License from Google
9.1 Subject to terms and conditions of these Terms, Google gives you a personal, worldwide, royalty-free, non-assignable and non-exclusive license to use the Service as provided to you by Google. This license is for the sole purpose of enabling you to use and enjoy the benefit of the Service as provided by Google, in the manner permitted by the Terms.
9.2 You may not (and you may not permit anyone else to) copy, modify, create a derivative work of, reverse engineer, decompile or otherwise attempt to extract the source code from the Service or any part thereof, unless this is expressly permitted or required by law, or unless you have been specifically told that you may do so by Google, in writing.
9.3 Unless Google has given you specific written permission to do so, you may not assign (or grant a sub-license of) your rights to use the Service, grant a security interest in or over your rights to use the Service, or otherwise transfer any part of your rights to use the Service.
10. Your code
10.1 Google claims no ownership or control over any source code written by you to be used with the Service. You retain copyright and any other rights you already hold in this code, and you are responsible for protecting those rights, as appropriate.
11. Ending your relationship with Google
11.1 The Terms will continue to apply until terminated by either you or Google as set out below.
11.2 You may terminate your legal agreement with Google by discontinuing your use of the Service at any time.
11.3 Google may, at any time, terminate its legal agreement with you if:
(A) you have breached any provision of the Terms (or have acted in manner which clearly shows that you do not intend to, or are unable to comply with the provisions of the Terms); or
(B) Google is required to do so by law (for example, where the provision of the Service to you is, or becomes, unlawful); or
(C) Google is transitioning to no longer providing the Service; or
(D) your Application fails to meet the documentation guidelines provided by Google.
11.4 Nothing in this Section shall affect Google's rights regarding provision of the Service under Section 3 of the Terms.
11.5 When these Terms come to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst the Terms have been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of Sections 12, 13 and Paragraph 16 shall continue to apply to such rights, obligations and liabilities indefinitely.
12. EXCLUSION OF WARRANTIES
12.1 NOTHING IN THESE TERMS, INCLUDING SECTIONS 12 AND 13, SHALL EXCLUDE OR LIMIT GOOGLE'S WARRANTY OR LIABILITY FOR LOSSES WHICH MAY NOT BE LAWFULLY EXCLUDED OR LIMITED BY APPLICABLE LAW. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR CONDITIONS OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR LOSS OR DAMAGE CAUSED BY NEGLIGENCE, BREACH OF CONTRACT OR BREACH OF IMPLIED TERMS, OR INCIDENTAL OR CONSEQUENTIAL DAMAGES. ACCORDINGLY, ONLY THE LIMITATIONS WHICH ARE LAWFUL IN YOUR JURISDICTION WILL APPLY TO YOU AND OUR LIABILITY WILL BE LIMITED TO THE MAXIMUM EXTENT PERMITTED BY LAW.
12.2 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SERVICE IS AT YOUR SOLE RISK AND THAT THE SERVICE AND CONTENT ARE PROVIDED "AS IS" AND "AS AVAILABLE".
12.3 IN PARTICULAR, GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS DO NOT REPRESENT OR WARRANT TO YOU THAT:
(A) YOUR USE OF THE SERVICE WILL MEET YOUR REQUIREMENTS,
(B) YOUR USE OF THE SERVICE WILL BE UNINTERRUPTED, TIMELY, SECURE OR FREE FROM ERROR, AND
(C) THAT DEFECTS IN THE OPERATION OR FUNCTIONALITY OF ANY SOFTWARE PROVIDED TO YOU AS PART OF THE SERVICE WILL BE CORRECTED.
12.4 NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED BY YOU FROM GOOGLE OR THROUGH OR FROM THE SERVICE SHALL CREATE ANY WARRANTY NOT EXPRESSLY STATED IN THE TERMS.
12.5 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
13. LIMITATION OF LIABILITY
13.1 SUBJECT TO OVERALL PROVISION IN PARAGRAPH 12.1 ABOVE, YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU FOR:
(A) ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL CONSEQUENTIAL OR EXEMPLARY DAMAGES WHICH MAY BE INCURRED BY YOU, HOWEVER CAUSED AND UNDER ANY THEORY OF LIABILITY. THIS SHALL INCLUDE, BUT NOT BE LIMITED TO, ANY LOSS OF PROFIT (WHETHER INCURRED DIRECTLY OR INDIRECTLY), ANY LOSS OF GOODWILL OR BUSINESS REPUTATION, ANY LOSS OF DATA SUFFERED, COST OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICE, OR OTHER INTANGIBLE LOSS;
(B) ANY LOSS OR DAMAGE WHICH MAY BE INCURRED BY YOU, INCLUDING BUT NOT LIMITED TO LOSS OR DAMAGE AS A RESULT OF:
(I) ANY CHANGES WHICH GOOGLE MAY MAKE TO THE SERVICE, OR FOR ANY PERMANENT OR TEMPORARY CESSATION IN THE PROVISION OF THE SERVICE (OR ANY FEATURES WITHIN THE SERVICE);
(II) THE DELETION OF, CORRUPTION OF, OR FAILURE TO STORE, ANY CONTENT AND OTHER COMMUNICATIONS DATA MAINTAINED OR TRANSMITTED BY OR THROUGH YOUR USE OF THE SERVICE;
(III) YOUR FAILURE TO PROVIDE GOOGLE WITH ACCURATE ACCOUNT INFORMATION; OR
(IV) YOUR FAILURE TO KEEP YOUR PASSWORD OR ACCOUNT DETAILS SECURE AND CONFIDENTIAL.
13.2 THE LIMITATIONS ON GOOGLE'S LIABILITY TO YOU IN PARAGRAPH 13.1 ABOVE SHALL APPLY WHETHER OR NOT GOOGLE HAS BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.
14. Indemnification
14.1 You agree to hold harmless and indemnify Google, and its subsidiaries, affiliates, officers, agents, employees, or licensors from and against any third party claim arising from or in any way related to (a) your breach of the Terms, (b) your use of the Service, or (c) your violation of applicable laws, rules or regulations in connection with the Service, including any liability or expense arising from all claims, losses, damages (actual and consequential), suits, judgments, litigation costs and attorneys' fees, of every kind and nature. In such a case, Google will provide you with written notice of such claim, suit or action.
15. Changes to the Terms
15.1 Due to things like changes to the law or changes to functionality offered through the Service, Google may need to change these Terms from time to time. You should look at the Terms regularly. We'll post notice of the modified Terms within, or through, the Service. Once the modified Terms are posted, the changes will become effective immediately, and you are deemed to have accepted the modified Terms if you continue to use the Service. If you do not agree to the modified Terms for the Service, please stop using the Service.
16. General legal terms
16.1 The Terms constitute the whole legal agreement between you and Google and govern your use of the Service (but excluding any service which Google may provide to you under a separate written agreement), and completely replace any prior agreements between you and Google in relation to the Service.
16.2 You agree that Google may provide you with notices, including those regarding changes to the Terms, by email, regular mail, or postings on the Service.
16.3 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in the Terms (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google.
16.4 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of these Terms is invalid, then that provision will be removed from the Terms without affecting the rest of the Terms. The remaining provisions of the Terms will continue to be valid and enforceable.
16.5 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to the Terms and that such other companies shall be entitled to directly enforce, and rely upon, any provision of the Terms which confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to the Terms.
16.6 The Terms, and your relationship with Google under the Terms, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from the Terms. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.

91
README.md Normal file
View File

@@ -0,0 +1,91 @@
# Android KeepassDX
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/icon.png"> KeepassDX is a **multi-format KeePass manager for Android devices**. The app allows creating keys and passwords in a secure way by integrating with the Android design standards.
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen.jpg" width="220">
### Features
- 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, KeePassX, KeePassXC, …).
- 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**.
- **History** of each entry.
- Precise management of **settings**.
- Code written in **native languages** *(Kotlin / Java / JNI / C)*.
KeepassDX is **open source** and **ad-free**.
## What is KeePassDX?
An alternative to remembering an endless list of passwords manually. This is made more difficult by **using different passwords for each account**. If you use one password everywhere and security fails only one of those places, it grants access to your e-mail account, website, etc, and you may not know about it or notice, before bad things happen.
KeePassDX is a **password manager for Android**, which helps you **manage your passwords in a secure way**. You can put all your passwords in one database, locked with a **master key** and/or a **keyfile**. You **only have to remember one single master password and/or select the keyfile** to unlock the whole database. The databases are encrypted using the best and **most secure encryption algorithms** currently known.
## Small print?
KeePassDX is under **open source GPL3 license**, meaning you can use, study, change and share it at will. Copyleft ensures it stays that way.
From the full source, anyone can build, fork, and check whether for example the encryption algorithms are implemented correctly.
There is **no advertising**.
Do not worry, **the main features remain completely free**.
Optional visual styles are accessible after a contribution (and a congratulatory message (Ո‿Ո) ) or the purchase of an extended version to encourage contribution to the work of open source projects!
*If you contribute to the project and do not have access to the styles, do not hesitate to contact the author at [contact@kunzisoft.com](contact@kunzisoft.com).*
## Contributions
* Add features by making a **[pull request](https://help.github.com/articles/about-pull-requests/)**.
* Help to **[translate](https://hosted.weblate.org/projects/keepass-dx/strings/)** KeePassDX to your language (on [Weblate](https://hosted.weblate.org/projects/keepass-dx/) or by sending a [pull request](https://help.github.com/articles/about-pull-requests/)).
* **[Donate](https://www.kunzisoft.com/donation)** 人◕ ‿‿ ◕人Y for a better service and a quick development of your features.
* Buy the **[Pro version](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro)** of KeePassDX.
## Download
*[F-Droid](https://f-droid.org/en/packages/com.kunzisoft.keepass.libre/) is the recommended way of installing, a libre software project that verifies that all the libraries and app code is libre software.*
[<img src="https://f-droid.org/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.kunzisoft.keepass.libre/)
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play"
height="80">](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.free)
## Frequently Asked Questions
Other questions? You can read the [FAQ](https://github.com/Kunzisoft/KeePassDX/wiki/FAQ)
## Other devices
- [KeePass](https://keepass.info/) (https://keepass.info/) is the original and official project for the desktop, with technical documentation for standardized database files. It is updated regularly with active maintenance (written in C#).
- [KeePassXC](https://keepassxc.org/) (https://keepassxc.org/) is an alternative integration of KeePass written in C++.
- [KeeWeb](https://keeweb.info/) (https://keeweb.info/) is a web version that is also compatible with KeePass files.
## License
Copyright © 2020 Jeremy Jamet / [Kunzisoft](https://www.kunzisoft.com).
This file is part of KeePassDX.
[KeePassDX](https://www.keepassdx.com) 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/>.
*This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid) by bpellin.*

View File

@@ -1,84 +0,0 @@
# Android Keepass DX
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/icon.png"> Keepass DX is a **multi-format KeePass manager for Android devices**. The application allows to create keys and passwords in a secure way by integrating with the Android design standards.
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen.jpg" width="220">
### Features
* 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, KeePassX, KeePass XC...)
* Allows **fast copy** of fields and opening of URI / URL
* **Fingerprint** for fast unlocking
* Material design with **themes**
* **AutoFill** and Integration
* Field filling **keyboard**
* Precise management of **settings**
* Code written in **native language** *(Kotlin / Java / JNI / C)*
Keepass DX is **open source** and **ad-free**.
## What is KeePass DX?
Today you need to remember many passwords. You need a password for your e-mail account, your website's FTP password, online passwords (like website member account), etc. etc. etc. The list is endless. Also, you **should use different passwords for each account**. Because if you use only one password everywhere and someone gets this password you have a problem... A serious problem. The thief would have access to your e-mail account, website, etc. Unimaginable.
KeePass DX is a **free open source password manager for Android**, which helps you to **manage your passwords in a secure way**. You can put all your passwords in one database, which is locked with one **master key** or a **key file**. So you **only have to remember one single master password or select the key file** to unlock the whole database. The databases are encrypted using the best and **most secure encryption algorithms** currently known.
## Is it really free?
Yes, KeePass DX is under **free license (OSI certified)** and **without advertising**. You can have a look at its full source and check whether the encryption algorithms are implemented correctly.
*Note : If you access the application from a store, visual features may not be available to incentivize the contribution to the work of open source projects. These optional visuals are accessible after a donation (and a small congratulation message :) or the purchase of an extended version, but do not worry, the main features remain completely free. If you contribute to the project and you do not have access to the themes, do not hesitate to contact me at [contact@kunzisoft.com](contact@kunzisoft.com), I will give you the procedure.*
## Contributions
You can contribute in different ways to help us on our work.
* Add features by a **[pull request](https://help.github.com/articles/about-pull-requests/)**.
* Help to **[translate](https://hosted.weblate.org/projects/keepass-dx/strings/)** into your language (By using [Weblate](https://hosted.weblate.org/projects/keepass-dx/) or with a manual [pull request](https://help.github.com/articles/about-pull-requests/))
* **[Donate](https://www.kunzisoft.com/donation)** 人◕ ‿‿ ◕人Y for a better service and a quick development of your features.
* Buy the **[Pro version](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro)** of KeePass DX
## Download
*We recommend the installation from [F-Droid](https://f-droid.org/en/packages/com.kunzisoft.keepass.libre/) which verifies that all libraries and application code are open source.*
[<img src="https://f-droid.org/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.kunzisoft.keepass.libre/)
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play"
height="80">](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.free)
## F.A.Q.
Other questions? You can read the [F.A.Q.](https://www.keepassdx.com/FAQ)
## Other devices
- [KeePass XC](https://keepassxc.org/) (https://keepassxc.org/) works with **GNU/Linux**, **Mac** and **Windows**, is updated regularly and under the terms of the GNU General Public License. This is the recommended version for computers.
- [KeePass](https://keepass.info/) (https://keepass.info/) is the historical project, with good technical documentation for standardized database files but only running on **Windows**.
## License
Copyright (c) 2019 Jeremy Jamet / [Kunzisoft](https://www.kunzisoft.com).
This file is part of KeePass DX.
[KeePass DX](https://www.keepassdx.com) 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.
KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid) by bpellin.*

1
_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-cayman

1
app/.gitignore vendored
View File

@@ -1 +1,2 @@
.cxx
.externalNativeBuild .externalNativeBuild

View File

@@ -4,21 +4,29 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
android { android {
compileSdkVersion 27 compileSdkVersion 29
buildToolsVersion '28.0.3' buildToolsVersion '29.0.3'
defaultConfig { defaultConfig {
applicationId "com.kunzisoft.keepass" applicationId "com.kunzisoft.keepass"
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 27 targetSdkVersion 29
versionCode = 21 versionCode = 33
versionName = "2.5.0.0beta21" versionName = "2.5"
multiDexEnabled true multiDexEnabled true
testApplicationId = "com.kunzisoft.keepass.tests" testApplicationId = "com.kunzisoft.keepass.tests"
testInstrumentationRunner = "android.test.InstrumentationTestRunner" testInstrumentationRunner = "android.test.InstrumentationTestRunner"
buildConfigField "String[]", "ICON_PACKS", "{\"classic\",\"material\"}" buildConfigField "String[]", "ICON_PACKS", "{\"classic\",\"material\"}"
manifestPlaceholders = [ googleAndroidBackupAPIKey:"" ]
kapt {
arguments {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas".toString())
}
}
} }
externalNativeBuild { externalNativeBuild {
@@ -27,7 +35,6 @@ android {
} }
} }
buildTypes { buildTypes {
release { release {
minifyEnabled = false minifyEnabled = false
@@ -38,32 +45,36 @@ android {
dexOptions { dexOptions {
} }
flavorDimensions "tier" flavorDimensions "version"
productFlavors { productFlavors {
libre { libre {
dimension "version"
applicationIdSuffix = ".libre" applicationIdSuffix = ".libre"
buildConfigField "String", "BUILD_VERSION", "\"libre\"" buildConfigField "String", "BUILD_VERSION", "\"libre\""
buildConfigField "boolean", "FULL_VERSION", "true" buildConfigField "boolean", "FULL_VERSION", "true"
buildConfigField "boolean", "CLOSED_STORE", "false" buildConfigField "boolean", "CLOSED_STORE", "false"
buildConfigField "String[]", "STYLES_DISABLED", "{\"KeepassDXStyle_Dark\",\"KeepassDXStyle_Red\",\"KeepassDXStyle_Purple\"}" buildConfigField "String[]", "STYLES_DISABLED", "{\"KeepassDXStyle_Dark\",\"KeepassDXStyle_Red\",\"KeepassDXStyle_Purple\"}"
buildConfigField "String[]", "ICON_PACKS_DISABLED", "{}" buildConfigField "String[]", "ICON_PACKS_DISABLED", "{}"
} }
pro { pro {
dimension "version"
applicationIdSuffix = ".pro" applicationIdSuffix = ".pro"
buildConfigField "String", "BUILD_VERSION", "\"pro\"" buildConfigField "String", "BUILD_VERSION", "\"pro\""
buildConfigField "boolean", "FULL_VERSION", "true" buildConfigField "boolean", "FULL_VERSION", "true"
buildConfigField "boolean", "CLOSED_STORE", "true" buildConfigField "boolean", "CLOSED_STORE", "true"
buildConfigField "String[]", "STYLES_DISABLED", "{}" buildConfigField "String[]", "STYLES_DISABLED", "{}"
buildConfigField "String[]", "ICON_PACKS_DISABLED", "{}" buildConfigField "String[]", "ICON_PACKS_DISABLED", "{}"
manifestPlaceholders = [ googleAndroidBackupAPIKey:"AEdPqrEAAAAIZiXvrQCzSV9LNI6-p7cjTKENZLHIrz_zaqZuQQ" ]
} }
free { free {
dimension "version"
applicationIdSuffix = ".free" applicationIdSuffix = ".free"
buildConfigField "String", "BUILD_VERSION", "\"free\"" buildConfigField "String", "BUILD_VERSION", "\"free\""
buildConfigField "boolean", "FULL_VERSION", "false" buildConfigField "boolean", "FULL_VERSION", "false"
buildConfigField "boolean", "CLOSED_STORE", "true" buildConfigField "boolean", "CLOSED_STORE", "true"
buildConfigField "String[]", "STYLES_DISABLED", "{\"KeepassDXStyle_Dark\",\"KeepassDXStyle_Blue\",\"KeepassDXStyle_Red\",\"KeepassDXStyle_Purple\"}" buildConfigField "String[]", "STYLES_DISABLED", "{\"KeepassDXStyle_Dark\",\"KeepassDXStyle_Blue\",\"KeepassDXStyle_Red\",\"KeepassDXStyle_Purple\"}"
buildConfigField "String[]", "ICON_PACKS_DISABLED", "{}" buildConfigField "String[]", "ICON_PACKS_DISABLED", "{}"
manifestPlaceholders = [ googleAndroidBackupAPIKey:"AEdPqrEAAAAIbRfbV8fHLItXo8OcHwrO0sSNblqhPwkc0DPTqg" ]
} }
} }
@@ -79,43 +90,40 @@ android {
} }
} }
def supportVersion = "27.1.1" def room_version = "2.2.5"
def spongycastleVersion = "1.58.0.0"
def permissionDispatcherVersion = "3.3.1"
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.android.support:appcompat-v7:$supportVersion" implementation 'androidx.appcompat:appcompat:1.1.0'
implementation "com.android.support:design:$supportVersion" implementation 'androidx.preference:preference:1.1.1'
implementation "com.android.support:preference-v7:$supportVersion" implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation "com.android.support:preference-v14:$supportVersion" implementation 'androidx.cardview:cardview:1.0.0'
implementation "com.android.support:cardview-v7:$supportVersion" implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'androidx.documentfile:documentfile:1.0.1'
implementation "com.madgag.spongycastle:core:$spongycastleVersion" implementation 'androidx.biometric:biometric:1.0.1'
implementation "com.madgag.spongycastle:prov:$spongycastleVersion" implementation "androidx.core:core-ktx:1.2.0"
// Expandable view // To upgrade with style
implementation 'net.cachapa.expandablelayout:expandablelayout:2.9.2' implementation 'com.google.android.material:material:1.0.0'
// Database
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// Crypto
implementation 'org.bouncycastle:bcprov-jdk15on:1.65'
// Time // Time
implementation 'joda-time:joda-time:2.9.9' implementation 'joda-time:joda-time:2.9.9'
implementation 'org.sufficientlysecure:html-textview:3.5' // Color
implementation 'com.nononsenseapps:filepicker:4.1.0' implementation 'com.github.Kunzisoft:AndroidClearChroma:2.3'
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.12.0' // Education
// Permissions implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.0'
implementation("com.github.hotchemi:permissionsdispatcher:$permissionDispatcherVersion") {
// if you don't use android.app.Fragment you can exclude support for them
exclude module: "support-v13"
}
kapt "com.github.hotchemi:permissionsdispatcher-processor:$permissionDispatcherVersion"
// Apache Commons Collections // Apache Commons Collections
implementation 'commons-collections:commons-collections:3.2.1' implementation 'commons-collections:commons-collections:3.2.1'
implementation 'org.apache.commons:commons-io:1.3.2' implementation 'org.apache.commons:commons-io:1.3.2'
// Base64 // Apache Commons Codec
implementation 'biz.source_code:base64coder:2010-12-19' implementation 'commons-codec:commons-codec:1.11'
// IO-Extras
implementation 'com.github.davidmoten:io-extras:0.1'
implementation 'com.google.code.gson:gson:2.8.4'
implementation 'com.google.guava:guava:23.0-android'
// Icon pack // Icon pack
implementation project(path: ':icon-pack-classic') implementation project(path: ':icon-pack-classic')
implementation project(path: ':icon-pack-material') implementation project(path: ':icon-pack-material')
// Tests
androidTestImplementation 'junit:junit:4.12'
} }

View File

@@ -0,0 +1,84 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "56438e5f7372ef3e36e33b782aed245d",
"entities": [
{
"tableName": "file_database_history",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `database_alias` TEXT NOT NULL, `keyfile_uri` TEXT, `updated` INTEGER NOT NULL, PRIMARY KEY(`database_uri`))",
"fields": [
{
"fieldPath": "databaseUri",
"columnName": "database_uri",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "databaseAlias",
"columnName": "database_alias",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "keyFileUri",
"columnName": "keyfile_uri",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "updated",
"columnName": "updated",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"database_uri"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "cipher_database",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `encrypted_value` TEXT NOT NULL, `specs_parameters` TEXT NOT NULL, PRIMARY KEY(`database_uri`))",
"fields": [
{
"fieldPath": "databaseUri",
"columnName": "database_uri",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "encryptedValue",
"columnName": "encrypted_value",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "specParameters",
"columnName": "specs_parameters",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"database_uri"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '56438e5f7372ef3e36e33b782aed245d')"
]
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests;
import android.test.AndroidTestCase;
import com.kunzisoft.keepass.tests.database.TestData;
public class AccentTest extends AndroidTestCase {
private static final String KEYFILE = "";
private static final String PASSWORD = "é";
private static final String ASSET = "accent.kdb";
private static final String FILENAME = "/sdcard/accent.kdb";
public void testOpen() {
/*
try {
TestData.GetDb(getContext(), ASSET, PASSWORD, KEYFILE, FILENAME);
} catch (Exception e) {
assertTrue("Failed to open database", false);
}
*/
}
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests;
import junit.framework.Test;
import junit.framework.TestSuite;
import android.test.suitebuilder.TestSuiteBuilder;
public class AllTests extends TestSuite {
public static Test suite() {
return new TestSuiteBuilder(AllTests.class)
.includeAllPackagesUnderHere()
.build();
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests;
import junit.framework.Test;
import junit.framework.TestSuite;
import android.test.suitebuilder.TestSuiteBuilder;
public class OutputTests extends TestSuite {
public static Test suite() {
return new TestSuiteBuilder(AllTests.class)
.includePackages("com.kunzisoft.keepass.tests.output")
.build();
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests
import junit.framework.TestCase
import com.kunzisoft.keepass.database.element.PwDate
import org.junit.Assert
class PwDateTest : TestCase() {
fun testDate() {
val jDate = PwDate(System.currentTimeMillis())
val intermediate = PwDate(jDate)
val cDate = PwDate(intermediate.byteArrayDate!!, 0)
Assert.assertTrue("jDate and intermediate not equal", jDate == intermediate)
Assert.assertTrue("jDate and cDate not equal", cDate == jDate)
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests;
import static org.junit.Assert.assertArrayEquals;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import android.test.AndroidTestCase;
import com.kunzisoft.keepass.database.element.PwEntryV3;
import com.kunzisoft.keepass.tests.database.TestData;
public class PwEntryTestV3 extends AndroidTestCase {
PwEntryV3 mPE;
@Override
protected void setUp() throws Exception {
super.setUp();
// mPE = (PwEntryV3) TestData.GetTest1(getContext()).getEntryAt(0);
}
public void testName() {
assertTrue("Name was " + mPE.getTitle(), mPE.getTitle().equals("Amazon"));
}
public void testPassword() throws UnsupportedEncodingException {
String sPass = "12345";
byte[] password = sPass.getBytes("UTF-8");
assertArrayEquals(password, mPE.getPasswordBytes());
}
public void testCreation() {
Calendar cal = Calendar.getInstance();
cal.setTime(mPE.getCreationTime().getDate());
assertEquals("Incorrect year.", cal.get(Calendar.YEAR), 2009);
assertEquals("Incorrect month.", cal.get(Calendar.MONTH), 3);
assertEquals("Incorrect day.", cal.get(Calendar.DAY_OF_MONTH), 23);
}
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests;
import junit.framework.TestCase;
public class PwEntryTestV4 extends TestCase {
public void testAssign() {
/*
TODO Test
PwEntryV4 entry = new PwEntryV4();
entry.setAdditional("test223");
entry.setAutoType(new AutoType());
entry.getAutoType().defaultSequence = "1324";
entry.getAutoType().enabled = true;
entry.getAutoType().obfuscationOptions = 123412432109L;
entry.getAutoType().put("key", "value");
entry.setBackgroundColor("blue");
entry.putProtectedBinary("key1", new ProtectedBinary(false, new byte[] {0,1}));
entry.setIconCustom(new PwIconCustom(UUID.randomUUID(), new byte[0]));
entry.setForegroundColor("red");
entry.addToHistory(new PwEntryV4());
entry.setIconStandard(new PwIconStandard(5));
entry.setOverrideURL("override");
entry.setParent(new PwGroupV4());
entry.addExtraField("key2", new ProtectedString(false, "value2"));
entry.setUrl("http://localhost");
entry.setNodeId(UUID.randomUUID());
PwEntryV4 target = new PwEntryV4();
target.updateWith(entry);
/* This test is not so useful now that I am not implementing value equality for Entries
assertTrue("Entries do not match.", entry.equals(target));
*/
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests;
import android.test.AndroidTestCase;
import com.kunzisoft.keepass.database.element.PwGroupV3;
import com.kunzisoft.keepass.tests.database.TestData;
public class PwGroupTest extends AndroidTestCase {
PwGroupV3 mPG;
@Override
protected void setUp() throws Exception {
super.setUp();
//mPG = (PwGroupV3) TestData.GetTest1(getContext()).getGroups().get(0);
}
public void testGroupName() {
//assertTrue("Name was " + mPG.getTitle(), mPG.getTitle().equals("Internet"));
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Environment;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
public class TestUtil {
private static final File sdcard = Environment.getExternalStorageDirectory();
public static void extractKey(Context ctx, String asset, String target) throws Exception {
InputStream key = ctx.getAssets().open(asset, AssetManager.ACCESS_STREAMING);
FileOutputStream keyFile = new FileOutputStream(target);
while (true) {
byte[] buf = new byte[1024];
int read = key.read(buf);
if ( read == -1 ) {
break;
} else {
keyFile.write(buf, 0, read);
}
}
keyFile.close();
}
public static String getSdPath(String filename) {
File file = new File(sdcard, filename);
return file.getAbsolutePath();
}
}

View File

@@ -1,211 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests;
import static org.junit.Assert.assertArrayEquals;
import java.io.ByteArrayOutputStream;
import java.util.Calendar;
import java.util.Random;
import java.util.UUID;
import junit.framework.TestCase;
import com.kunzisoft.keepass.database.element.PwDate;
import com.kunzisoft.keepass.stream.LEDataInputStream;
import com.kunzisoft.keepass.stream.LEDataOutputStream;
import com.kunzisoft.keepass.utils.Types;
public class TypesTest extends TestCase {
public void testReadWriteLongZero() {
testReadWriteLong((byte) 0);
}
public void testReadWriteLongMax() {
testReadWriteLong(Byte.MAX_VALUE);
}
public void testReadWriteLongMin() {
testReadWriteLong(Byte.MIN_VALUE);
}
public void testReadWriteLongRnd() {
Random rnd = new Random();
byte[] buf = new byte[1];
rnd.nextBytes(buf);
testReadWriteLong(buf[0]);
}
private void testReadWriteLong(byte value) {
byte[] orig = new byte[8];
byte[] dest = new byte[8];
setArray(orig, value, 0, 8);
long one = LEDataInputStream.readLong(orig, 0);
LEDataOutputStream.writeLong(one, dest, 0);
assertArrayEquals(orig, dest);
}
public void testReadWriteIntZero() {
testReadWriteInt((byte) 0);
}
public void testReadWriteIntMin() {
testReadWriteInt(Byte.MIN_VALUE);
}
public void testReadWriteIntMax() {
testReadWriteInt(Byte.MAX_VALUE);
}
private void testReadWriteInt(byte value) {
byte[] orig = new byte[4];
byte[] dest = new byte[4];
for (int i = 0; i < 4; i++ ) {
orig[i] = 0;
}
setArray(orig, value, 0, 4);
int one = LEDataInputStream.readInt(orig, 0);
LEDataOutputStream.writeInt(one, dest, 0);
assertArrayEquals(orig, dest);
}
private void setArray(byte[] buf, byte value, int offset, int size) {
for (int i = offset; i < offset + size; i++) {
buf[i] = value;
}
}
public void testReadWriteShortOne() {
byte[] orig = new byte[2];
byte[] dest = new byte[2];
orig[0] = 0;
orig[1] = 1;
int one = LEDataInputStream.readUShort(orig, 0);
dest = LEDataOutputStream.writeUShortBuf(one);
assertArrayEquals(orig, dest);
}
public void testReadWriteShortMin() {
testReadWriteShort(Byte.MIN_VALUE);
}
public void testReadWriteShortMax() {
testReadWriteShort(Byte.MAX_VALUE);
}
private void testReadWriteShort(byte value) {
byte[] orig = new byte[2];
byte[] dest = new byte[2];
setArray(orig, value, 0, 2);
int one = LEDataInputStream.readUShort(orig, 0);
LEDataOutputStream.writeUShort(one, dest, 0);
assertArrayEquals(orig, dest);
}
public void testReadWriteByteZero() {
testReadWriteByte((byte) 0);
}
public void testReadWriteByteMin() {
testReadWriteByte(Byte.MIN_VALUE);
}
public void testReadWriteByteMax() {
testReadWriteShort(Byte.MAX_VALUE);
}
private void testReadWriteByte(byte value) {
byte[] orig = new byte[1];
byte[] dest = new byte[1];
setArray(orig, value, 0, 1);
int one = Types.readUByte(orig, 0);
Types.writeUByte(one, dest, 0);
assertArrayEquals(orig, dest);
}
public void testDate() {
Calendar cal = Calendar.getInstance();
Calendar expected = Calendar.getInstance();
expected.set(2008, 1, 2, 3, 4, 5);
byte[] buf = PwDate.Companion.writeTime(expected.getTime(), cal);
Calendar actual = Calendar.getInstance();
actual.setTime(PwDate.Companion.readTime(buf, 0, cal));
assertEquals("Year mismatch: ", 2008, actual.get(Calendar.YEAR));
assertEquals("Month mismatch: ", 1, actual.get(Calendar.MONTH));
assertEquals("Day mismatch: ", 1, actual.get(Calendar.DAY_OF_MONTH));
assertEquals("Hour mismatch: ", 3, actual.get(Calendar.HOUR_OF_DAY));
assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE));
assertEquals("Second mismatch: ", 5, actual.get(Calendar.SECOND));
}
public void testUUID() {
Random rnd = new Random();
byte[] bUUID = new byte[16];
rnd.nextBytes(bUUID);
UUID uuid = Types.bytestoUUID(bUUID);
byte[] eUUID = Types.UUIDtoBytes(uuid);
assertArrayEquals("UUID match failed", bUUID, eUUID);
}
public void testULongMax() throws Exception {
byte[] ulongBytes = new byte[8];
for (int i = 0; i < ulongBytes.length; i++) {
ulongBytes[i] = -1;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
LEDataOutputStream leos = new LEDataOutputStream(bos);
leos.writeLong(Types.ULONG_MAX_VALUE);
leos.close();
byte[] uLongMax = bos.toByteArray();
assertArrayEquals(ulongBytes, uLongMax);
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2017 Brian Pellin, 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.tests.crypto
import org.junit.Assert.assertArrayEquals
import java.io.IOException
import java.util.Random
import junit.framework.TestCase
import com.kunzisoft.keepass.crypto.finalkey.AndroidAESKeyTransformer
import com.kunzisoft.keepass.crypto.finalkey.NativeAESKeyTransformer
class AESKeyTest : TestCase() {
private lateinit var mRand: Random
@Throws(Exception::class)
override fun setUp() {
super.setUp()
mRand = Random()
}
@Throws(IOException::class)
fun testAES() {
// Test both an old and an even number to test my flip variable
testAESFinalKey(5)
testAESFinalKey(6)
}
@Throws(IOException::class)
private fun testAESFinalKey(rounds: Long) {
val seed = ByteArray(32)
val key = ByteArray(32)
val nativeKey: ByteArray?
val androidKey: ByteArray?
mRand.nextBytes(seed)
mRand.nextBytes(key)
val androidAESKey = AndroidAESKeyTransformer()
androidKey = androidAESKey.transformMasterKey(seed, key, rounds)
val nativeAESKey = NativeAESKeyTransformer()
nativeKey = nativeAESKey.transformMasterKey(seed, key, rounds)
assertArrayEquals("Does not match", androidKey, nativeKey)
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.crypto;
import com.kunzisoft.keepass.crypto.CipherFactory;
import junit.framework.TestCase;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import static org.junit.Assert.assertArrayEquals;
public class AESTest extends TestCase {
private Random mRand = new Random();
public void testEncrypt() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
// Test above below and at the blocksize
testFinal(15);
testFinal(16);
testFinal(17);
// Test random larger sizes
int size = mRand.nextInt(494) + 18;
testFinal(size);
}
private void testFinal(int dataSize) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
// Generate some input
byte[] input = new byte[dataSize];
mRand.nextBytes(input);
// Generate key
byte[] keyArray = new byte[32];
mRand.nextBytes(keyArray);
SecretKeySpec key = new SecretKeySpec(keyArray, "AES");
// Generate IV
byte[] ivArray = new byte[16];
mRand.nextBytes(ivArray);
IvParameterSpec iv = new IvParameterSpec(ivArray);
Cipher android = CipherFactory.INSTANCE.getInstance("AES/CBC/PKCS5Padding", true);
android.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] outAndroid = android.doFinal(input, 0, dataSize);
Cipher nat = CipherFactory.INSTANCE.getInstance("AES/CBC/PKCS5Padding");
nat.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] outNative = nat.doFinal(input, 0, dataSize);
assertArrayEquals("Arrays differ on size: " + dataSize, outAndroid, outNative);
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2017 Brian Pellin, 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.tests.crypto
import com.kunzisoft.keepass.crypto.CipherFactory
import junit.framework.TestCase
import java.security.InvalidAlgorithmParameterException
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import java.util.Random
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import org.junit.Assert.assertArrayEquals
class AESTest : TestCase() {
private val mRand = Random()
@Throws(InvalidKeyException::class, NoSuchAlgorithmException::class, NoSuchPaddingException::class, IllegalBlockSizeException::class, BadPaddingException::class, InvalidAlgorithmParameterException::class)
fun testEncrypt() {
// Test above below and at the blocksize
testFinal(15)
testFinal(16)
testFinal(17)
// Test random larger sizes
val size = mRand.nextInt(494) + 18
testFinal(size)
}
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, IllegalBlockSizeException::class, BadPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
private fun testFinal(dataSize: Int) {
// Generate some input
val input = ByteArray(dataSize)
mRand.nextBytes(input)
// Generate key
val keyArray = ByteArray(32)
mRand.nextBytes(keyArray)
val key = SecretKeySpec(keyArray, "AES")
// Generate IV
val ivArray = ByteArray(16)
mRand.nextBytes(ivArray)
val iv = IvParameterSpec(ivArray)
val android = CipherFactory.getInstance("AES/CBC/PKCS5Padding", true)
android.init(Cipher.ENCRYPT_MODE, key, iv)
val outAndroid = android.doFinal(input, 0, dataSize)
val nat = CipherFactory.getInstance("AES/CBC/PKCS5Padding")
nat.init(Cipher.ENCRYPT_MODE, key, iv)
val outNative = nat.doFinal(input, 0, dataSize)
assertArrayEquals("Arrays differ on size: $dataSize", outAndroid, outNative)
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.crypto;
import static org.junit.Assert.assertArrayEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import junit.framework.TestCase;
import com.kunzisoft.keepass.crypto.CipherFactory;
import com.kunzisoft.keepass.crypto.engine.AesEngine;
import com.kunzisoft.keepass.crypto.engine.CipherEngine;
import com.kunzisoft.keepass.stream.BetterCipherInputStream;
import com.kunzisoft.keepass.stream.LEDataInputStream;
public class CipherTest extends TestCase {
private Random rand = new Random();
public void testCipherFactory() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
byte[] key = new byte[32];
byte[] iv = new byte[16];
byte[] plaintext = new byte[1024];
rand.nextBytes(key);
rand.nextBytes(iv);
rand.nextBytes(plaintext);
CipherEngine aes = CipherFactory.INSTANCE.getInstance(AesEngine.CIPHER_UUID);
Cipher encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv);
Cipher decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv);
byte[] secrettext = encrypt.doFinal(plaintext);
byte[] decrypttext = decrypt.doFinal(secrettext);
assertArrayEquals("Encryption and decryption failed", plaintext, decrypttext);
}
public void testCipherStreams() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException {
final int MESSAGE_LENGTH = 1024;
byte[] key = new byte[32];
byte[] iv = new byte[16];
byte[] plaintext = new byte[MESSAGE_LENGTH];
rand.nextBytes(key);
rand.nextBytes(iv);
rand.nextBytes(plaintext);
CipherEngine aes = CipherFactory.INSTANCE.getInstance(AesEngine.CIPHER_UUID);
Cipher encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv);
Cipher decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
CipherOutputStream cos = new CipherOutputStream(bos, encrypt);
cos.write(plaintext);
cos.close();
byte[] secrettext = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(secrettext);
BetterCipherInputStream cis = new BetterCipherInputStream(bis, decrypt);
LEDataInputStream lis = new LEDataInputStream(cis);
byte[] decrypttext = lis.readBytes(MESSAGE_LENGTH);
assertArrayEquals("Encryption and decryption failed", plaintext, decrypttext);
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2017 Brian Pellin, 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.tests.crypto
import org.junit.Assert.assertArrayEquals
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.security.InvalidAlgorithmParameterException
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import java.util.Random
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.CipherOutputStream
import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException
import junit.framework.TestCase
import com.kunzisoft.keepass.crypto.CipherFactory
import com.kunzisoft.keepass.crypto.engine.AesEngine
import com.kunzisoft.keepass.stream.BetterCipherInputStream
import com.kunzisoft.keepass.stream.LittleEndianDataInputStream
class CipherTest : TestCase() {
private val rand = Random()
@Throws(InvalidKeyException::class, NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidAlgorithmParameterException::class, IllegalBlockSizeException::class, BadPaddingException::class)
fun testCipherFactory() {
val key = ByteArray(32)
val iv = ByteArray(16)
val plaintext = ByteArray(1024)
rand.nextBytes(key)
rand.nextBytes(iv)
rand.nextBytes(plaintext)
val aes = CipherFactory.getInstance(AesEngine.CIPHER_UUID)
val encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv)
val decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv)
val secrettext = encrypt.doFinal(plaintext)
val decrypttext = decrypt.doFinal(secrettext)
assertArrayEquals("Encryption and decryption failed", plaintext, decrypttext)
}
@Throws(InvalidKeyException::class, NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidAlgorithmParameterException::class, IllegalBlockSizeException::class, BadPaddingException::class, IOException::class)
fun testCipherStreams() {
val MESSAGE_LENGTH = 1024
val key = ByteArray(32)
val iv = ByteArray(16)
val plaintext = ByteArray(MESSAGE_LENGTH)
rand.nextBytes(key)
rand.nextBytes(iv)
rand.nextBytes(plaintext)
val aes = CipherFactory.getInstance(AesEngine.CIPHER_UUID)
val encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv)
val decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv)
val bos = ByteArrayOutputStream()
val cos = CipherOutputStream(bos, encrypt)
cos.write(plaintext)
cos.close()
val secrettext = bos.toByteArray()
val bis = ByteArrayInputStream(secrettext)
val cis = BetterCipherInputStream(bis, decrypt)
val lis = LittleEndianDataInputStream(cis)
val decrypttext = lis.readBytes(MESSAGE_LENGTH)
assertArrayEquals("Encryption and decryption failed", plaintext, decrypttext)
}
}

View File

@@ -1,66 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.crypto;
import static org.junit.Assert.assertArrayEquals;
import java.io.IOException;
import java.util.Random;
import junit.framework.TestCase;
import com.kunzisoft.keepass.crypto.finalkey.AndroidFinalKey;
import com.kunzisoft.keepass.crypto.finalkey.NativeFinalKey;
public class FinalKeyTest extends TestCase {
private Random mRand;
@Override
protected void setUp() throws Exception {
super.setUp();
mRand = new Random();
}
public void testNativeAndroid() throws IOException {
// Test both an old and an even number to test my flip variable
testNativeFinalKey(5);
testNativeFinalKey(6);
}
private void testNativeFinalKey(int rounds) throws IOException {
byte[] seed = new byte[32];
byte[] key = new byte[32];
byte[] nativeKey;
byte[] androidKey;
mRand.nextBytes(seed);
mRand.nextBytes(key);
AndroidFinalKey aKey = new AndroidFinalKey();
androidKey = aKey.transformMasterKey(seed, key, rounds);
NativeFinalKey nKey = new NativeFinalKey();
nativeKey = nKey.transformMasterKey(seed, key, rounds);
assertArrayEquals("Does not match", androidKey, nativeKey);
}
}

View File

@@ -1,111 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.database;
import android.test.AndroidTestCase;
import com.kunzisoft.keepass.database.element.GroupVersioned;
import com.kunzisoft.keepass.database.element.PwDatabase;
import com.kunzisoft.keepass.database.element.PwDatabaseV3;
import com.kunzisoft.keepass.database.element.PwEntryV3;
public class DeleteEntry extends AndroidTestCase {
private static final String GROUP1_NAME = "Group1";
private static final String ENTRY1_NAME = "Test1";
private static final String ENTRY2_NAME = "Test2";
private static final String KEYFILE = "";
private static final String PASSWORD = "12345";
private static final String ASSET = "delete.kdb";
private static final String FILENAME = "/sdcard/delete.kdb";
public void testDelete() {
/*
Database db;
Context ctx = getContext();
try {
db = TestData.GetDb(ctx, ASSET, PASSWORD, KEYFILE, FILENAME);
} catch (Exception e) {
assertTrue("Failed to open database: " + e.getMessage(), false);
return;
}
PwDatabaseV3 pm = (PwDatabaseV3) db.getPwDatabase();
GroupVersioned group1 = getGroup(pm, GROUP1_NAME);
assertNotNull("Could not find group1", group1);
// Delete the group
DeleteGroupRunnable task = new DeleteGroupRunnable(null, db, group1, null, true);
task.run();
// Verify the entries were deleted
PwEntryInterface entry1 = getEntry(pm, ENTRY1_NAME);
assertNull("Entry 1 was not removed", entry1);
PwEntryInterface entry2 = getEntry(pm, ENTRY2_NAME);
assertNull("Entry 2 was not removed", entry2);
// Verify the entries were removed from the search index
SearchDbHelper dbHelp = new SearchDbHelper(ctx);
GroupVersioned results1 = dbHelp.search(db.getPwDatabase(), ENTRY1_NAME, 100);
GroupVersioned results2 = dbHelp.search(db.getPwDatabase(), ENTRY2_NAME, 100);
assertEquals("Entry1 was not removed from the search results", 0, results1.numbersOfChildEntries());
assertEquals("Entry2 was not removed from the search results", 0, results2.numbersOfChildEntries());
// Verify the group was deleted
group1 = getGroup(pm, GROUP1_NAME);
assertNull("Group 1 was not removed.", group1);
*/
}
private PwEntryV3 getEntry(PwDatabaseV3 pm, String name) {
/*
TODO test
List<PwEntryV3> entries = pm.getEntries();
for ( int i = 0; i < entries.size(); i++ ) {
PwEntryV3 entry = entries.get(i);
if ( entry.getTitle().equals(name) ) {
return entry;
}
}
*/
return null;
}
private GroupVersioned getGroup(PwDatabase pm, String name) {
/*
List<GroupVersioned> groups = pm.getGroups();
for ( int i = 0; i < groups.size(); i++ ) {
GroupVersioned group = groups.get(i);
if ( group.getTitle().equals(name) ) {
return group;
}
}
*/
return null;
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.database;
import com.kunzisoft.keepass.database.element.PwDatabaseV4;
import com.kunzisoft.keepass.database.element.PwEntryV4;
import junit.framework.TestCase;
public class EntryV4 extends TestCase {
public void testBackup() {
/*
PwDatabaseV4 db = new PwDatabaseV4();
db.setHistoryMaxItems(2);
PwEntryV4 entry = new PwEntryV4();
entry.startToManageFieldReferences(db);
entry.setTitle("Title1");
entry.setUsername("User1");
entry.createBackup(db);
entry.setTitle("Title2");
entry.setUsername("User2");
entry.createBackup(db);
entry.setTitle("Title3");
entry.setUsername("User3");
entry.createBackup(db);
PwEntryV4 backup = entry.getHistory().get(0);
entry.stopToManageFieldReferences();
assertEquals("Title2", backup.getTitle());
assertEquals("User2", backup.getUsername());
*/
}
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.database;
import android.test.AndroidTestCase;
public class Kdb3 extends AndroidTestCase {
private void testKeyfile(String dbAsset, String keyAsset, String password) throws Exception {
/*
Context ctx = getContext();
File sdcard = Environment.getExternalStorageDirectory();
String keyPath = sdcard.getAbsolutePath() + "/key";
TestUtil.extractKey(ctx, keyAsset, keyPath);
AssetManager am = ctx.getAssets();
InputStream is = am.open(dbAsset, AssetManager.ACCESS_STREAMING);
ImporterV3 importer = new ImporterV3();
importer.openDatabase(is, password, TestUtil.getKeyFileInputStream(ctx, keyPath));
is.close();
*/
}
public void testXMLKeyFile() throws Exception {
testKeyfile("kdb_with_xml_keyfile.kdb", "keyfile.key", "12345");
}
public void testBinary64KeyFile() throws Exception {
testKeyfile("binary-key.kdb", "binary.key", "12345");
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.database;
import android.test.AndroidTestCase;
public class Kdb3Twofish extends AndroidTestCase {
public void testReadTwofish() throws Exception {
/*
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open("twofish.kdb", AssetManager.ACCESS_STREAMING);
ImporterV3 importer = new ImporterV3();
PwDatabaseV3 db = importer.openDatabase(is, "12345", null);
assertTrue(db.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish);
is.close();
*/
}
}

View File

@@ -1,171 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.database;
import android.content.Context;
import android.content.res.AssetManager;
import android.test.AndroidTestCase;
import com.kunzisoft.keepass.database.exception.InvalidDBException;
import com.kunzisoft.keepass.database.exception.PwDbOutputException;
import com.kunzisoft.keepass.tests.TestUtil;
import java.io.IOException;
import java.io.InputStream;
public class Kdb4 extends AndroidTestCase {
public void testDetection() throws IOException, InvalidDBException {
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING);
/*
TODO Test
Importer importer = ImporterFactory.createImporter(is);
assertTrue(importer instanceof ImporterV4);
is.close();
*/
}
public void testParsing() throws IOException, InvalidDBException {
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING);
/*
TODO Test
ImporterV4 importer = new ImporterV4();
importer.openDatabase(is, "12345", null);
is.close();
*/
}
public void testSavingKDBXV3() throws IOException, InvalidDBException, PwDbOutputException {
testSaving("test.kdbx", "12345", "test-out.kdbx");
}
public void testSavingKDBXV4() throws IOException, InvalidDBException, PwDbOutputException {
testSaving("test-kdbxv4.kdbx", "1", "test-kdbxv4-out.kdbx");
}
private void testSaving(String inputFile, String password, String outputFile) throws IOException, InvalidDBException, PwDbOutputException {
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open(inputFile, AssetManager.ACCESS_STREAMING);
/*
TODO Test
ImporterV4 importer = new ImporterV4();
PwDatabaseV4 db = importer.openDatabase(is, password, null);
is.close();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PwDbV4Output output = (PwDbV4Output) PwDbOutput.getInstance(db, bos);
output.output();
byte[] data = bos.toByteArray();
FileOutputStream fos = new FileOutputStream(TestUtil.getSdPath(outputFile), false);
InputStream bis = new ByteArrayInputStream(data);
bis = new CopyInputStream(bis, fos);
importer = new ImporterV4();
db = importer.openDatabase(bis, password, null);
bis.close();
fos.close();
*/
}
@Override
protected void setUp() throws Exception {
super.setUp();
TestUtil.extractKey(getContext(), "keyfile.key", TestUtil.getSdPath("key"));
TestUtil.extractKey(getContext(), "binary.key", TestUtil.getSdPath("key-binary"));
}
public void testComposite() throws IOException, InvalidDBException {
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open("keyfile.kdbx", AssetManager.ACCESS_STREAMING);
/*
TODO Test
ImporterV4 importer = new ImporterV4();
importer.openDatabase(is, "12345", TestUtil.getKeyFileInputStream(ctx, TestUtil.getSdPath("key")));
is.close();
*/
}
public void testCompositeBinary() throws IOException, InvalidDBException {
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open("keyfile-binary.kdbx", AssetManager.ACCESS_STREAMING);
/*
TODO Test
ImporterV4 importer = new ImporterV4();
importer.openDatabase(is, "12345", TestUtil.getKeyFileInputStream(ctx,TestUtil.getSdPath("key-binary")));
is.close();
*/
}
public void testKeyfile() throws IOException, InvalidDBException {
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open("key-only.kdbx", AssetManager.ACCESS_STREAMING);
/*
TODO Test
ImporterV4 importer = new ImporterV4();
importer.openDatabase(is, "", TestUtil.getKeyFileInputStream(ctx, TestUtil.getSdPath("key")));
is.close();
*/
}
public void testNoGzip() throws IOException, InvalidDBException {
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open("no-encrypt.kdbx", AssetManager.ACCESS_STREAMING);
/*
TODO Test
ImporterV4 importer = new ImporterV4();
importer.openDatabase(is, "12345", null);
is.close();
*/
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.database;
import android.content.Context;
import android.content.res.AssetManager;
import android.test.AndroidTestCase;
import java.io.InputStream;
public class Kdb4Header extends AndroidTestCase {
public void testReadHeader() throws Exception {
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING);
/*
TODO Test
ImporterV4 importer = new ImporterV4();
PwDatabaseV4 db = importer.openDatabase(is, "12345", null);
assertEquals(6000, db.getNumberKeyEncryptionRounds());
assertTrue(db.getDataCipher().equals(AesEngine.CIPHER_UUID));
is.close();
*/
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.database;
import android.content.Context;
import android.content.res.AssetManager;
import android.test.AndroidTestCase;
import com.kunzisoft.keepass.database.element.PwDatabaseV4;
import com.kunzisoft.keepass.database.element.SprEngineV4;
import java.io.InputStream;
public class SprEngineTest extends AndroidTestCase {
private PwDatabaseV4 db;
private SprEngineV4 spr;
@Override
protected void setUp() throws Exception {
super.setUp();
Context ctx = getContext();
AssetManager am = ctx.getAssets();
InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING);
/*
TODO Test
ImporterV4 importer = new ImporterV4();
db = importer.openDatabase(is, "12345", null);
is.close();
spr = new SprEngineV4();
*/
}
private final String REF = "{REF:P@I:2B1D56590D961F48A8CE8C392CE6CD35}";
private final String ENCODE_UUID = "IN7RkON49Ui1UZ2ddqmLcw==";
private final String RESULT = "Password";
public void testRefReplace() {
/*
TODO TEST
UUID entryUUID = decodeUUID(ENCODE_UUID);
PwEntryV4 entry = (PwEntryV4) db.getEntryById(entryUUID);
assertEquals(RESULT, spr.compile(REF, entry, db));
*/
}
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.database;
import com.kunzisoft.keepass.database.element.Database;
public class TestData {
private static final String TEST1_KEYFILE = "";
private static final String TEST1_KDB = "test1.kdb";
private static final String TEST1_PASSWORD = "12345";
private static Database mDb1;
/*
public static Database GetDb1(Context ctx) throws Exception {
return GetDb1(ctx, false);
}
public static Database GetDb1(Context ctx, boolean forceReload) throws Exception {
if ( mDb1 == null || forceReload ) {
mDb1 = GetDb(ctx, TEST1_KDB, TEST1_PASSWORD, TEST1_KEYFILE, "/sdcard/test1.kdb");
}
return mDb1;
}
public static Database GetDb(Context ctx, String asset, String password, String keyfile, String filename) throws Exception {
AssetManager am = ctx.getAssets();
InputStream is = am.open(asset, AssetManager.ACCESS_STREAMING);
Database Db = new Database();
InputStream keyIs = TestUtil.getKeyFileInputStream(ctx, keyfile);
Db.loadData(ctx, is, password, keyIs, Importer.DEBUG);
Uri.Builder b = new Uri.Builder();
Db.setUri(b.scheme("file").path(filename).build());
return Db;
}
public static PwDatabaseV3Debug GetTest1(Context ctx) throws Exception {
if ( mDb1 == null ) {
GetDb1(ctx);
}
//return (PwDatabaseV3Debug) mDb1.getPwDatabase();
return null;
}
*/
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.output;
import android.test.AndroidTestCase;
public class PwManagerOutputTest extends AndroidTestCase {
// PwDatabaseV3Debug mPM;
/*
@Override
protected void setUp() throws Exception {
super.setUp();
mPM = TestData.GetTest1(getContext());
}
public void testPlainContent() throws IOException, PwDbOutputException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PwDbV3Output pos = new PwDbV3OutputDebug(mPM, bos, true);
pos.outputPlanGroupAndEntries(bos);
assertTrue("No output", bos.toByteArray().length > 0);
assertArrayEquals("Group and entry output doesn't match.", mPM.getPostHeader(), bos.toByteArray());
}
public void testChecksum() throws NoSuchAlgorithmException, IOException, PwDbOutputException {
//FileOutputStream fos = new FileOutputStream("/dev/null");
NullOutputStream nos = new NullOutputStream();
MessageDigest md = MessageDigest.getInstance("SHA-256");
DigestOutputStream dos = new DigestOutputStream(nos, md);
PwDbV3Output pos = new PwDbV3OutputDebug(mPM, dos, true);
pos.outputPlanGroupAndEntries(dos);
dos.close();
byte[] digest = md.digest();
assertTrue("No output", digest.length > 0);
assertArrayEquals("Hash of groups and entries failed.", mPM.getDbHeader().contentsHash, digest);
}
private void assertHeadersEquals(PwDbHeaderV3 expected, PwDbHeaderV3 actual) {
assertEquals("Flags unequal", expected.flags, actual.flags);
assertEquals("Entries unequal", expected.numEntries, actual.numEntries);
assertEquals("Groups unequal", expected.numGroups, actual.numGroups);
assertEquals("Key Rounds unequal", expected.numKeyEncRounds, actual.numKeyEncRounds);
assertEquals("Signature1 unequal", expected.signature1, actual.signature1);
assertEquals("Signature2 unequal", expected.signature2, actual.signature2);
assertTrue("Version incompatible", PwDbHeaderV3.compatibleHeaders(expected.version, actual.version));
assertArrayEquals("Hash unequal", expected.contentsHash, actual.contentsHash);
assertArrayEquals("IV unequal", expected.encryptionIV, actual.encryptionIV);
assertArrayEquals("Seed unequal", expected.masterSeed, actual.masterSeed);
assertArrayEquals("Seed2 unequal", expected.transformSeed, actual.transformSeed);
}
public void testHeader() throws PwDbOutputException, IOException {
ByteArrayOutputStream bActual = new ByteArrayOutputStream();
PwDbV3Output pActual = new PwDbV3OutputDebug(mPM, bActual, true);
PwDbHeaderV3 header = pActual.outputHeader(bActual);
ByteArrayOutputStream bExpected = new ByteArrayOutputStream();
PwDbHeaderOutputV3 outExpected = new PwDbHeaderOutputV3(mPM.getDbHeader(), bExpected);
outExpected.output();
assertHeadersEquals(mPM.getDbHeader(), header);
assertTrue("No output", bActual.toByteArray().length > 0);
assertArrayEquals("Header does not match.", bExpected.toByteArray(), bActual.toByteArray());
}
public void testFinalKey() throws PwDbOutputException {
ByteArrayOutputStream bActual = new ByteArrayOutputStream();
PwDbV3Output pActual = new PwDbV3OutputDebug(mPM, bActual, true);
PwDbHeader hActual = pActual.outputHeader(bActual);
byte[] finalKey = pActual.getFinalKey(hActual);
assertArrayEquals("Keys mismatched", mPM.getFinalKey(), finalKey);
}
public void testFullWrite() throws IOException, PwDbOutputException {
AssetManager am = getContext().getAssets();
InputStream is = am.open("test1.kdb");
// Pull file into byte array (for streaming fun)
ByteArrayOutputStream bExpected = new ByteArrayOutputStream();
while (true) {
int data = is.read();
if ( data == -1 ) {
break;
}
bExpected.write(data);
}
ByteArrayOutputStream bActual = new ByteArrayOutputStream();
PwDbV3Output pActual = new PwDbV3OutputDebug(mPM, bActual, true);
pActual.output();
//pActual.close();
FileOutputStream fos = new FileOutputStream(TestUtil.getSdPath("test1_out.kdb"));
fos.write(bActual.toByteArray());
fos.close();
assertArrayEquals("Databases do not match.", bExpected.toByteArray(), bActual.toByteArray());
}
*/
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.search;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.test.AndroidTestCase;
import com.kunzisoft.keepass.database.element.Database;
import com.kunzisoft.keepass.database.element.GroupVersioned;
public class SearchTest extends AndroidTestCase {
private Database mDb;
@Override
protected void setUp() throws Exception {
super.setUp();
//mDb = TestData.GetDb1(getContext(), true);
}
public void testSearch() {
GroupVersioned results = mDb.search("Amazon");
//assertTrue("Search result not found.", results.numbersOfChildEntries() > 0);
}
public void testBackupIncluded() {
updateOmitSetting(false);
GroupVersioned results = mDb.search("BackupOnly");
//assertTrue("Search result not found.", results.numbersOfChildEntries() > 0);
}
public void testBackupExcluded() {
updateOmitSetting(true);
GroupVersioned results = mDb.search("BackupOnly");
//assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0);
}
private void updateOmitSetting(boolean setting) {
Context ctx = getContext();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("settings_omitbackup_key", setting);
editor.commit();
}
}

View File

@@ -1,110 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.stream;
import static org.junit.Assert.assertArrayEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Random;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import junit.framework.TestCase;
import com.kunzisoft.keepass.stream.HashedBlockInputStream;
import com.kunzisoft.keepass.stream.HashedBlockOutputStream;
public class HashedBlock extends TestCase {
private static Random rand = new Random();
public void testBlockAligned() throws IOException {
testSize(1024, 1024);
}
public void testOffset() throws IOException {
testSize(1500, 1024);
}
private void testSize(int blockSize, int bufferSize) throws IOException {
byte[] orig = new byte[blockSize];
rand.nextBytes(orig);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
HashedBlockOutputStream output = new HashedBlockOutputStream(bos, bufferSize);
output.write(orig);
output.close();
byte[] encoded = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(encoded);
HashedBlockInputStream input = new HashedBlockInputStream(bis);
ByteArrayOutputStream decoded = new ByteArrayOutputStream();
while ( true ) {
byte[] buf = new byte[1024];
int read = input.read(buf);
if ( read == -1 ) {
break;
}
decoded.write(buf, 0, read);
}
byte[] out = decoded.toByteArray();
assertArrayEquals(orig, out);
}
public void testGZIPStream() throws IOException {
final int testLength = 32000;
byte[] orig = new byte[testLength];
rand.nextBytes(orig);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
HashedBlockOutputStream hos = new HashedBlockOutputStream(bos);
GZIPOutputStream zos = new GZIPOutputStream(hos);
zos.write(orig);
zos.close();
byte[] compressed = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(compressed);
HashedBlockInputStream his = new HashedBlockInputStream(bis);
GZIPInputStream zis = new GZIPInputStream(his);
byte[] uncompressed = new byte[testLength];
int read = 0;
while (read != -1 && testLength - read > 0) {
read += zis.read(uncompressed, read, testLength - read);
}
assertArrayEquals("Output not equal to input", orig, uncompressed);
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2017 Brian Pellin, 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.tests.stream
import org.junit.Assert.assertArrayEquals
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.util.Random
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import junit.framework.TestCase
import com.kunzisoft.keepass.stream.HashedBlockInputStream
import com.kunzisoft.keepass.stream.HashedBlockOutputStream
class HashedBlockTest : TestCase() {
@Throws(IOException::class)
fun testBlockAligned() {
testSize(1024, 1024)
}
@Throws(IOException::class)
fun testOffset() {
testSize(1500, 1024)
}
@Throws(IOException::class)
private fun testSize(blockSize: Int, bufferSize: Int) {
val orig = ByteArray(blockSize)
rand.nextBytes(orig)
val bos = ByteArrayOutputStream()
val output = HashedBlockOutputStream(bos, bufferSize)
output.write(orig)
output.close()
val encoded = bos.toByteArray()
val bis = ByteArrayInputStream(encoded)
val input = HashedBlockInputStream(bis)
val decoded = ByteArrayOutputStream()
while (true) {
val buf = ByteArray(1024)
val read = input.read(buf)
if (read == -1) {
break
}
decoded.write(buf, 0, read)
}
val out = decoded.toByteArray()
assertArrayEquals(orig, out)
}
@Throws(IOException::class)
fun testGZIPStream() {
val testLength = 32000
val orig = ByteArray(testLength)
rand.nextBytes(orig)
val bos = ByteArrayOutputStream()
val hos = HashedBlockOutputStream(bos)
val zos = GZIPOutputStream(hos)
zos.write(orig)
zos.close()
val compressed = bos.toByteArray()
val bis = ByteArrayInputStream(compressed)
val his = HashedBlockInputStream(bis)
val zis = GZIPInputStream(his)
val uncompressed = ByteArray(testLength)
var read = 0
while (read != -1 && testLength - read > 0) {
read += zis.read(uncompressed, read, testLength - read)
}
assertArrayEquals("Output not equal to input", orig, uncompressed)
}
companion object {
private val rand = Random()
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.utils;
import java.util.Locale;
import com.kunzisoft.keepass.utils.StringUtil;
import junit.framework.TestCase;
public class StringUtilTest extends TestCase {
private final String text = "AbCdEfGhIj";
private final String search = "BcDe";
private final String badSearch = "Ed";
public void testIndexOfIgnoreCase1() {
assertEquals(1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH));
}
public void testIndexOfIgnoreCase2() {
assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH), 2);
}
public void testIndexOfIgnoreCase3() {
assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, badSearch, Locale.ENGLISH));
}
private final String repText = "AbCtestingaBc";
private final String repSearch = "ABc";
private final String repSearchBad = "CCCCCC";
private final String repNew = "12345";
private final String repResult = "12345testing12345";
public void testReplaceAllIgnoresCase1() {
assertEquals(repResult, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearch, repNew, Locale.ENGLISH));
}
public void testReplaceAllIgnoresCase2() {
assertEquals(repText, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearchBad, repNew, Locale.ENGLISH));
}
}

View File

@@ -0,0 +1,28 @@
package com.kunzisoft.keepass.tests.utils
import com.kunzisoft.keepass.utils.UnsignedInt
import junit.framework.TestCase
class UnsignedIntTest: TestCase() {
fun testUInt() {
val standardInt = UnsignedInt(15).toInt()
assertEquals(15, standardInt)
val unsignedInt = UnsignedInt(-1).toLong()
assertEquals(4294967295L, unsignedInt)
}
fun testMaxValue() {
val maxValue = UnsignedInt.MAX_VALUE.toLong()
assertEquals(4294967295L, maxValue)
val longValue = UnsignedInt.fromLong(4294967295L).toLong()
assertEquals(longValue, maxValue)
}
fun testLong() {
val longValue = UnsignedInt.fromLong(50L).toInt()
assertEquals(50, longValue)
val uIntLongValue = UnsignedInt.fromLong(4294967290).toLong()
assertEquals(4294967290, uIntLongValue)
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2019 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.tests.utils
import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.stream.*
import com.kunzisoft.keepass.utils.UnsignedInt
import com.kunzisoft.keepass.utils.UnsignedLong
import junit.framework.TestCase
import org.junit.Assert.assertArrayEquals
import java.io.ByteArrayOutputStream
import java.util.*
class ValuesTest : TestCase() {
fun testReadWriteLongZero() {
testReadWriteLong(0.toByte())
}
fun testReadWriteLongMax() {
testReadWriteLong(java.lang.Byte.MAX_VALUE)
}
fun testReadWriteLongMin() {
testReadWriteLong(java.lang.Byte.MIN_VALUE)
}
fun testReadWriteLongRnd() {
val rnd = Random()
val buf = ByteArray(1)
rnd.nextBytes(buf)
testReadWriteLong(buf[0])
}
private fun testReadWriteLong(value: Byte) {
val orig = ByteArray(8)
setArray(orig, value, 8)
assertArrayEquals(orig, longTo8Bytes(bytes64ToLong(orig)))
}
fun testReadWriteIntZero() {
testReadWriteInt(0.toByte())
}
fun testReadWriteIntMin() {
testReadWriteInt(java.lang.Byte.MIN_VALUE)
}
fun testReadWriteIntMax() {
testReadWriteInt(java.lang.Byte.MAX_VALUE)
}
private fun testReadWriteInt(value: Byte) {
val orig = ByteArray(4)
for (i in 0..3) {
orig[i] = 0
}
setArray(orig, value, 4)
val one = bytes4ToUInt(orig)
val dest = uIntTo4Bytes(one)
assertArrayEquals(orig, dest)
}
private fun setArray(buf: ByteArray, value: Byte, size: Int) {
for (i in 0 until size) {
buf[i] = value
}
}
fun testReadWriteShortOne() {
val orig = ByteArray(2)
orig[0] = 0
orig[1] = 1
val one = bytes2ToUShort(orig)
val dest = uShortTo2Bytes(one)
assertArrayEquals(orig, dest)
}
fun testReadWriteShortMin() {
testReadWriteShort(java.lang.Byte.MIN_VALUE)
}
fun testReadWriteShortMax() {
testReadWriteShort(java.lang.Byte.MAX_VALUE)
}
private fun testReadWriteShort(value: Byte) {
val orig = ByteArray(2)
setArray(orig, value, 2)
val one = bytes2ToUShort(orig)
val dest = uShortTo2Bytes(one)
assertArrayEquals(orig, dest)
}
fun testReadWriteByteZero() {
testReadWriteByte(0.toByte())
}
fun testReadWriteByteMin() {
testReadWriteByte(java.lang.Byte.MIN_VALUE)
}
fun testReadWriteByteMax() {
testReadWriteShort(java.lang.Byte.MAX_VALUE)
}
private fun testReadWriteByte(value: Byte) {
val dest: Byte = UnsignedInt(UnsignedInt.fromByte(value)).toByte()
assert(value == dest)
}
fun testDate() {
val cal = Calendar.getInstance()
val expected = Calendar.getInstance()
expected.set(2008, 1, 2, 3, 4, 5)
val actual = Calendar.getInstance()
dateTo5Bytes(expected.time, cal)?.let { buf ->
actual.time = bytes5ToDate(buf, cal).date
}
val jDate = DateInstant(System.currentTimeMillis())
val intermediate = DateInstant(jDate)
val cDate = bytes5ToDate(dateTo5Bytes(intermediate.date)!!)
assertEquals("Year mismatch: ", 2008, actual.get(Calendar.YEAR))
assertEquals("Month mismatch: ", 1, actual.get(Calendar.MONTH))
assertEquals("Day mismatch: ", 2, actual.get(Calendar.DAY_OF_MONTH))
assertEquals("Hour mismatch: ", 3, actual.get(Calendar.HOUR_OF_DAY))
assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE))
assertEquals("Second mismatch: ", 5, actual.get(Calendar.SECOND))
assertTrue("jDate and intermediate not equal", jDate == intermediate)
assertTrue("jDate $jDate and cDate $cDate not equal", cDate == jDate)
}
fun testUUID() {
val bUUID = ByteArray(16)
Random().nextBytes(bUUID)
val uuid = bytes16ToUuid(bUUID)
val eUUID = uuidTo16Bytes(uuid)
val lUUID = bytes16ToUuid(bUUID)
val leUUID = uuidTo16Bytes(lUUID)
assertArrayEquals("UUID match failed", bUUID, eUUID)
assertArrayEquals("UUID match failed", bUUID, leUUID)
}
@Throws(Exception::class)
fun testULongMax() {
val ulongBytes = ByteArray(8)
for (i in ulongBytes.indices) {
ulongBytes[i] = -1
}
val bos = ByteArrayOutputStream()
val leos = LittleEndianDataOutputStream(bos)
leos.writeLong(UnsignedLong.MAX_VALUE)
leos.close()
val uLongMax = bos.toByteArray()
assertArrayEquals(ulongBytes, uLongMax)
}
}

View File

@@ -8,9 +8,14 @@
android:normalScreens="true" android:normalScreens="true"
android:largeScreens="true" android:largeScreens="true"
android:anyDensity="true" /> android:anyDensity="true" />
<uses-permission android:name="android.permission.USE_FINGERPRINT"/> <uses-permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission
android:name="android.permission.USE_BIOMETRIC" />
<uses-permission
android:name="android.permission.VIBRATE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application <application
android:label="@string/app_name" android:label="@string/app_name"
@@ -18,14 +23,15 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:name="com.kunzisoft.keepass.app.App" android:name="com.kunzisoft.keepass.app.App"
android:allowBackup="true" android:allowBackup="true"
android:fullBackupContent="@xml/backup" android:fullBackupContent="@xml/backup_rules"
android:backupAgent="com.kunzisoft.keepass.backup.SettingsBackupAgent" android:backupAgent="com.kunzisoft.keepass.backup.SettingsBackupAgent"
android:theme="@style/KeepassDXStyle.Night"> android:largeHeap="true"
<!-- TODO backup API Key --> android:resizeableActivity="true"
android:theme="@style/KeepassDXStyle.Night"
tools:targetApi="n">
<meta-data <meta-data
android:name="com.google.android.backup.api_key" android:name="com.google.android.backup.api_key"
android:value="" /> android:value="${googleAndroidBackupAPIKey}" />
<activity <activity
android:name="com.kunzisoft.keepass.activities.FileDatabaseSelectActivity" android:name="com.kunzisoft.keepass.activities.FileDatabaseSelectActivity"
android:theme="@style/KeepassDXStyle.SplashScreen" android:theme="@style/KeepassDXStyle.SplashScreen"
@@ -48,7 +54,7 @@
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" /> <data android:scheme="file" />
<data android:scheme="content" /> <data android:scheme="content" />
<data android:mimeType="application/octet-stream" /> <data android:mimeType="*/*" />
<data android:host="*" /> <data android:host="*" />
<data android:pathPattern=".*\\.kdb" /> <data android:pathPattern=".*\\.kdb" />
<data android:pathPattern=".*\\..*\\.kdb" /> <data android:pathPattern=".*\\..*\\.kdb" />
@@ -71,29 +77,28 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" /> <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" /> <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
</intent-filter> </intent-filter>
<intent-filter tools:ignore="AppLinkUrlError"> <intent-filter>
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/> <category android:name="android.intent.category.BROWSABLE"/>
<data android:mimeType="application/octet-stream"/> <data android:scheme="file" />
</intent-filter> <data android:scheme="content" />
</activity> <data android:mimeType="application/octet-stream" />
<!-- Folder picker --> <data android:mimeType="application/x-kdb" />
<provider <data android:mimeType="application/x-kdbx" />
android:name="android.support.v4.content.FileProvider" <data android:mimeType="application/x-keepass" />
android:authorities="${applicationId}.provider" <data android:host="*" />
android:exported="false" <data android:pathPattern=".*" />
android:grantUriPermissions="true"> <data android:pathPattern=".*\\.*" />
<meta-data <data android:pathPattern=".*\\..*\\.*" />
android:name="android.support.FILE_PROVIDER_PATHS" <data android:pathPattern=".*\\..*\\..*\\.*" />
android:resource="@xml/nnf_provider_paths" /> <data android:pathPattern=".*\\..*\\..*\\..*\\.*" />
</provider> <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.*" />
<activity <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.*" />
android:name=".activities.stylish.FilePickerStylishActivity" <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.*" />
android:label="@string/app_name"> <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.*" />
<intent-filter> <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.*" />
<action android:name="android.intent.action.GET_CONTENT" /> <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.*" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Main Activity --> <!-- Main Activity -->
@@ -125,26 +130,35 @@
<activity <activity
android:name="com.kunzisoft.keepass.activities.AboutActivity" android:name="com.kunzisoft.keepass.activities.AboutActivity"
android:launchMode="singleTask" android:launchMode="singleTask"
android:label="@string/menu_about" /> android:label="@string/about" />
<activity android:name="com.kunzisoft.keepass.settings.SettingsActivity" /> <activity android:name="com.kunzisoft.keepass.settings.SettingsActivity" />
<activity android:name="com.kunzisoft.keepass.autofill.AutofillLauncherActivity" <activity android:name="com.kunzisoft.keepass.autofill.AutofillLauncherActivity"
android:configChanges="keyboardHidden" /> android:configChanges="keyboardHidden" />
<activity android:name="com.kunzisoft.keepass.settings.SettingsAutofillActivity" /> <activity android:name="com.kunzisoft.keepass.settings.SettingsAdvancedUnlockActivity" />
<activity android:name="com.kunzisoft.keepass.settings.AutofillSettingsActivity" />
<activity android:name="com.kunzisoft.keepass.magikeyboard.KeyboardLauncherActivity" <activity android:name="com.kunzisoft.keepass.magikeyboard.KeyboardLauncherActivity"
android:label="@string/keyboard_name"> android:label="@string/keyboard_name"
android:exported="true">
</activity> </activity>
<activity android:name="com.kunzisoft.keepass.settings.MagikIMESettings" <activity android:name="com.kunzisoft.keepass.settings.MagikeyboardSettingsActivity"
android:label="@string/keyboard_setting_label"> android:label="@string/keyboard_setting_label">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
</intent-filter> </intent-filter>
</activity> </activity>
<service
android:name="com.kunzisoft.keepass.notifications.DatabaseOpenNotificationService"
android:enabled="true"
android:exported="false" />
<service <service
android:name="com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService" android:name="com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService"
android:enabled="true" android:enabled="true"
android:exported="false" /> android:exported="false" />
<service
android:name=".notifications.AttachmentFileNotificationService"
android:enabled="true"
android:exported="false" />
<service <service
android:name="com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService" android:name="com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService"
android:enabled="true" android:enabled="true"

Binary file not shown.

Binary file not shown.

View File

@@ -1,2 +0,0 @@
v7<EFBFBD><07>gx<67><78><EFBFBD>"<04>Dm<44>]tIWRP<52>g<18>y<15>/˰1<CBB0><31><13>X <0B><>fW[<5B>F%<25><1E>\<5C>up4
<EFBFBD><EFBFBD>-t;<3B>z<EFBFBD>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<KeyFile>
<Meta>
<Version>1.00</Version>
</Meta>
<Key>
<Data>zaTWphVNtRbspnwkqjy8FGTy5IqCUx9+FNb5H+VdB24=</Data>
</Key>
</KeyFile>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,35 +1,35 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities package com.kunzisoft.keepass.activities
import android.content.pm.PackageManager.NameNotFoundException import android.content.pm.PackageManager.NameNotFoundException
import android.os.Bundle import android.os.Bundle
import android.support.v7.widget.Toolbar import android.text.method.LinkMovementMethod
import android.util.Log import android.util.Log
import android.view.MenuItem import android.view.MenuItem
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.core.text.HtmlCompat
import com.kunzisoft.keepass.BuildConfig import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.activities.stylish.StylishActivity
import org.joda.time.DateTime import org.joda.time.DateTime
class AboutActivity : StylishActivity() { class AboutActivity : StylishActivity() {
@@ -40,7 +40,7 @@ class AboutActivity : StylishActivity() {
setContentView(R.layout.activity_about) setContentView(R.layout.activity_about)
val toolbar = findViewById<Toolbar>(R.id.toolbar) val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.title = getString(R.string.menu_about) toolbar.title = getString(R.string.about)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
@@ -64,9 +64,17 @@ class AboutActivity : StylishActivity() {
val buildTextView = findViewById<TextView>(R.id.activity_about_build) val buildTextView = findViewById<TextView>(R.id.activity_about_build)
buildTextView.text = build buildTextView.text = build
findViewById<TextView>(R.id.activity_about_licence_text).apply {
movementMethod = LinkMovementMethod.getInstance()
text = HtmlCompat.fromHtml(getString(R.string.html_about_licence, DateTime().year),
HtmlCompat.FROM_HTML_MODE_LEGACY)
}
val disclaimerText = findViewById<TextView>(R.id.disclaimer) findViewById<TextView>(R.id.activity_about_contribution_text).apply {
disclaimerText.text = getString(R.string.disclaimer_formal, DateTime().year) movementMethod = LinkMovementMethod.getInstance()
text = HtmlCompat.fromHtml(getString(R.string.html_about_contribution),
HtmlCompat.FROM_HTML_MODE_LEGACY)
}
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {

View File

@@ -1,68 +1,93 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.kunzisoft.keepass.activities package com.kunzisoft.keepass.activities
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.support.design.widget.CollapsingToolbarLayout
import android.support.v7.app.AlertDialog
import android.support.v7.widget.Toolbar
import android.util.Log import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.lock.LockingHideActivity import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.EntryVersioned import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.PwNodeId import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.education.EntryActivityEducation import com.kunzisoft.keepass.education.EntryActivityEducation
import com.kunzisoft.keepass.icons.assignDatabaseIcon import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.magikeyboard.MagikIME import com.kunzisoft.keepass.magikeyboard.MagikIME
import com.kunzisoft.keepass.model.AttachmentState
import com.kunzisoft.keepass.model.EntryAttachment
import com.kunzisoft.keepass.notifications.AttachmentFileNotificationService
import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_ENTRY_HISTORY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RESTORE_ENTRY_HISTORY
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.settings.PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager
import com.kunzisoft.keepass.settings.SettingsAutofillActivity
import com.kunzisoft.keepass.timeout.ClipboardHelper import com.kunzisoft.keepass.timeout.ClipboardHelper
import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.Util import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.createDocument
import com.kunzisoft.keepass.utils.onCreateDocumentResult
import com.kunzisoft.keepass.view.EntryContentsView import com.kunzisoft.keepass.view.EntryContentsView
import com.kunzisoft.keepass.view.showActionError
import java.util.*
import kotlin.collections.HashMap
class EntryActivity : LockingHideActivity() { class EntryActivity : LockingActivity() {
private var coordinatorLayout: CoordinatorLayout? = null
private var collapsingToolbarLayout: CollapsingToolbarLayout? = null private var collapsingToolbarLayout: CollapsingToolbarLayout? = null
private var titleIconView: ImageView? = null private var titleIconView: ImageView? = null
private var historyView: View? = null
private var entryContentsView: EntryContentsView? = null private var entryContentsView: EntryContentsView? = null
private var entryProgress: ProgressBar? = null
private var lockView: View? = null
private var toolbar: Toolbar? = null private var toolbar: Toolbar? = null
private var mEntry: EntryVersioned? = null private var mDatabase: Database? = null
private var mEntry: Entry? = null
private var mIsHistory: Boolean = false
private var mEntryLastVersion: Entry? = null
private var mEntryHistoryPosition: Int = -1
private var mShowPassword: Boolean = false private var mShowPassword: Boolean = false
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
private var mAttachmentsToDownload: HashMap<Int, EntryAttachment> = HashMap()
private var clipboardHelper: ClipboardHelper? = null private var clipboardHelper: ClipboardHelper? = null
private var firstLaunchOfActivity: Boolean = false private var firstLaunchOfActivity: Boolean = false
@@ -78,19 +103,81 @@ class EntryActivity : LockingHideActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
val currentDatabase = Database.getInstance() mDatabase = Database.getInstance()
readOnly = currentDatabase.isReadOnly || readOnly mReadOnly = mDatabase!!.isReadOnly || mReadOnly
mShowPassword = !PreferencesUtil.isPasswordMask(this) mShowPassword = !PreferencesUtil.isPasswordMask(this)
// Retrieve the textColor to tint the icon
val taIconColor = theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
iconColor = taIconColor.getColor(0, Color.BLACK)
taIconColor.recycle()
// Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set
invalidateOptionsMenu()
// Get views
coordinatorLayout = findViewById(R.id.toolbar_coordinator)
collapsingToolbarLayout = findViewById(R.id.toolbar_layout)
titleIconView = findViewById(R.id.entry_icon)
historyView = findViewById(R.id.history_container)
entryContentsView = findViewById(R.id.entry_contents)
entryContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this))
entryProgress = findViewById(R.id.entry_progress)
lockView = findViewById(R.id.lock_button)
lockView?.setOnClickListener {
lockAndExit()
}
// Init the clipboard helper
clipboardHelper = ClipboardHelper(this)
firstLaunchOfActivity = true
// Init attachment service binder manager
mAttachmentFileBinderManager = AttachmentFileBinderManager(this)
mProgressDialogThread?.onActionFinish = { actionTask, result ->
when (actionTask) {
ACTION_DATABASE_RESTORE_ENTRY_HISTORY,
ACTION_DATABASE_DELETE_ENTRY_HISTORY -> {
// Close the current activity after an history action
if (result.isSuccess)
finish()
}
}
coordinatorLayout?.showActionError(result)
}
}
override fun onResume() {
super.onResume()
// Show the lock button
lockView?.visibility = if (PreferencesUtil.showLockDatabaseButton(this)) {
View.VISIBLE
} else {
View.GONE
}
// Get Entry from UUID // Get Entry from UUID
try { try {
val keyEntry: PwNodeId<*> = intent.getParcelableExtra(KEY_ENTRY) val keyEntry: NodeId<UUID>? = intent.getParcelableExtra(KEY_ENTRY)
mEntry = currentDatabase.getEntryById(keyEntry) if (keyEntry != null) {
mEntry = mDatabase?.getEntryById(keyEntry)
mEntryLastVersion = mEntry
}
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
Log.e(TAG, "Unable to retrieve the entry key") Log.e(TAG, "Unable to retrieve the entry key")
} }
val historyPosition = intent.getIntExtra(KEY_ENTRY_HISTORY_POSITION, mEntryHistoryPosition)
mEntryHistoryPosition = historyPosition
if (historyPosition >= 0) {
mIsHistory = true
mEntry = mEntry?.getHistory()?.get(historyPosition)
}
if (mEntry == null) { if (mEntry == null) {
Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show()
finish() finish()
@@ -100,28 +187,6 @@ class EntryActivity : LockingHideActivity() {
// Update last access time. // Update last access time.
mEntry?.touch(modified = false, touchParents = false) mEntry?.touch(modified = false, touchParents = false)
// Retrieve the textColor to tint the icon
val taIconColor = theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse))
iconColor = taIconColor.getColor(0, Color.WHITE)
taIconColor.recycle()
// Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set
invalidateOptionsMenu()
// Get views
collapsingToolbarLayout = findViewById(R.id.toolbar_layout)
titleIconView = findViewById(R.id.entry_icon)
entryContentsView = findViewById(R.id.entry_contents)
entryContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this))
// Init the clipboard helper
clipboardHelper = ClipboardHelper(this)
firstLaunchOfActivity = true
}
override fun onResume() {
super.onResume()
mEntry?.let { entry -> mEntry?.let { entry ->
// Fill data in resume to update from EntryEditActivity // Fill data in resume to update from EntryEditActivity
fillEntryDataInContentsView(entry) fillEntryDataInContentsView(entry)
@@ -141,10 +206,25 @@ class EntryActivity : LockingHideActivity() {
} }
} }
mAttachmentFileBinderManager?.apply {
registerProgressTask()
onActionTaskListener = object : AttachmentFileNotificationService.ActionTaskListener {
override fun onAttachmentProgress(fileUri: Uri, attachment: EntryAttachment) {
entryContentsView?.updateAttachmentDownloadProgress(attachment)
}
}
}
firstLaunchOfActivity = false firstLaunchOfActivity = false
} }
private fun fillEntryDataInContentsView(entry: EntryVersioned) { override fun onPause() {
mAttachmentFileBinderManager?.unregisterProgressTask()
super.onPause()
}
private fun fillEntryDataInContentsView(entry: Entry) {
val database = Database.getInstance() val database = Database.getInstance()
database.startManageEntry(entry) database.startManageEntry(entry)
@@ -152,95 +232,164 @@ class EntryActivity : LockingHideActivity() {
titleIconView?.assignDatabaseIcon(database.drawFactory, entry.icon, iconColor) titleIconView?.assignDatabaseIcon(database.drawFactory, entry.icon, iconColor)
// Assign title text // Assign title text
val entryTitle = entry.getVisualTitle() val entryTitle = entry.title
collapsingToolbarLayout?.title = entryTitle collapsingToolbarLayout?.title = entryTitle
toolbar?.title = entryTitle toolbar?.title = entryTitle
// Assign basic fields // Assign basic fields
entryContentsView?.assignUserName(entry.username) entryContentsView?.assignUserName(entry.username)
entryContentsView?.assignUserNameCopyListener(View.OnClickListener { entryContentsView?.assignUserNameCopyListener(View.OnClickListener {
database.startManageEntry(entry)
clipboardHelper?.timeoutCopyToClipboard(entry.username, clipboardHelper?.timeoutCopyToClipboard(entry.username,
getString(R.string.copy_field, getString(R.string.copy_field,
getString(R.string.entry_user_name))) getString(R.string.entry_user_name)))
database.stopManageEntry(entry)
}) })
val allowCopyPassword = PreferencesUtil.allowCopyPasswordAndProtectedFields(this) val isFirstTimeAskAllowCopyPasswordAndProtectedFields =
entryContentsView?.assignPassword(entry.password, allowCopyPassword) PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields(this)
if (allowCopyPassword) { val allowCopyPasswordAndProtectedFields =
PreferencesUtil.allowCopyPasswordAndProtectedFields(this)
val showWarningClipboardDialogOnClickListener = View.OnClickListener {
AlertDialog.Builder(this@EntryActivity)
.setMessage(getString(R.string.allow_copy_password_warning) +
"\n\n" +
getString(R.string.clipboard_warning))
.create().apply {
setButton(AlertDialog.BUTTON_POSITIVE, getText(R.string.enable)) { dialog, _ ->
PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, true)
dialog.dismiss()
fillEntryDataInContentsView(entry)
}
setButton(AlertDialog.BUTTON_NEGATIVE, getText(R.string.disable)) { dialog, _ ->
PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, false)
dialog.dismiss()
fillEntryDataInContentsView(entry)
}
show()
}
}
entryContentsView?.assignPassword(entry.password, allowCopyPasswordAndProtectedFields)
if (allowCopyPasswordAndProtectedFields) {
entryContentsView?.assignPasswordCopyListener(View.OnClickListener { entryContentsView?.assignPasswordCopyListener(View.OnClickListener {
database.startManageEntry(entry)
clipboardHelper?.timeoutCopyToClipboard(entry.password, clipboardHelper?.timeoutCopyToClipboard(entry.password,
getString(R.string.copy_field, getString(R.string.copy_field,
getString(R.string.entry_password))) getString(R.string.entry_password)))
database.stopManageEntry(entry)
}) })
} else { } else {
// If dialog not already shown // If dialog not already shown
if (isFirstTimeAskAllowCopyPasswordAndProtectedFields(this)) { if (isFirstTimeAskAllowCopyPasswordAndProtectedFields) {
entryContentsView?.assignPasswordCopyListener(View.OnClickListener { entryContentsView?.assignPasswordCopyListener(showWarningClipboardDialogOnClickListener)
val message = getString(R.string.allow_copy_password_warning) +
"\n\n" +
getString(R.string.clipboard_warning)
val warningDialog = AlertDialog.Builder(this@EntryActivity)
.setMessage(message).create()
warningDialog.setButton(AlertDialog.BUTTON_POSITIVE, getText(android.R.string.ok)
) { dialog, _ ->
PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, true)
dialog.dismiss()
fillEntryDataInContentsView(entry)
}
warningDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getText(android.R.string.cancel)
) { dialog, _ ->
PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, false)
dialog.dismiss()
fillEntryDataInContentsView(entry)
}
warningDialog.show()
})
} else { } else {
entryContentsView?.assignPasswordCopyListener(null) entryContentsView?.assignPasswordCopyListener(null)
} }
} }
//Assign OTP field
entryContentsView?.assignOtp(entry.getOtpElement(), entryProgress,
View.OnClickListener {
entry.getOtpElement()?.let { otpElement ->
clipboardHelper?.timeoutCopyToClipboard(
otpElement.token,
getString(R.string.copy_field, getString(R.string.entry_otp))
)
}
})
entryContentsView?.assignURL(entry.url) entryContentsView?.assignURL(entry.url)
entryContentsView?.setHiddenPasswordStyle(!mShowPassword)
entryContentsView?.assignComment(entry.notes) entryContentsView?.assignComment(entry.notes)
// Assign custom fields // Assign custom fields
if (entry.allowExtraFields()) { if (entry.allowCustomFields()) {
entryContentsView?.clearExtraFields() entryContentsView?.clearExtraFields()
entry.fields.doActionToAllCustomProtectedField { label, value -> for (element in entry.customFields.entries) {
val showAction = !value.isProtected || PreferencesUtil.allowCopyPasswordAndProtectedFields(this@EntryActivity) val label = element.key
entryContentsView?.addExtraField(label, value, showAction, View.OnClickListener { val value = element.value
clipboardHelper?.timeoutCopyToClipboard(
value.toString(), val allowCopyProtectedField = !value.isProtected || allowCopyPasswordAndProtectedFields
getString(R.string.copy_field, label) if (allowCopyProtectedField) {
) entryContentsView?.addExtraField(label, value, allowCopyProtectedField, View.OnClickListener {
}) clipboardHelper?.timeoutCopyToClipboard(
value.toString(),
getString(R.string.copy_field, label)
)
})
} else {
// If dialog not already shown
if (isFirstTimeAskAllowCopyPasswordAndProtectedFields) {
entryContentsView?.addExtraField(label, value, allowCopyProtectedField, showWarningClipboardDialogOnClickListener)
} else {
entryContentsView?.addExtraField(label, value, allowCopyProtectedField, null)
}
}
} }
} }
entryContentsView?.setHiddenPasswordStyle(!mShowPassword)
// Manage attachments
val attachments = entry.getAttachments()
val showAttachmentsView = attachments.isNotEmpty()
entryContentsView?.showAttachments(showAttachmentsView)
if (showAttachmentsView) {
entryContentsView?.assignAttachments(attachments)
entryContentsView?.onAttachmentClick { attachmentItem, _ ->
when (attachmentItem.downloadState) {
AttachmentState.NULL, AttachmentState.ERROR, AttachmentState.COMPLETE -> {
createDocument(this, attachmentItem.name)?.let { requestCode ->
mAttachmentsToDownload[requestCode] = attachmentItem
}
}
else -> {
// TODO Stop download
}
}
}
}
entryContentsView?.refreshAttachments()
// Assign dates // Assign dates
entry.creationTime.date?.let { entryContentsView?.assignCreationDate(entry.creationTime)
entryContentsView?.assignCreationDate(it) entryContentsView?.assignModificationDate(entry.lastModificationTime)
} entryContentsView?.assignLastAccessDate(entry.lastAccessTime)
entry.lastModificationTime.date?.let { entryContentsView?.setExpires(entry.isCurrentlyExpires)
entryContentsView?.assignModificationDate(it) if (entry.expires) {
} entryContentsView?.assignExpiresDate(entry.expiryTime)
entry.lastAccessTime.date?.let {
entryContentsView?.assignLastAccessDate(it)
}
val expires = entry.expiryTime.date
if (entry.isExpires && expires != null) {
entryContentsView?.assignExpiresDate(expires)
} else { } else {
entryContentsView?.assignExpiresDate(getString(R.string.never)) entryContentsView?.assignExpiresDate(getString(R.string.never))
} }
// Manage history
historyView?.visibility = if (mIsHistory) View.VISIBLE else View.GONE
if (mIsHistory) {
val taColorAccent = theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
collapsingToolbarLayout?.contentScrim = ColorDrawable(taColorAccent.getColor(0, Color.BLACK))
taColorAccent.recycle()
}
val entryHistory = entry.getHistory()
val showHistoryView = entryHistory.isNotEmpty()
entryContentsView?.showHistory(showHistoryView)
if (showHistoryView) {
entryContentsView?.assignHistory(entryHistory)
entryContentsView?.onHistoryClick { historyItem, position ->
launch(this, historyItem, mReadOnly, position)
}
}
entryContentsView?.refreshHistory()
// Assign special data
entryContentsView?.assignUUID(entry.nodeId.id)
database.stopManageEntry(entry) database.stopManageEntry(entry)
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
when (requestCode) { when (requestCode) {
EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE -> EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE ->
// Not directly get the entry from intent data but from database // Not directly get the entry from intent data but from database
@@ -248,6 +397,15 @@ class EntryActivity : LockingHideActivity() {
fillEntryDataInContentsView(it) fillEntryDataInContentsView(it)
} }
} }
onCreateDocumentResult(requestCode, resultCode, data) { createdFileUri ->
if (createdFileUri != null) {
mAttachmentsToDownload[requestCode]?.let { attachmentToDownload ->
mAttachmentFileBinderManager
?.startDownloadAttachment(createdFileUri, attachmentToDownload)
}
}
}
} }
private fun changeShowPasswordIcon(togglePassword: MenuItem?) { private fun changeShowPasswordIcon(togglePassword: MenuItem?) {
@@ -266,9 +424,12 @@ class EntryActivity : LockingHideActivity() {
val inflater = menuInflater val inflater = menuInflater
MenuUtil.contributionMenuInflater(inflater, menu) MenuUtil.contributionMenuInflater(inflater, menu)
inflater.inflate(R.menu.entry, menu) inflater.inflate(R.menu.entry, menu)
inflater.inflate(R.menu.database_lock, menu) inflater.inflate(R.menu.database, menu)
if (mIsHistory && !mReadOnly) {
if (readOnly) { inflater.inflate(R.menu.entry_history, menu)
}
if (mIsHistory || mReadOnly) {
menu.findItem(R.id.menu_save_database)?.isVisible = false
menu.findItem(R.id.menu_edit)?.isVisible = false menu.findItem(R.id.menu_edit)?.isVisible = false
} }
@@ -303,7 +464,7 @@ class EntryActivity : LockingHideActivity() {
private fun performedNextEducation(entryActivityEducation: EntryActivityEducation, private fun performedNextEducation(entryActivityEducation: EntryActivityEducation,
menu: Menu) { menu: Menu) {
if (entryContentsView?.isUserNamePresent == true val entryCopyEducationPerformed = entryContentsView?.isUserNamePresent == true
&& entryActivityEducation.checkAndPerformedEntryCopyEducation( && entryActivityEducation.checkAndPerformedEntryCopyEducation(
findViewById(R.id.entry_user_name_action_image), findViewById(R.id.entry_user_name_action_image),
{ {
@@ -312,40 +473,40 @@ class EntryActivity : LockingHideActivity() {
getString(R.string.entry_user_name))) getString(R.string.entry_user_name)))
}, },
{ {
// Launch autofill settings performedNextEducation(entryActivityEducation, menu)
startActivity(Intent(this@EntryActivity, SettingsAutofillActivity::class.java)) })
}))
else if (toolbar?.findViewById<View>(R.id.menu_edit) != null && entryActivityEducation.checkAndPerformedEntryEditEducation( if (!entryCopyEducationPerformed) {
toolbar!!.findViewById(R.id.menu_edit), // entryEditEducationPerformed
{ toolbar?.findViewById<View>(R.id.menu_edit) != null && entryActivityEducation.checkAndPerformedEntryEditEducation(
onOptionsItemSelected(menu.findItem(R.id.menu_edit)) toolbar!!.findViewById(R.id.menu_edit),
}, {
{ onOptionsItemSelected(menu.findItem(R.id.menu_edit))
// Open Keepass doc to create field references },
startActivity(Intent(Intent.ACTION_VIEW, {
Uri.parse(getString(R.string.field_references_url)))) performedNextEducation(entryActivityEducation, menu)
})) })
; }
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.menu_contribute -> return MenuUtil.onContributionItemSelected(this) R.id.menu_contribute -> {
MenuUtil.onContributionItemSelected(this)
return true
}
R.id.menu_toggle_pass -> { R.id.menu_toggle_pass -> {
mShowPassword = !mShowPassword mShowPassword = !mShowPassword
changeShowPasswordIcon(item) changeShowPasswordIcon(item)
entryContentsView?.setHiddenPasswordStyle(!mShowPassword) entryContentsView?.setHiddenPasswordStyle(!mShowPassword)
return true return true
} }
R.id.menu_edit -> { R.id.menu_edit -> {
mEntry?.let { mEntry?.let {
EntryEditActivity.launch(this@EntryActivity, it) EntryEditActivity.launch(this@EntryActivity, it)
} }
return true return true
} }
R.id.menu_goto_url -> { R.id.menu_goto_url -> {
var url: String = mEntry?.url ?: "" var url: String = mEntry?.url ?: ""
@@ -354,48 +515,56 @@ class EntryActivity : LockingHideActivity() {
url = "http://$url" url = "http://$url"
} }
try { UriUtil.gotoUrl(this, url)
Util.gotoUrl(this, url) return true
} catch (e: ActivityNotFoundException) { }
Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show() R.id.menu_restore_entry_history -> {
mEntryLastVersion?.let { mainEntry ->
mProgressDialogThread?.startDatabaseRestoreEntryHistory(
mainEntry,
mEntryHistoryPosition,
!mReadOnly && mAutoSaveEnable)
} }
return true
} }
R.id.menu_delete_entry_history -> {
R.id.menu_lock -> { mEntryLastVersion?.let { mainEntry ->
lockAndExit() mProgressDialogThread?.startDatabaseDeleteEntryHistory(
return true mainEntry,
mEntryHistoryPosition,
!mReadOnly && mAutoSaveEnable)
}
}
R.id.menu_save_database -> {
mProgressDialogThread?.startDatabaseSave(!mReadOnly)
} }
android.R.id.home -> finish() // close this activity and return to preview activity (if there is any) android.R.id.home -> finish() // close this activity and return to preview activity (if there is any)
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
override fun finish() { override fun finish() {
// Transit data in previous Activity after an update // Transit data in previous Activity after an update
/* Intent().apply {
TODO Slowdown when add entry as result putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntry)
Intent intent = new Intent(); setResult(EntryEditActivity.UPDATE_ENTRY_RESULT_CODE, this)
intent.putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntry); }
setResult(EntryEditActivity.UPDATE_ENTRY_RESULT_CODE, intent);
*/
super.finish() super.finish()
} }
companion object { companion object {
private val TAG = EntryActivity::class.java.name private val TAG = EntryActivity::class.java.name
const val KEY_ENTRY = "entry" const val KEY_ENTRY = "KEY_ENTRY"
const val KEY_ENTRY_HISTORY_POSITION = "KEY_ENTRY_HISTORY_POSITION"
fun launch(activity: Activity, pw: EntryVersioned, readOnly: Boolean) { fun launch(activity: Activity, entry: Entry, readOnly: Boolean, historyPosition: Int? = null) {
if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) {
val intent = Intent(activity, EntryActivity::class.java) val intent = Intent(activity, EntryActivity::class.java)
intent.putExtra(KEY_ENTRY, pw.nodeId) intent.putExtra(KEY_ENTRY, entry.nodeId)
ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly) ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly)
if (historyPosition != null)
intent.putExtra(KEY_ENTRY_HISTORY_POSITION, historyPosition)
activity.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE) activity.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE)
} }
} }

View File

@@ -1,67 +1,87 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.kunzisoft.keepass.activities package com.kunzisoft.keepass.activities
import android.app.Activity import android.app.Activity
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.support.v7.widget.Toolbar
import android.util.Log import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.ScrollView import android.widget.DatePicker
import android.widget.TimePicker
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.ActionMenuView
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.NestedScrollView
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.GeneratePasswordDialogFragment import com.kunzisoft.keepass.activities.dialogs.*
import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.lock.LockingHideActivity import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.action.ProgressDialogSaveDatabaseThread import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.database.action.node.ActionNodeValues import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.action.node.AddEntryRunnable import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.education.EntryEditActivityEducation import com.kunzisoft.keepass.education.EntryEditActivityEducation
import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
import com.kunzisoft.keepass.otp.OtpElement
import com.kunzisoft.keepass.otp.OtpEntryFields
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.view.EntryEditContentsView import com.kunzisoft.keepass.view.EntryEditContentsView
import com.kunzisoft.keepass.view.showActionError
import org.joda.time.DateTime
import java.util.*
class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPickerListener, GeneratePasswordDialogFragment.GeneratePasswordListener { class EntryEditActivity : LockingActivity(),
IconPickerDialogFragment.IconPickerListener,
GeneratePasswordDialogFragment.GeneratePasswordListener,
SetOTPDialogFragment.CreateOtpListener,
DatePickerDialog.OnDateSetListener,
TimePickerDialog.OnTimeSetListener {
private var mDatabase: Database? = null private var mDatabase: Database? = null
// Refs of an entry and group in database, are not modifiable // Refs of an entry and group in database, are not modifiable
private var mEntry: EntryVersioned? = null private var mEntry: Entry? = null
private var mParent: GroupVersioned? = null private var mParent: Group? = null
// New or copy of mEntry in the database to be modifiable // New or copy of mEntry in the database to be modifiable
private var mNewEntry: EntryVersioned? = null private var mNewEntry: Entry? = null
private var mIsNew: Boolean = false private var mIsNew: Boolean = false
// Views // Views
private var scrollView: ScrollView? = null private var coordinatorLayout: CoordinatorLayout? = null
private var scrollView: NestedScrollView? = null
private var entryEditContentsView: EntryEditContentsView? = null private var entryEditContentsView: EntryEditContentsView? = null
private var entryEditAddToolBar: ActionMenuView? = null
private var saveView: View? = null private var saveView: View? = null
private var lockView: View? = null
// Education // Education
private var entryEditActivityEducation: EntryEditActivityEducation? = null private var entryEditActivityEducation: EntryEditActivityEducation? = null
@@ -76,19 +96,40 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
coordinatorLayout = findViewById(R.id.entry_edit_coordinator_layout)
scrollView = findViewById(R.id.entry_edit_scroll) scrollView = findViewById(R.id.entry_edit_scroll)
scrollView?.scrollBarStyle = View.SCROLLBARS_INSIDE_INSET scrollView?.scrollBarStyle = View.SCROLLBARS_INSIDE_INSET
entryEditContentsView = findViewById(R.id.entry_edit_contents) entryEditContentsView = findViewById(R.id.entry_edit_contents)
entryEditContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this)) entryEditContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this))
entryEditContentsView?.onDateClickListener = View.OnClickListener {
entryEditContentsView?.expiresDate?.date?.let { expiresDate ->
val dateTime = DateTime(expiresDate)
val defaultYear = dateTime.year
val defaultMonth = dateTime.monthOfYear-1
val defaultDay = dateTime.dayOfMonth
DatePickerFragment.getInstance(defaultYear, defaultMonth, defaultDay)
.show(supportFragmentManager, "DatePickerFragment")
}
}
lockView = findViewById(R.id.lock_button)
lockView?.setOnClickListener {
lockAndExit()
}
// Focus view to reinitialize timeout // Focus view to reinitialize timeout
resetAppTimeoutWhenViewFocusedOrChanged(entryEditContentsView) resetAppTimeoutWhenViewFocusedOrChanged(entryEditContentsView)
stopService(Intent(this, ClipboardEntryNotificationService::class.java))
stopService(Intent(this, KeyboardEntryNotificationService::class.java))
// Likely the app has been killed exit the activity // Likely the app has been killed exit the activity
mDatabase = Database.getInstance() mDatabase = Database.getInstance()
// Entry is retrieve, it's an entry to update // Entry is retrieve, it's an entry to update
intent.getParcelableExtra<PwNodeId<*>>(KEY_ENTRY)?.let { intent.getParcelableExtra<NodeId<UUID>>(KEY_ENTRY)?.let {
mIsNew = false mIsNew = false
// Create an Entry copy to modify from the database entry // Create an Entry copy to modify from the database entry
mEntry = mDatabase?.getEntryById(it) mEntry = mDatabase?.getEntryById(it)
@@ -103,25 +144,27 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
} }
} }
// Retrieve the icon after an orientation change // Create the new entry from the current one
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_NEW_ENTRY)) { if (savedInstanceState == null
mNewEntry = savedInstanceState.getParcelable(KEY_NEW_ENTRY) as EntryVersioned || !savedInstanceState.containsKey(KEY_NEW_ENTRY)) {
} else {
mEntry?.let { entry -> mEntry?.let { entry ->
// Create a copy to modify // Create a copy to modify
mNewEntry = EntryVersioned(entry).also { newEntry -> mNewEntry = Entry(entry).also { newEntry ->
// WARNING Remove the parent to keep memory with parcelable // WARNING Remove the parent to keep memory with parcelable
newEntry.parent = null newEntry.removeParent()
} }
} }
} }
} }
// Parent is retrieve, it's a new entry to create // Parent is retrieve, it's a new entry to create
intent.getParcelableExtra<PwNodeId<*>>(KEY_PARENT)?.let { intent.getParcelableExtra<NodeId<*>>(KEY_PARENT)?.let {
mIsNew = true mIsNew = true
mNewEntry = mDatabase?.createEntry() // Create an empty new entry
if (savedInstanceState == null
|| !savedInstanceState.containsKey(KEY_NEW_ENTRY)) {
mNewEntry = mDatabase?.createEntry()
}
mParent = mDatabase?.getGroupById(it) mParent = mDatabase?.getGroupById(it)
// Add the default icon // Add the default icon
mDatabase?.drawFactory?.let { iconFactory -> mDatabase?.drawFactory?.let { iconFactory ->
@@ -129,6 +172,12 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
} }
} }
// Retrieve the new entry after an orientation change
if (savedInstanceState != null
&& savedInstanceState.containsKey(KEY_NEW_ENTRY)) {
mNewEntry = savedInstanceState.getParcelable(KEY_NEW_ENTRY)
}
// Close the activity if entry or parent can't be retrieve // Close the activity if entry or parent can't be retrieve
if (mNewEntry == null || mParent == null) { if (mNewEntry == null || mParent == null) {
finish() finish()
@@ -143,47 +192,73 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
// Add listener to the icon // Add listener to the icon
entryEditContentsView?.setOnIconViewClickListener { IconPickerDialogFragment.launch(this@EntryEditActivity) } entryEditContentsView?.setOnIconViewClickListener { IconPickerDialogFragment.launch(this@EntryEditActivity) }
// Generate password button // Bottom Bar
entryEditContentsView?.setOnPasswordGeneratorClickListener { openPasswordGenerator() } entryEditAddToolBar = findViewById(R.id.entry_edit_bottom_bar)
entryEditAddToolBar?.apply {
menuInflater.inflate(R.menu.entry_edit, menu)
menu.findItem(R.id.menu_add_field).apply {
val allowCustomField = mNewEntry?.allowCustomFields() == true
isEnabled = allowCustomField
isVisible = allowCustomField
}
menu.findItem(R.id.menu_add_otp).apply {
val allowOTP = mDatabase?.allowOTP == true
isEnabled = allowOTP
isVisible = allowOTP
}
setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.menu_generate_password -> {
openPasswordGenerator()
true
}
R.id.menu_add_field -> {
addNewCustomField()
true
}
R.id.menu_add_otp -> {
setupOTP()
true
}
else -> true
}
}
}
// Save button // Save button
saveView = findViewById(R.id.entry_edit_save) saveView = findViewById(R.id.entry_edit_validate)
saveView?.setOnClickListener { saveEntry() } saveView?.setOnClickListener { saveEntry() }
entryEditContentsView?.allowCustomField(mNewEntry?.allowExtraFields() == true) { addNewCustomField() }
// Verify the education views // Verify the education views
entryEditActivityEducation = EntryEditActivityEducation(this) entryEditActivityEducation = EntryEditActivityEducation(this)
entryEditActivityEducation?.let {
Handler().post { performedNextEducation(it) } // Create progress dialog
mProgressDialogThread?.onActionFinish = { actionTask, result ->
when (actionTask) {
ACTION_DATABASE_CREATE_ENTRY_TASK,
ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
if (result.isSuccess)
finish()
}
}
coordinatorLayout?.showActionError(result)
} }
} }
private fun performedNextEducation(entryEditActivityEducation: EntryEditActivityEducation) { override fun onResume() {
val passwordView = entryEditContentsView?.generatePasswordView super.onResume()
val addNewFieldView = entryEditContentsView?.addNewFieldView
if (passwordView != null lockView?.visibility = if (PreferencesUtil.showLockDatabaseButton(this)) {
&& entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation( View.VISIBLE
passwordView, } else {
{ View.GONE
openPasswordGenerator() }
},
{
performedNextEducation(entryEditActivityEducation)
}
))
else if (mNewEntry != null && mNewEntry!!.allowExtraFields() && !mNewEntry!!.containsCustomFields()
&& addNewFieldView != null && addNewFieldView.visibility == View.VISIBLE
&& entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
addNewFieldView,
{
addNewCustomField()
}))
;
} }
private fun populateViewsWithEntry(newEntry: EntryVersioned) { private fun populateViewsWithEntry(newEntry: Entry) {
// Don't start the field reference manager, we want to see the raw ref // Don't start the field reference manager, we want to see the raw ref
mDatabase?.stopManageEntry(newEntry) mDatabase?.stopManageEntry(newEntry)
@@ -193,30 +268,43 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
// Set info in view // Set info in view
entryEditContentsView?.apply { entryEditContentsView?.apply {
title = newEntry.title title = newEntry.title
username = newEntry.username username = if (mIsNew && newEntry.username.isEmpty())
mDatabase?.defaultUsername ?: ""
else
newEntry.username
url = newEntry.url url = newEntry.url
password = newEntry.password password = newEntry.password
expires = newEntry.expires
if (expires)
expiresDate = newEntry.expiryTime
notes = newEntry.notes notes = newEntry.notes
newEntry.fields.doActionToAllCustomProtectedField { key, value -> for (entry in newEntry.customFields.entries) {
addNewCustomField(key, value) post {
putCustomField(entry.key, entry.value)
}
} }
} }
} }
private fun populateEntryWithViews(newEntry: EntryVersioned) { private fun populateEntryWithViews(newEntry: Entry) {
mDatabase?.startManageEntry(newEntry) mDatabase?.startManageEntry(newEntry)
newEntry.apply { newEntry.apply {
// Build info from view // Build info from view
entryEditContentsView?.let { entryView -> entryEditContentsView?.let { entryView ->
removeAllFields()
title = entryView.title title = entryView.title
username = entryView.username username = entryView.username
url = entryView.url url = entryView.url
password = entryView.password password = entryView.password
notes = entryView.notes expires = entryView.expires
if (entryView.expires) {
expiryTime = entryView.expiresDate
}
notes = entryView. notes
entryView.customFields.forEach { customField -> entryView.customFields.forEach { customField ->
addExtraField(customField.name, customField.protectedValue) putExtraField(customField.name, customField.protectedValue)
} }
} }
} }
@@ -224,7 +312,7 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
mDatabase?.stopManageEntry(newEntry) mDatabase?.stopManageEntry(newEntry)
} }
private fun temporarilySaveAndShowSelectedIcon(icon: PwIcon) { private fun temporarilySaveAndShowSelectedIcon(icon: IconImage) {
mNewEntry?.icon = icon mNewEntry?.icon = icon
mDatabase?.drawFactory?.let { iconDrawFactory -> mDatabase?.drawFactory?.let { iconDrawFactory ->
entryEditContentsView?.setIcon(iconDrawFactory, icon) entryEditContentsView?.setIcon(iconDrawFactory, icon)
@@ -242,9 +330,14 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
* Add a new customized field view and scroll to bottom * Add a new customized field view and scroll to bottom
*/ */
private fun addNewCustomField() { private fun addNewCustomField() {
entryEditContentsView?.addNewCustomField() entryEditContentsView?.addEmptyCustomField()
// Scroll bottom }
scrollView?.post { scrollView?.fullScroll(ScrollView.FOCUS_DOWN) }
private fun setupOTP() {
// Retrieve the current otpElement if exists
// and open the dialog to set up the OTP
SetOTPDialogFragment.build(mEntry?.getOtpElement()?.otpModel)
.show(supportFragmentManager, "addOTPDialog")
} }
/** /**
@@ -255,47 +348,32 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
// Launch a validation and show the error if present // Launch a validation and show the error if present
if (entryEditContentsView?.isValid() == true) { if (entryEditContentsView?.isValid() == true) {
// Clone the entry // Clone the entry
mDatabase?.let { database -> mNewEntry?.let { newEntry ->
mNewEntry?.let { newEntry ->
// WARNING Add the parent previously deleted // WARNING Add the parent previously deleted
newEntry.parent = mEntry?.parent newEntry.parent = mEntry?.parent
// Build info // Build info
newEntry.lastAccessTime = PwDate() newEntry.lastAccessTime = DateInstant()
newEntry.lastModificationTime = PwDate() newEntry.lastModificationTime = DateInstant()
populateEntryWithViews(newEntry) populateEntryWithViews(newEntry)
// Open a progress dialog and save entry // Open a progress dialog and save entry
var actionRunnable: ActionRunnable? = null if (mIsNew) {
val afterActionNodeFinishRunnable = object : AfterActionNodeFinishRunnable() { mParent?.let { parent ->
override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) { mProgressDialogThread?.startDatabaseCreateEntry(
if (actionNodeValues.result.isSuccess) newEntry,
finish() parent,
} !mReadOnly && mAutoSaveEnable
)
} }
if (mIsNew) { } else {
mParent?.let { parent -> mEntry?.let { oldEntry ->
actionRunnable = AddEntryRunnable(this@EntryEditActivity, mProgressDialogThread?.startDatabaseUpdateEntry(
database, oldEntry,
newEntry, newEntry,
parent, !mReadOnly && mAutoSaveEnable
afterActionNodeFinishRunnable, )
!readOnly)
}
} else {
mEntry?.let { oldEntry ->
actionRunnable = UpdateEntryRunnable(this@EntryEditActivity,
database,
oldEntry,
newEntry,
afterActionNodeFinishRunnable,
!readOnly)
}
}
actionRunnable?.let { runnable ->
ProgressDialogSaveDatabaseThread(this@EntryEditActivity) { runnable }.start()
} }
} }
} }
@@ -306,35 +384,125 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
super.onCreateOptionsMenu(menu) super.onCreateOptionsMenu(menu)
val inflater = menuInflater val inflater = menuInflater
inflater.inflate(R.menu.database_lock, menu) inflater.inflate(R.menu.database, menu)
// Save database not needed here
menu.findItem(R.id.menu_save_database)?.isVisible = false
MenuUtil.contributionMenuInflater(inflater, menu) MenuUtil.contributionMenuInflater(inflater, menu)
entryEditActivityEducation?.let {
Handler().post { performedNextEducation(it) }
}
return true return true
} }
private fun performedNextEducation(entryEditActivityEducation: EntryEditActivityEducation) {
val passwordGeneratorView: View? = entryEditAddToolBar?.findViewById(R.id.menu_generate_password)
val generatePasswordEducationPerformed = passwordGeneratorView != null
&& entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation(
passwordGeneratorView,
{
openPasswordGenerator()
},
{
performedNextEducation(entryEditActivityEducation)
}
)
if (!generatePasswordEducationPerformed) {
val addNewFieldView: View? = entryEditAddToolBar?.findViewById(R.id.menu_add_field)
val addNewFieldEducationPerformed = mNewEntry != null
&& mNewEntry!!.allowCustomFields() && mNewEntry!!.customFields.isEmpty()
&& addNewFieldView != null && addNewFieldView.visibility == View.VISIBLE
&& entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
addNewFieldView,
{
addNewCustomField()
},
{
performedNextEducation(entryEditActivityEducation)
}
)
if (!addNewFieldEducationPerformed) {
val setupOtpView: View? = entryEditAddToolBar?.findViewById(R.id.menu_add_otp)
setupOtpView != null && setupOtpView.visibility == View.VISIBLE
&& entryEditActivityEducation.checkAndPerformedSetUpOTPEducation(
setupOtpView,
{
setupOTP()
})
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.menu_lock -> { R.id.menu_save_database -> {
lockAndExit() mProgressDialogThread?.startDatabaseSave(!mReadOnly)
}
R.id.menu_contribute -> {
MenuUtil.onContributionItemSelected(this)
return true return true
} }
android.R.id.home -> {
R.id.menu_contribute -> return MenuUtil.onContributionItemSelected(this) onBackPressed()
}
android.R.id.home -> finish()
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
override fun onOtpCreated(otpElement: OtpElement) {
// Update the otp field with otpauth:// url
val otpField = OtpEntryFields.buildOtpField(otpElement,
mEntry?.title, mEntry?.username)
entryEditContentsView?.putCustomField(otpField.name, otpField.protectedValue)
mEntry?.putExtraField(otpField.name, otpField.protectedValue)
}
override fun iconPicked(bundle: Bundle) { override fun iconPicked(bundle: Bundle) {
IconPickerDialogFragment.getIconStandardFromBundle(bundle)?.let { icon -> IconPickerDialogFragment.getIconStandardFromBundle(bundle)?.let { icon ->
temporarilySaveAndShowSelectedIcon(icon) temporarilySaveAndShowSelectedIcon(icon)
} }
} }
override fun onDateSet(datePicker: DatePicker?, year: Int, month: Int, day: Int) {
// To fix android 4.4 issue
// https://stackoverflow.com/questions/12436073/datepicker-ondatechangedlistener-called-twice
if (datePicker?.isShown == true) {
entryEditContentsView?.expiresDate?.date?.let { expiresDate ->
// Save the date
entryEditContentsView?.expiresDate =
DateInstant(DateTime(expiresDate)
.withYear(year)
.withMonthOfYear(month + 1)
.withDayOfMonth(day)
.toDate())
// Launch the time picker
val dateTime = DateTime(expiresDate)
val defaultHour = dateTime.hourOfDay
val defaultMinute = dateTime.minuteOfHour
TimePickerFragment.getInstance(defaultHour, defaultMinute)
.show(supportFragmentManager, "TimePickerFragment")
}
}
}
override fun onTimeSet(timePicker: TimePicker?, hours: Int, minutes: Int) {
entryEditContentsView?.expiresDate?.date?.let { expiresDate ->
// Save the date
entryEditContentsView?.expiresDate =
DateInstant(DateTime(expiresDate)
.withHourOfDay(hours)
.withMinuteOfHour(minutes)
.toDate())
}
}
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
outState.putParcelable(KEY_NEW_ENTRY, mNewEntry) mNewEntry?.let {
populateEntryWithViews(it)
outState.putParcelable(KEY_NEW_ENTRY, it)
}
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
} }
@@ -353,6 +521,15 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
// Do nothing here // Do nothing here
} }
override fun onBackPressed() {
AlertDialog.Builder(this)
.setMessage(R.string.discard_changes)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.discard) { _, _ ->
super@EntryEditActivity.onBackPressed()
}.create().show()
}
override fun finish() { override fun finish() {
// Assign entry callback as a result in all case // Assign entry callback as a result in all case
try { try {
@@ -397,7 +574,7 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
* @param activity from activity * @param activity from activity
* @param pwEntry Entry to update * @param pwEntry Entry to update
*/ */
fun launch(activity: Activity, pwEntry: EntryVersioned) { fun launch(activity: Activity, pwEntry: Entry) {
if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) {
val intent = Intent(activity, EntryEditActivity::class.java) val intent = Intent(activity, EntryEditActivity::class.java)
intent.putExtra(KEY_ENTRY, pwEntry.nodeId) intent.putExtra(KEY_ENTRY, pwEntry.nodeId)
@@ -411,7 +588,7 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
* @param activity from activity * @param activity from activity
* @param pwGroup Group who will contains new entry * @param pwGroup Group who will contains new entry
*/ */
fun launch(activity: Activity, pwGroup: GroupVersioned) { fun launch(activity: Activity, pwGroup: Group) {
if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) {
val intent = Intent(activity, EntryEditActivity::class.java) val intent = Intent(activity, EntryEditActivity::class.java)
intent.putExtra(KEY_PARENT, pwGroup.nodeId) intent.putExtra(KEY_PARENT, pwGroup.nodeId)

View File

@@ -1,243 +1,206 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities package com.kunzisoft.keepass.activities
import android.Manifest import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.assist.AssistStructure import android.app.assist.AssistStructure
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.os.Handler import android.os.Handler
import android.preference.PreferenceManager
import android.support.annotation.RequiresApi
import android.support.v7.app.AlertDialog
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.Toolbar
import android.util.Log import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.EditText import androidx.annotation.RequiresApi
import android.widget.TextView import androidx.appcompat.widget.Toolbar
import android.widget.Toast import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.snackbar.Snackbar
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.dialogs.CreateFileDialogFragment
import com.kunzisoft.keepass.activities.dialogs.FileInformationDialogFragment
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.activities.helpers.KeyFileHelper import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable import com.kunzisoft.keepass.autofill.AutofillHelper.KEY_SEARCH_INFO
import com.kunzisoft.keepass.database.action.ProgressDialogThread import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
import com.kunzisoft.keepass.fileselect.DeleteFileHistoryAsyncTask import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.fileselect.FileDatabaseModel import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_TASK
import com.kunzisoft.keepass.fileselect.OpenFileHistoryAsyncTask
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
import com.kunzisoft.keepass.magikeyboard.KeyboardHelper
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.utils.*
import com.kunzisoft.keepass.utils.MenuUtil import com.kunzisoft.keepass.view.asError
import com.kunzisoft.keepass.utils.UriUtil import kotlinx.android.synthetic.main.activity_file_selection.*
import net.cachapa.expandablelayout.ExpandableLayout
import permissions.dispatcher.*
import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.IOException
import java.lang.ref.WeakReference
import java.net.URLDecoder
import java.util.*
@RuntimePermissions
class FileDatabaseSelectActivity : StylishActivity(), class FileDatabaseSelectActivity : StylishActivity(),
CreateFileDialogFragment.DefinePathDialogListener, AssignMasterKeyDialogFragment.AssignPasswordDialogListener {
AssignMasterKeyDialogFragment.AssignPasswordDialogListener,
FileDatabaseHistoryAdapter.FileItemOpenListener,
FileDatabaseHistoryAdapter.FileSelectClearListener,
FileDatabaseHistoryAdapter.FileInformationShowListener {
// Views // Views
private var fileListContainer: View? = null private var coordinatorLayout: CoordinatorLayout? = null
private var fileManagerExplanationButton: View? = null
private var createButtonView: View? = null private var createButtonView: View? = null
private var browseButtonView: View? = null private var openDatabaseButtonView: View? = null
private var openButtonView: View? = null
private var fileSelectExpandableButtonView: View? = null
private var fileSelectExpandableLayout: ExpandableLayout? = null
private var openFileNameView: EditText? = null
// Adapter to manage database history list // Adapter to manage database history list
private var mAdapterDatabaseHistory: FileDatabaseHistoryAdapter? = null private var mAdapterDatabaseHistory: FileDatabaseHistoryAdapter? = null
private var mFileDatabaseHistory: FileDatabaseHistory? = null private var mFileDatabaseHistoryAction: FileDatabaseHistoryAction? = null
private var mDatabaseFileUri: Uri? = null private var mDatabaseFileUri: Uri? = null
private var mKeyFileHelper: KeyFileHelper? = null private var mOpenFileHelper: OpenFileHelper? = null
private var mDefaultPath: String? = null private var mProgressDialogThread: ProgressDialogThread? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
mFileDatabaseHistory = FileDatabaseHistory.getInstance(WeakReference(applicationContext)) mFileDatabaseHistoryAction = FileDatabaseHistoryAction.getInstance(applicationContext)
setContentView(R.layout.activity_file_selection) setContentView(R.layout.activity_file_selection)
fileListContainer = findViewById(R.id.container_file_list) coordinatorLayout = findViewById(R.id.activity_file_selection_coordinator_layout)
val toolbar = findViewById<Toolbar>(R.id.toolbar) val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.title = "" toolbar.title = ""
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
openFileNameView = findViewById(R.id.file_filename) fileManagerExplanationButton = findViewById(R.id.file_manager_explanation_button)
fileManagerExplanationButton?.setOnClickListener {
// Set the initial value of the filename UriUtil.gotoUrl(this, R.string.file_manager_explanation_url)
mDefaultPath = (Environment.getExternalStorageDirectory().absolutePath
+ getString(R.string.database_file_path_default)
+ getString(R.string.database_file_name_default)
+ getString(R.string.database_file_extension_default))
openFileNameView?.setHint(R.string.open_link_database)
// Button to expand file selection
fileSelectExpandableButtonView = findViewById(R.id.file_select_expandable_button)
fileSelectExpandableLayout = findViewById(R.id.file_select_expandable)
fileSelectExpandableButtonView?.setOnClickListener { _ ->
if (fileSelectExpandableLayout?.isExpanded == true)
fileSelectExpandableLayout?.collapse()
else
fileSelectExpandableLayout?.expand()
}
// History list
val databaseFileListView = findViewById<RecyclerView>(R.id.file_list)
databaseFileListView.layoutManager = LinearLayoutManager(this)
// Open button
openButtonView = findViewById(R.id.open_database)
openButtonView?.setOnClickListener { _ ->
var fileName = openFileNameView?.text?.toString() ?: ""
mDefaultPath?.let {
if (fileName.isEmpty())
fileName = it
}
launchPasswordActivityWithPath(fileName)
} }
// Create button // Create button
createButtonView = findViewById(R.id.create_database) createButtonView = findViewById(R.id.create_database_button)
createButtonView?.setOnClickListener { openCreateFileDialogFragmentWithPermissionCheck() } if (allowCreateDocumentByStorageAccessFramework(packageManager)) {
// There is an activity which can handle this intent.
createButtonView?.visibility = View.VISIBLE
}
else{
// No Activity found that can handle this intent.
createButtonView?.visibility = View.GONE
}
mKeyFileHelper = KeyFileHelper(this) createButtonView?.setOnClickListener { createNewFile() }
browseButtonView = findViewById(R.id.browse_button)
browseButtonView?.setOnClickListener(mKeyFileHelper!!.getOpenFileOnClickViewListener {
Uri.parse("file://" + openFileNameView!!.text.toString())
})
mOpenFileHelper = OpenFileHelper(this)
openDatabaseButtonView = findViewById(R.id.open_keyfile_button)
openDatabaseButtonView?.apply {
mOpenFileHelper?.openFileOnClickViewListener?.let {
setOnClickListener(it)
setOnLongClickListener(it)
}
}
// History list
val fileDatabaseHistoryRecyclerView = findViewById<RecyclerView>(R.id.file_list)
fileDatabaseHistoryRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
// Removes blinks
(fileDatabaseHistoryRecyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
// Construct adapter with listeners // Construct adapter with listeners
mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this@FileDatabaseSelectActivity, mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this)
mFileDatabaseHistory?.databaseUriList ?: ArrayList()) mAdapterDatabaseHistory?.setOnFileDatabaseHistoryOpenListener { fileDatabaseHistoryEntityToOpen ->
mAdapterDatabaseHistory?.setOnItemClickListener(this) UriUtil.parse(fileDatabaseHistoryEntityToOpen.databaseUri)?.let { databaseFileUri ->
mAdapterDatabaseHistory?.setFileSelectClearListener(this) launchPasswordActivity(
mAdapterDatabaseHistory?.setFileInformationShowListener(this) databaseFileUri,
databaseFileListView.adapter = mAdapterDatabaseHistory UriUtil.parse(fileDatabaseHistoryEntityToOpen.keyFileUri))
}
}
mAdapterDatabaseHistory?.setOnFileDatabaseHistoryDeleteListener { fileDatabaseHistoryToDelete ->
// Remove from app database
mFileDatabaseHistoryAction?.deleteFileDatabaseHistory(fileDatabaseHistoryToDelete) { fileHistoryDeleted ->
// Remove from adapter
fileHistoryDeleted?.let { databaseFileHistoryDeleted ->
mAdapterDatabaseHistory?.deleteDatabaseFileHistory(databaseFileHistoryDeleted)
mAdapterDatabaseHistory?.notifyDataSetChanged()
}
}
true
}
mAdapterDatabaseHistory?.setOnSaveAliasListener { fileDatabaseHistoryWithNewAlias ->
mFileDatabaseHistoryAction?.addOrUpdateFileDatabaseHistory(fileDatabaseHistoryWithNewAlias)
}
fileDatabaseHistoryRecyclerView.adapter = mAdapterDatabaseHistory
// Load default database if not an orientation change // Load default database if not an orientation change
if (!(savedInstanceState != null if (!(savedInstanceState != null
&& savedInstanceState.containsKey(EXTRA_STAY) && savedInstanceState.containsKey(EXTRA_STAY)
&& savedInstanceState.getBoolean(EXTRA_STAY, false))) { && savedInstanceState.getBoolean(EXTRA_STAY, false))) {
val prefs = PreferenceManager.getDefaultSharedPreferences(this) val databasePath = PreferencesUtil.getDefaultDatabasePath(this)
val fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, "")
if (fileName != null && fileName.isNotEmpty()) { UriUtil.parse(databasePath)?.let { databaseFileUri ->
val dbUri = UriUtil.parseUriFile(fileName) launchPasswordActivityWithPath(databaseFileUri)
var scheme: String? = null } ?: run {
if (dbUri != null) Log.i(TAG, "Unable to launch Password Activity")
scheme = dbUri.scheme
if (scheme != null && scheme.isNotEmpty() && scheme.equals("file", ignoreCase = true)) {
val path = dbUri!!.path
val db = File(path!!)
if (db.exists()) {
launchPasswordActivityWithPath(path)
}
} else {
if (dbUri != null)
launchPasswordActivityWithPath(dbUri.toString())
}
} }
} }
Handler().post { performedNextEducation(FileDatabaseSelectActivityEducation(this)) } // Retrieve the database URI provided by file manager after an orientation change
if (savedInstanceState != null
&& savedInstanceState.containsKey(EXTRA_DATABASE_URI)) {
mDatabaseFileUri = savedInstanceState.getParcelable(EXTRA_DATABASE_URI)
}
// Attach the dialog thread to this activity
mProgressDialogThread = ProgressDialogThread(this).apply {
onActionFinish = { actionTask, _ ->
when (actionTask) {
ACTION_DATABASE_CREATE_TASK -> {
GroupActivity.launch(this@FileDatabaseSelectActivity)
}
}
}
}
} }
private fun performedNextEducation(fileDatabaseSelectActivityEducation: FileDatabaseSelectActivityEducation) { /**
// If no recent files * Create a new file by calling the content provider
if (createButtonView != null */
&& mFileDatabaseHistory != null @SuppressLint("InlinedApi")
&& !mFileDatabaseHistory!!.hasRecentFiles() && fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation( private fun createNewFile() {
createButtonView!!, createDocument(this, getString(R.string.database_file_name_default) +
{ getString(R.string.database_file_extension_default), "application/x-keepass")
openCreateFileDialogFragmentWithPermissionCheck()
},
{
// But if the user cancel, it can also select a database
performedNextEducation(fileDatabaseSelectActivityEducation)
}))
else if (browseButtonView != null
&& fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation(
browseButtonView!!,
{tapTargetView ->
tapTargetView?.let {
mKeyFileHelper?.openFileOnClickViewListener?.onClick(it)
}
},
{
fileSelectExpandableButtonView?.let {
fileDatabaseSelectActivityEducation
.checkAndPerformedOpenLinkDatabaseEducation(it)
}
}
))
;
} }
private fun fileNoFoundAction(e: FileNotFoundException) { private fun fileNoFoundAction(e: FileNotFoundException) {
val error = getString(R.string.file_not_found_content) val error = getString(R.string.file_not_found_content)
Toast.makeText(this@FileDatabaseSelectActivity, coordinatorLayout?.let {
error, Toast.LENGTH_LONG).show() Snackbar.make(it, error, Snackbar.LENGTH_LONG).asError().show()
}
Log.e(TAG, error, e) Log.e(TAG, error, e)
} }
private fun launchPasswordActivity(fileName: String, keyFile: String) { private fun launchPasswordActivity(databaseUri: Uri, keyFile: Uri?) {
EntrySelectionHelper.doEntrySelectionAction(intent, EntrySelectionHelper.doEntrySelectionAction(intent,
{ {
try { try {
PasswordActivity.launch(this@FileDatabaseSelectActivity, PasswordActivity.launch(this@FileDatabaseSelectActivity,
fileName, keyFile) databaseUri, keyFile)
} catch (e: FileNotFoundException) { } catch (e: FileNotFoundException) {
fileNoFoundAction(e) fileNoFoundAction(e)
} }
@@ -245,7 +208,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
{ {
try { try {
PasswordActivity.launchForKeyboardResult(this@FileDatabaseSelectActivity, PasswordActivity.launchForKeyboardResult(this@FileDatabaseSelectActivity,
fileName, keyFile) databaseUri, keyFile)
finish() finish()
} catch (e: FileNotFoundException) { } catch (e: FileNotFoundException) {
fileNoFoundAction(e) fileNoFoundAction(e)
@@ -255,8 +218,9 @@ class FileDatabaseSelectActivity : StylishActivity(),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try { try {
PasswordActivity.launchForAutofillResult(this@FileDatabaseSelectActivity, PasswordActivity.launchForAutofillResult(this@FileDatabaseSelectActivity,
fileName, keyFile, databaseUri, keyFile,
assistStructure) assistStructure,
intent.getParcelableExtra(KEY_SEARCH_INFO))
} catch (e: FileNotFoundException) { } catch (e: FileNotFoundException) {
fileNoFoundAction(e) fileNoFoundAction(e)
} }
@@ -265,139 +229,82 @@ class FileDatabaseSelectActivity : StylishActivity(),
}) })
} }
private fun launchPasswordActivityWithPath(path: String) { private fun launchGroupActivity(readOnly: Boolean) {
launchPasswordActivity(path, "") EntrySelectionHelper.doEntrySelectionAction(intent,
{
GroupActivity.launch(this@FileDatabaseSelectActivity,
readOnly)
},
{
GroupActivity.launchForKeyboardSelection(this@FileDatabaseSelectActivity,
readOnly)
// Do not keep history
finish()
},
{ assistStructure ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
GroupActivity.launchForAutofillResult(this@FileDatabaseSelectActivity,
assistStructure,
intent.getParcelableExtra(KEY_SEARCH_INFO),
readOnly)
}
})
}
private fun launchPasswordActivityWithPath(databaseUri: Uri) {
launchPasswordActivity(databaseUri, null)
// Delete flickering for kitkat <= // Delete flickering for kitkat <=
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
overridePendingTransition(0, 0) overridePendingTransition(0, 0)
} }
private fun updateExternalStorageWarning() {
// To show errors
var warning = -1
val state = Environment.getExternalStorageState()
if (state == Environment.MEDIA_MOUNTED_READ_ONLY) {
warning = R.string.read_only_warning
} else if (state != Environment.MEDIA_MOUNTED) {
warning = R.string.warning_unmounted
}
val labelWarningView = findViewById<TextView>(R.id.label_warning)
if (warning != -1) {
labelWarningView.setText(warning)
labelWarningView.visibility = View.VISIBLE
} else {
labelWarningView.visibility = View.INVISIBLE
}
}
override fun onResume() { override fun onResume() {
val database = Database.getInstance()
if (database.loaded) {
launchGroupActivity(database.isReadOnly)
}
super.onResume() super.onResume()
updateExternalStorageWarning() // Construct adapter with listeners
updateFileListVisibility() if (PreferencesUtil.showRecentFiles(this)) {
mAdapterDatabaseHistory!!.notifyDataSetChanged() mFileDatabaseHistoryAction?.getAllFileDatabaseHistories { databaseFileHistoryList ->
databaseFileHistoryList?.let { historyList ->
val hideBrokenLocations = PreferencesUtil.hideBrokenLocations(this@FileDatabaseSelectActivity)
mAdapterDatabaseHistory?.addDatabaseFileHistoryList(
// Show only uri accessible
historyList.filter {
if (hideBrokenLocations) {
FileDatabaseInfo(this@FileDatabaseSelectActivity,
it.databaseUri).exists
} else
true
})
mAdapterDatabaseHistory?.notifyDataSetChanged()
}
}
} else {
mAdapterDatabaseHistory?.clearDatabaseFileHistoryList()
mAdapterDatabaseHistory?.notifyDataSetChanged()
}
// Register progress task
mProgressDialogThread?.registerProgressTask()
}
override fun onPause() {
// Unregister progress task
mProgressDialogThread?.unregisterProgressTask()
super.onPause()
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
// only to keep the current activity // only to keep the current activity
outState.putBoolean(EXTRA_STAY, true) outState.putBoolean(EXTRA_STAY, true)
} // to retrieve the URI of a created database after an orientation change
outState.putParcelable(EXTRA_DATABASE_URI, mDatabaseFileUri)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// NOTE: delegate the permission handling to generated method
onRequestPermissionsResult(requestCode, grantResults)
}
@NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
fun openCreateFileDialogFragment() {
val createFileDialogFragment = CreateFileDialogFragment()
createFileDialogFragment.show(supportFragmentManager, "createFileDialogFragment")
}
private fun updateFileListVisibility() {
if (mAdapterDatabaseHistory?.itemCount == 0)
fileListContainer?.visibility = View.INVISIBLE
else
fileListContainer?.visibility = View.VISIBLE
}
/**
* Create file for database
* @return If not created, return false
*/
private fun createDatabaseFile(path: Uri): Boolean {
val pathString = URLDecoder.decode(path.path, "UTF-8")
// Make sure file name exists
if (pathString.isEmpty()) {
Log.e(TAG, getString(R.string.error_filename_required))
Toast.makeText(this@FileDatabaseSelectActivity,
R.string.error_filename_required,
Toast.LENGTH_LONG).show()
return false
}
// Try to create the file
val file = File(pathString)
try {
if (file.exists()) {
Log.e(TAG, getString(R.string.error_database_exists) + " " + file)
Toast.makeText(this@FileDatabaseSelectActivity,
R.string.error_database_exists,
Toast.LENGTH_LONG).show()
return false
}
val parent = file.parentFile
if (parent == null || parent.exists() && !parent.isDirectory) {
Log.e(TAG, getString(R.string.error_invalid_path) + " " + file)
Toast.makeText(this@FileDatabaseSelectActivity,
R.string.error_invalid_path,
Toast.LENGTH_LONG).show()
return false
}
if (!parent.exists()) {
// Create parent directory
if (!parent.mkdirs()) {
Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + parent)
Toast.makeText(this@FileDatabaseSelectActivity,
R.string.error_could_not_create_parent,
Toast.LENGTH_LONG).show()
return false
}
}
return file.createNewFile()
} catch (e: IOException) {
Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + e.localizedMessage)
e.printStackTrace()
Toast.makeText(
this@FileDatabaseSelectActivity,
getText(R.string.error_file_not_create).toString() + " "
+ e.localizedMessage,
Toast.LENGTH_LONG).show()
return false
}
}
override fun onDefinePathDialogPositiveClick(pathFile: Uri?): Boolean {
mDatabaseFileUri = pathFile
if (pathFile == null)
return false
return if (createDatabaseFile(pathFile)) {
AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog")
true
} else
false
}
override fun onDefinePathDialogNegativeClick(pathFile: Uri?): Boolean {
return true
} }
override fun onAssignKeyDialogPositiveClick( override fun onAssignKeyDialogPositiveClick(
@@ -405,52 +312,21 @@ class FileDatabaseSelectActivity : StylishActivity(),
keyFileChecked: Boolean, keyFile: Uri?) { keyFileChecked: Boolean, keyFile: Uri?) {
try { try {
UriUtil.parseUriFile(mDatabaseFileUri)?.let { databaseUri -> mDatabaseFileUri?.let { databaseUri ->
// Create the new database // Create the new database
ProgressDialogThread(this@FileDatabaseSelectActivity, mProgressDialogThread?.startDatabaseCreate(
{ databaseUri,
CreateDatabaseRunnable(this@FileDatabaseSelectActivity, masterPasswordChecked,
databaseUri, masterPassword,
Database.getInstance(), keyFileChecked,
masterPasswordChecked, keyFile
masterPassword, )
keyFileChecked,
keyFile,
true, // TODO get readonly
LaunchGroupActivityFinish(databaseUri)
)
},
R.string.progress_create)
.start()
} }
} catch (e: Exception) { } catch (e: Exception) {
val error = "Unable to create database with this password and key file" val error = getString(R.string.error_create_database_file)
Toast.makeText(this, error, Toast.LENGTH_LONG).show() Snackbar.make(activity_file_selection_coordinator_layout, error, Snackbar.LENGTH_LONG).asError().show()
Log.e(TAG, error + " " + e.message) Log.e(TAG, error, e)
// TODO remove
e.printStackTrace()
}
}
private inner class LaunchGroupActivityFinish internal constructor(private val fileURI: Uri) : ActionRunnable() {
override fun run() {
finishRun(true, null)
}
override fun onFinishRun(result: Result) {
runOnUiThread {
if (result.isSuccess) {
// Add database to recent files
mFileDatabaseHistory?.addDatabaseUri(fileURI)
mAdapterDatabaseHistory?.notifyDataSetChanged()
updateFileListVisibility()
GroupActivity.launch(this@FileDatabaseSelectActivity)
} else {
Log.e(TAG, "Unable to open the database")
}
}
} }
} }
@@ -460,29 +336,6 @@ class FileDatabaseSelectActivity : StylishActivity(),
} }
override fun onFileItemOpenListener(itemPosition: Int) {
OpenFileHistoryAsyncTask({ fileName, keyFile ->
if (fileName != null && keyFile != null)
launchPasswordActivity(fileName, keyFile)
updateFileListVisibility()
}, mFileDatabaseHistory).execute(itemPosition)
}
override fun onClickFileInformation(fileDatabaseModel: FileDatabaseModel) {
FileInformationDialogFragment.newInstance(fileDatabaseModel).show(supportFragmentManager, "fileInformation")
}
override fun onFileSelectClearListener(fileDatabaseModel: FileDatabaseModel): Boolean {
DeleteFileHistoryAsyncTask({
fileDatabaseModel.fileUri?.let {
mFileDatabaseHistory?.deleteDatabaseUri(it)
}
mAdapterDatabaseHistory?.notifyDataSetChanged()
updateFileListVisibility()
}, mFileDatabaseHistory, mAdapterDatabaseHistory).execute(fileDatabaseModel)
return true
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
@@ -490,44 +343,67 @@ class FileDatabaseSelectActivity : StylishActivity(),
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data) AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
} }
mKeyFileHelper?.onActivityResultCallback(requestCode, resultCode, data mOpenFileHelper?.onActivityResultCallback(requestCode, resultCode, data
) { uri -> ) { uri ->
if (uri != null) { if (uri != null) {
if (PreferencesUtil.autoOpenSelectedFile(this@FileDatabaseSelectActivity)) { launchPasswordActivityWithPath(uri)
launchPasswordActivityWithPath(uri.toString())
} else {
fileSelectExpandableLayout?.expand(false)
openFileNameView?.setText(uri.toString())
}
} }
} }
}
@OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) // Retrieve the created URI from the file manager
internal fun showRationaleForExternalStorage(request: PermissionRequest) { onCreateDocumentResult(requestCode, resultCode, data) { databaseFileCreatedUri ->
AlertDialog.Builder(this) mDatabaseFileUri = databaseFileCreatedUri
.setMessage(R.string.permission_external_storage_rationale_write_database) if (mDatabaseFileUri != null) {
.setPositiveButton(R.string.allow) { _, _ -> request.proceed() } AssignMasterKeyDialogFragment.getInstance(true)
.setNegativeButton(R.string.cancel) { _, _ -> request.cancel() } .show(supportFragmentManager, "passwordDialog")
.show() } else {
} val error = getString(R.string.error_create_database)
coordinatorLayout?.let {
@OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) Snackbar.make(it, error, Snackbar.LENGTH_LONG).asError().show()
internal fun showDeniedForExternalStorage() { }
Toast.makeText(this, R.string.permission_external_storage_denied, Toast.LENGTH_SHORT).show() Log.e(TAG, error)
} }
}
@OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE)
internal fun showNeverAskForExternalStorage() {
Toast.makeText(this, R.string.permission_external_storage_never_ask, Toast.LENGTH_SHORT).show()
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu) super.onCreateOptionsMenu(menu)
MenuUtil.defaultMenuInflater(menuInflater, menu) MenuUtil.defaultMenuInflater(menuInflater, menu)
Handler().post { performedNextEducation(FileDatabaseSelectActivityEducation(this)) }
return true return true
} }
private fun performedNextEducation(fileDatabaseSelectActivityEducation: FileDatabaseSelectActivityEducation) {
// If no recent files
val createDatabaseEducationPerformed = createButtonView != null && createButtonView!!.visibility == View.VISIBLE
&& mAdapterDatabaseHistory != null
&& mAdapterDatabaseHistory!!.itemCount > 0
&& fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation(
createButtonView!!,
{
createNewFile()
},
{
// But if the user cancel, it can also select a database
performedNextEducation(fileDatabaseSelectActivityEducation)
})
if (!createDatabaseEducationPerformed) {
// selectDatabaseEducationPerformed
openDatabaseButtonView != null
&& fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation(
openDatabaseButtonView!!,
{tapTargetView ->
tapTargetView?.let {
mOpenFileHelper?.openFileOnClickViewListener?.onClick(it)
}
},
{}
)
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return MenuUtil.onDefaultMenuOptionsItemSelected(this, item) && super.onOptionsItemSelected(item) return MenuUtil.onDefaultMenuOptionsItemSelected(this, item) && super.onOptionsItemSelected(item)
} }
@@ -536,6 +412,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
private const val TAG = "FileDbSelectActivity" private const val TAG = "FileDbSelectActivity"
private const val EXTRA_STAY = "EXTRA_STAY" private const val EXTRA_STAY = "EXTRA_STAY"
private const val EXTRA_DATABASE_URI = "EXTRA_DATABASE_URI"
/* /*
* ------------------------- * -------------------------
@@ -550,7 +427,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
*/ */
fun launchForKeyboardSelection(activity: Activity) { fun launchForKeyboardSelection(activity: Activity) {
KeyboardHelper.startActivityForKeyboardSelection(activity, Intent(activity, FileDatabaseSelectActivity::class.java)) EntrySelectionHelper.startActivityForEntrySelection(activity, Intent(activity, FileDatabaseSelectActivity::class.java))
} }
/* /*
@@ -560,10 +437,13 @@ class FileDatabaseSelectActivity : StylishActivity(),
*/ */
@RequiresApi(api = Build.VERSION_CODES.O) @RequiresApi(api = Build.VERSION_CODES.O)
fun launchForAutofillResult(activity: Activity, assistStructure: AssistStructure) { fun launchForAutofillResult(activity: Activity,
assistStructure: AssistStructure,
searchInfo: SearchInfo?) {
AutofillHelper.startActivityForAutofillResult(activity, AutofillHelper.startActivityForAutofillResult(activity,
Intent(activity, FileDatabaseSelectActivity::class.java), Intent(activity, FileDatabaseSelectActivity::class.java),
assistStructure) assistStructure,
searchInfo)
} }
} }
} }

View File

@@ -1,12 +1,29 @@
/*
* Copyright 2019 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.activities package com.kunzisoft.keepass.activities
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager
import android.support.v7.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView
import android.support.v7.widget.RecyclerView
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Menu import android.view.Menu
@@ -14,35 +31,42 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.adapters.NodeAdapter import com.kunzisoft.keepass.adapters.NodeAdapter
import com.kunzisoft.keepass.database.SortNodeEnum import com.kunzisoft.keepass.database.element.SortNodeEnum
import com.kunzisoft.keepass.database.element.GroupVersioned import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.NodeVersioned import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.activities.dialogs.SortDialogFragment import com.kunzisoft.keepass.activities.dialogs.SortDialogFragment
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.activities.stylish.StylishFragment import com.kunzisoft.keepass.activities.stylish.StylishFragment
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.node.Type
import java.util.*
class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionListener { class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionListener {
private var nodeClickCallback: NodeAdapter.NodeClickCallback? = null private var nodeClickListener: NodeClickListener? = null
private var nodeMenuListener: NodeAdapter.NodeMenuListener? = null
private var onScrollListener: OnScrollListener? = null private var onScrollListener: OnScrollListener? = null
private var listView: RecyclerView? = null private var mNodesRecyclerView: RecyclerView? = null
var mainGroup: GroupVersioned? = null var mainGroup: Group? = null
private set private set
private var mAdapter: NodeAdapter? = null private var mAdapter: NodeAdapter? = null
var nodeActionSelectionMode = false
private set
var nodeActionPasteMode: PasteMode = PasteMode.UNDEFINED
private set
private val listActionNodes = LinkedList<Node>()
private val listPasteNodes = LinkedList<Node>()
private var notFoundView: View? = null private var notFoundView: View? = null
private var isASearchResult: Boolean = false private var isASearchResult: Boolean = false
// Preferences for sorting
private var prefs: SharedPreferences? = null
private var readOnly: Boolean = false private var readOnly: Boolean = false
get() { get() {
@@ -53,31 +77,22 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
val isEmpty: Boolean val isEmpty: Boolean
get() = mAdapter == null || mAdapter?.itemCount?:0 <= 0 get() = mAdapter == null || mAdapter?.itemCount?:0 <= 0
override fun onAttach(context: Context?) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
try { try {
nodeClickCallback = context as NodeAdapter.NodeClickCallback? nodeClickListener = context as NodeClickListener
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
// The activity doesn't implement the interface, throw exception // The activity doesn't implement the interface, throw exception
throw ClassCastException(context?.toString() throw ClassCastException(context.toString()
+ " must implement " + NodeAdapter.NodeClickCallback::class.java.name) + " must implement " + NodeAdapter.NodeClickCallback::class.java.name)
} }
try { try {
nodeMenuListener = context as NodeAdapter.NodeMenuListener? onScrollListener = context as OnScrollListener
} catch (e: ClassCastException) {
nodeMenuListener = null
// Context menu can be omit
Log.w(TAG, context?.toString()
+ " must implement " + NodeAdapter.NodeMenuListener::class.java.name)
}
try {
onScrollListener = context as OnScrollListener?
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
onScrollListener = null onScrollListener = null
// Context menu can be omit // Context menu can be omit
Log.w(TAG, context?.toString() Log.w(TAG, context.toString()
+ " must implement " + RecyclerView.OnScrollListener::class.java.name) + " must implement " + RecyclerView.OnScrollListener::class.java.name)
} }
} }
@@ -85,32 +100,56 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
activity?.let { currentActivity -> setHasOptionsMenu(true)
setHasOptionsMenu(true)
readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, arguments) readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, arguments)
arguments?.let { args -> arguments?.let { args ->
// Contains all the group in element // Contains all the group in element
if (args.containsKey(GROUP_KEY)) { if (args.containsKey(GROUP_KEY)) {
mainGroup = args.getParcelable(GROUP_KEY) mainGroup = args.getParcelable(GROUP_KEY)
}
if (args.containsKey(IS_SEARCH)) {
isASearchResult = args.getBoolean(IS_SEARCH)
}
} }
if (args.containsKey(IS_SEARCH)) {
contextThemed?.let { context -> isASearchResult = args.getBoolean(IS_SEARCH)
mAdapter = NodeAdapter(context, currentActivity.menuInflater) }
mAdapter?.apply { }
setReadOnly(readOnly)
setIsASearchResult(isASearchResult) contextThemed?.let { context ->
setOnNodeClickListener(nodeClickCallback) mAdapter = NodeAdapter(context)
setActivateContextMenu(true) mAdapter?.apply {
setNodeMenuListener(nodeMenuListener) setOnNodeClickListener(object : NodeAdapter.NodeClickCallback {
} override fun onNodeClick(node: Node) {
if (nodeActionSelectionMode) {
if (listActionNodes.contains(node)) {
// Remove selected item if already selected
listActionNodes.remove(node)
} else {
// Add selected item if not already selected
listActionNodes.add(node)
}
nodeClickListener?.onNodeSelected(listActionNodes)
setActionNodes(listActionNodes)
notifyNodeChanged(node)
} else {
nodeClickListener?.onNodeClick(node)
}
}
override fun onNodeLongClick(node: Node): Boolean {
if (nodeActionPasteMode == PasteMode.UNDEFINED) {
// Select the first item after a long click
if (!listActionNodes.contains(node))
listActionNodes.add(node)
nodeClickListener?.onNodeSelected(listActionNodes)
setActionNodes(listActionNodes)
notifyNodeChanged(node)
}
return true
}
})
} }
prefs = PreferenceManager.getDefaultSharedPreferences(context)
} }
} }
@@ -125,20 +164,24 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
// To apply theme // To apply theme
val rootView = inflater.cloneInContext(contextThemed) val rootView = inflater.cloneInContext(contextThemed)
.inflate(R.layout.fragment_list_nodes, container, false) .inflate(R.layout.fragment_list_nodes, container, false)
listView = rootView.findViewById(R.id.nodes_list) mNodesRecyclerView = rootView.findViewById(R.id.nodes_list)
notFoundView = rootView.findViewById(R.id.not_found_container) notFoundView = rootView.findViewById(R.id.not_found_container)
mNodesRecyclerView?.apply {
scrollBarStyle = View.SCROLLBARS_INSIDE_INSET
layoutManager = LinearLayoutManager(context)
adapter = mAdapter
}
onScrollListener?.let { onScrollListener -> onScrollListener?.let { onScrollListener ->
listView?.addOnScrollListener(object : RecyclerView.OnScrollListener() { mNodesRecyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
onScrollListener.onScrolled(dy) onScrollListener.onScrolled(dy)
} }
}) })
} }
rebuildList()
return rootView return rootView
} }
@@ -148,20 +191,16 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
activity?.intent?.let { activity?.intent?.let {
selectionMode = EntrySelectionHelper.retrieveEntrySelectionModeFromIntent(it) selectionMode = EntrySelectionHelper.retrieveEntrySelectionModeFromIntent(it)
} }
// Force read only mode if selection mode
mAdapter?.apply {
setReadOnly(readOnly)
}
// Refresh data // Refresh data
mAdapter?.notifyDataSetChanged() rebuildList()
if (isASearchResult && mAdapter!= null && mAdapter!!.isEmpty) { if (isASearchResult && mAdapter!= null && mAdapter!!.isEmpty) {
// To show the " no search entry found " // To show the " no search entry found "
listView?.visibility = View.GONE mNodesRecyclerView?.visibility = View.GONE
notFoundView?.visibility = View.VISIBLE notFoundView?.visibility = View.VISIBLE
} else { } else {
listView?.visibility = View.VISIBLE mNodesRecyclerView?.visibility = View.VISIBLE
notFoundView?.visibility = View.GONE notFoundView?.visibility = View.GONE
} }
} }
@@ -169,46 +208,42 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
fun rebuildList() { fun rebuildList() {
// Add elements to the list // Add elements to the list
mainGroup?.let { mainGroup -> mainGroup?.let { mainGroup ->
mAdapter?.rebuildList(mainGroup) mAdapter?.apply {
} rebuildList(mainGroup)
listView?.apply { // To visually change the elements
scrollBarStyle = View.SCROLLBARS_INSIDE_INSET if (PreferencesUtil.APPEARANCE_CHANGED) {
layoutManager = LinearLayoutManager(context) notifyDataSetChanged()
adapter = mAdapter PreferencesUtil.APPEARANCE_CHANGED = false
}
}
} }
} }
override fun onSortSelected(sortNodeEnum: SortNodeEnum, ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean) { override fun onSortSelected(sortNodeEnum: SortNodeEnum,
// Toggle setting sortNodeParameters: SortNodeEnum.SortNodeParameters) {
prefs?.edit()?.apply { // Save setting
putString(getString(R.string.sort_node_key), sortNodeEnum.name) context?.let {
putBoolean(getString(R.string.sort_ascending_key), ascending) PreferencesUtil.saveNodeSort(it, sortNodeEnum, sortNodeParameters)
putBoolean(getString(R.string.sort_group_before_key), groupsBefore)
putBoolean(getString(R.string.sort_recycle_bin_bottom_key), recycleBinBottom)
apply()
} }
// Tell the adapter to refresh it's list // Tell the adapter to refresh it's list
mAdapter?.notifyChangeSort(sortNodeEnum, ascending, groupsBefore) mAdapter?.notifyChangeSort(sortNodeEnum, sortNodeParameters)
mainGroup?.let { mainGroup -> rebuildList()
mAdapter?.rebuildList(mainGroup)
}
} }
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater?.inflate(R.menu.tree, menu) inflater.inflate(R.menu.tree, menu)
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item?.itemId) { when (item.itemId) {
R.id.menu_sort -> { R.id.menu_sort -> {
context?.let { context -> context?.let { context ->
val sortDialogFragment: SortDialogFragment = val sortDialogFragment: SortDialogFragment =
if (Database.getInstance().isRecycleBinAvailable if (Database.getInstance().isRecycleBinEnabled) {
&& Database.getInstance().isRecycleBinEnabled) {
SortDialogFragment.getInstance( SortDialogFragment.getInstance(
PreferencesUtil.getListSort(context), PreferencesUtil.getListSort(context),
PreferencesUtil.getAscendingSort(context), PreferencesUtil.getAscendingSort(context),
@@ -230,6 +265,103 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
} }
} }
fun actionNodesCallback(nodes: List<Node>,
menuListener: NodesActionMenuListener?) : ActionMode.Callback {
return object : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
nodeActionSelectionMode = false
nodeActionPasteMode = PasteMode.UNDEFINED
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
menu?.clear()
if (nodeActionPasteMode != PasteMode.UNDEFINED) {
mode?.menuInflater?.inflate(R.menu.node_paste_menu, menu)
} else {
nodeActionSelectionMode = true
mode?.menuInflater?.inflate(R.menu.node_menu, menu)
val database = Database.getInstance()
// Open and Edit for a single item
if (nodes.size == 1) {
// Edition
if (readOnly
|| (database.isRecycleBinEnabled && nodes[0] == database.recycleBin)) {
menu?.removeItem(R.id.menu_edit)
}
} else {
menu?.removeItem(R.id.menu_open)
menu?.removeItem(R.id.menu_edit)
}
// Copy and Move (not for groups)
if (readOnly
|| isASearchResult
|| nodes.any { it.type == Type.GROUP }) {
// TODO Copy For Group
menu?.removeItem(R.id.menu_copy)
menu?.removeItem(R.id.menu_move)
}
// Deletion
if (readOnly
|| (database.isRecycleBinEnabled && nodes.any { it == database.recycleBin })) {
menu?.removeItem(R.id.menu_delete)
}
}
// Add the number of items selected in title
mode?.title = nodes.size.toString()
return true
}
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
if (menuListener == null)
return false
return when (item?.itemId) {
R.id.menu_open -> menuListener.onOpenMenuClick(nodes[0])
R.id.menu_edit -> menuListener.onEditMenuClick(nodes[0])
R.id.menu_copy -> {
nodeActionPasteMode = PasteMode.PASTE_FROM_COPY
mAdapter?.unselectActionNodes()
val returnValue = menuListener.onCopyMenuClick(nodes)
nodeActionSelectionMode = false
returnValue
}
R.id.menu_move -> {
nodeActionPasteMode = PasteMode.PASTE_FROM_MOVE
mAdapter?.unselectActionNodes()
val returnValue = menuListener.onMoveMenuClick(nodes)
nodeActionSelectionMode = false
returnValue
}
R.id.menu_delete -> menuListener.onDeleteMenuClick(nodes)
R.id.menu_paste -> {
val returnValue = menuListener.onPasteMenuClick(nodeActionPasteMode, nodes)
nodeActionPasteMode = PasteMode.UNDEFINED
nodeActionSelectionMode = false
returnValue
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
listActionNodes.clear()
listPasteNodes.clear()
mAdapter?.unselectActionNodes()
nodeActionPasteMode = PasteMode.UNDEFINED
nodeActionSelectionMode = false
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
@@ -237,33 +369,77 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE -> { EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE -> {
if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE
|| resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { || resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) {
data?.getParcelableExtra<NodeVersioned>(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY)?.let { newNode -> data?.getParcelableExtra<Node>(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY)?.let { changedNode ->
if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE) if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE)
mAdapter?.addNode(newNode) addNode(changedNode)
if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE)
//mAdapter.updateLastNodeRegister(newNode); mAdapter?.notifyDataSetChanged()
mainGroup?.let { mainGroup ->
mAdapter?.rebuildList(mainGroup)
}
}
} ?: Log.e(this.javaClass.name, "New node can be retrieve in Activity Result") } ?: Log.e(this.javaClass.name, "New node can be retrieve in Activity Result")
} }
} }
} }
} }
fun addNode(newNode: NodeVersioned) { fun contains(node: Node): Boolean {
return mAdapter?.contains(node) ?: false
}
fun addNode(newNode: Node) {
mAdapter?.addNode(newNode) mAdapter?.addNode(newNode)
} }
fun updateNode(oldNode: NodeVersioned, newNode: NodeVersioned) { fun addNodes(newNodes: List<Node>) {
mAdapter?.updateNode(oldNode, newNode) mAdapter?.addNodes(newNodes)
} }
fun removeNode(pwNode: NodeVersioned) { fun updateNode(oldNode: Node, newNode: Node? = null) {
mAdapter?.updateNode(oldNode, newNode ?: oldNode)
}
fun updateNodes(oldNodes: List<Node>, newNodes: List<Node>) {
mAdapter?.updateNodes(oldNodes, newNodes)
}
fun removeNode(pwNode: Node) {
mAdapter?.removeNode(pwNode) mAdapter?.removeNode(pwNode)
} }
fun removeNodes(nodes: List<Node>) {
mAdapter?.removeNodes(nodes)
}
fun removeNodeAt(position: Int) {
mAdapter?.removeNodeAt(position)
}
fun removeNodesAt(positions: IntArray) {
mAdapter?.removeNodesAt(positions)
}
/**
* Callback listener to redefine to do an action when a node is click
*/
interface NodeClickListener {
fun onNodeClick(node: Node)
fun onNodeSelected(nodes: List<Node>): Boolean
}
/**
* Menu listener to redefine to do an action in menu
*/
interface NodesActionMenuListener {
fun onOpenMenuClick(node: Node): Boolean
fun onEditMenuClick(node: Node): Boolean
fun onCopyMenuClick(nodes: List<Node>): Boolean
fun onMoveMenuClick(nodes: List<Node>): Boolean
fun onDeleteMenuClick(nodes: List<Node>): Boolean
fun onPasteMenuClick(pasteMode: PasteMode?, nodes: List<Node>): Boolean
}
enum class PasteMode {
UNDEFINED, PASTE_FROM_COPY, PASTE_FROM_MOVE
}
interface OnScrollListener { interface OnScrollListener {
/** /**
@@ -282,7 +458,7 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
private const val GROUP_KEY = "GROUP_KEY" private const val GROUP_KEY = "GROUP_KEY"
private const val IS_SEARCH = "IS_SEARCH" private const val IS_SEARCH = "IS_SEARCH"
fun newInstance(group: GroupVersioned?, readOnly: Boolean, isASearch: Boolean): ListNodesFragment { fun newInstance(group: Group?, readOnly: Boolean, isASearch: Boolean): ListNodesFragment {
val bundle = Bundle() val bundle = Bundle()
if (group != null) { if (group != null) {
bundle.putParcelable(GROUP_KEY, group) bundle.putParcelable(GROUP_KEY, group)

View File

@@ -1,20 +1,20 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
@@ -25,17 +25,17 @@ import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.DialogFragment import com.google.android.material.textfield.TextInputLayout
import android.support.v7.app.AlertDialog import androidx.fragment.app.DialogFragment
import androidx.appcompat.app.AlertDialog
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.View import android.view.View
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.KeyFileHelper import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.view.KeyFileSelectionView
class AssignMasterKeyDialogFragment : DialogFragment() { class AssignMasterKeyDialogFragment : DialogFragment() {
@@ -43,15 +43,30 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
private var mKeyFile: Uri? = null private var mKeyFile: Uri? = null
private var rootView: View? = null private var rootView: View? = null
private var passwordCheckBox: CompoundButton? = null private var passwordCheckBox: CompoundButton? = null
private var passView: TextView? = null
private var passConfView: TextView? = null private var passwordTextInputLayout: TextInputLayout? = null
private var passwordView: TextView? = null
private var passwordRepeatTextInputLayout: TextInputLayout? = null
private var passwordRepeatView: TextView? = null
private var keyFileCheckBox: CompoundButton? = null private var keyFileCheckBox: CompoundButton? = null
private var keyFileView: TextView? = null private var keyFileSelectionView: KeyFileSelectionView? = null
private var mListener: AssignPasswordDialogListener? = null private var mListener: AssignPasswordDialogListener? = null
private var mKeyFileHelper: KeyFileHelper? = null private var mOpenFileHelper: OpenFileHelper? = null
private val passwordTextWatcher = object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable) {
passwordCheckBox?.isChecked = true
}
}
interface AssignPasswordDialogListener { interface AssignPasswordDialogListener {
fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?, fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?,
@@ -60,18 +75,25 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
keyFileChecked: Boolean, keyFile: Uri?) keyFileChecked: Boolean, keyFile: Uri?)
} }
override fun onAttach(activity: Context?) { override fun onAttach(activity: Context) {
super.onAttach(activity) super.onAttach(activity)
try { try {
mListener = activity as AssignPasswordDialogListener? mListener = activity as AssignPasswordDialogListener
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
throw ClassCastException(activity?.toString() throw ClassCastException(activity.toString()
+ " must implement " + AssignPasswordDialogListener::class.java.name) + " must implement " + AssignPasswordDialogListener::class.java.name)
} }
} }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity -> activity?.let { activity ->
var allowNoMasterKey = false
arguments?.apply {
if (containsKey(ALLOW_NO_MASTER_KEY_ARG))
allowNoMasterKey = getBoolean(ALLOW_NO_MASTER_KEY_ARG, false)
}
val builder = AlertDialog.Builder(activity) val builder = AlertDialog.Builder(activity)
val inflater = activity.layoutInflater val inflater = activity.layoutInflater
@@ -80,36 +102,22 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
.setTitle(R.string.assign_master_key) .setTitle(R.string.assign_master_key)
// Add action buttons // Add action buttons
.setPositiveButton(android.R.string.ok) { _, _ -> } .setPositiveButton(android.R.string.ok) { _, _ -> }
.setNegativeButton(R.string.cancel) { _, _ -> } .setNegativeButton(android.R.string.cancel) { _, _ -> }
passwordCheckBox = rootView?.findViewById(R.id.password_checkbox) passwordCheckBox = rootView?.findViewById(R.id.password_checkbox)
passView = rootView?.findViewById(R.id.pass_password) passwordTextInputLayout = rootView?.findViewById(R.id.password_input_layout)
passView?.addTextChangedListener(object : TextWatcher { passwordView = rootView?.findViewById(R.id.pass_password)
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} passwordRepeatTextInputLayout = rootView?.findViewById(R.id.password_repeat_input_layout)
passwordRepeatView = rootView?.findViewById(R.id.pass_conf_password)
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable) {
passwordCheckBox?.isChecked = true
}
})
passConfView = rootView?.findViewById(R.id.pass_conf_password)
keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox) keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox)
keyFileView = rootView?.findViewById(R.id.pass_keyfile) keyFileSelectionView = rootView?.findViewById(R.id.keyfile_selection)
keyFileView?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} mOpenFileHelper = OpenFileHelper(this)
keyFileSelectionView?.apply {
override fun afterTextChanged(editable: Editable) { setOnClickListener(mOpenFileHelper?.openFileOnClickViewListener)
keyFileCheckBox?.isChecked = true setOnLongClickListener(mOpenFileHelper?.openFileOnClickViewListener)
} }
})
mKeyFileHelper = KeyFileHelper(this)
rootView?.findViewById<View>(R.id.browse_button)?.setOnClickListener { view ->
mKeyFileHelper?.openFileOnClickViewListener?.onClick(view) }
val dialog = builder.create() val dialog = builder.create()
@@ -124,7 +132,11 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
var error = verifyPassword() || verifyFile() var error = verifyPassword() || verifyFile()
if (!passwordCheckBox!!.isChecked && !keyFileCheckBox!!.isChecked) { if (!passwordCheckBox!!.isChecked && !keyFileCheckBox!!.isChecked) {
error = true error = true
showNoKeyConfirmationDialog() if (allowNoMasterKey)
showNoKeyConfirmationDialog()
else {
passwordTextInputLayout?.error = getString(R.string.error_disallow_no_credentials)
}
} }
if (!error) { if (!error) {
mListener?.onAssignKeyDialogPositiveClick( mListener?.onAssignKeyDialogPositiveClick(
@@ -149,20 +161,33 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
return super.onCreateDialog(savedInstanceState) return super.onCreateDialog(savedInstanceState)
} }
override fun onResume() {
super.onResume()
// To check checkboxes if a text is present
passwordView?.addTextChangedListener(passwordTextWatcher)
}
override fun onPause() {
super.onPause()
passwordView?.removeTextChangedListener(passwordTextWatcher)
}
private fun verifyPassword(): Boolean { private fun verifyPassword(): Boolean {
var error = false var error = false
if (passwordCheckBox != null if (passwordCheckBox != null
&& passwordCheckBox!!.isChecked && passwordCheckBox!!.isChecked
&& passView != null && passwordView != null
&& passConfView != null) { && passwordRepeatView != null) {
mMasterPassword = passView!!.text.toString() mMasterPassword = passwordView!!.text.toString()
val confPassword = passConfView!!.text.toString() val confPassword = passwordRepeatView!!.text.toString()
// Verify that passwords match // Verify that passwords match
if (mMasterPassword != confPassword) { if (mMasterPassword != confPassword) {
error = true error = true
// Passwords do not match // Passwords do not match
Toast.makeText(context, R.string.error_pass_match, Toast.LENGTH_LONG).show() passwordRepeatTextInputLayout?.error = getString(R.string.error_pass_match)
} }
if (mMasterPassword == null || mMasterPassword!!.isEmpty()) { if (mMasterPassword == null || mMasterPassword!!.isEmpty()) {
@@ -170,6 +195,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
showEmptyPasswordConfirmationDialog() showEmptyPasswordConfirmationDialog()
} }
} }
return error return error
} }
@@ -177,13 +203,12 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
var error = false var error = false
if (keyFileCheckBox != null if (keyFileCheckBox != null
&& keyFileCheckBox!!.isChecked) { && keyFileCheckBox!!.isChecked) {
val keyFile = UriUtil.parseUriFile(keyFileView?.text?.toString())
mKeyFile = keyFile
// Verify that a keyfile is set keyFileSelectionView?.uri?.let { uri ->
if (keyFile == null || keyFile.toString().isEmpty()) { mKeyFile = uri
} ?: run {
error = true error = true
Toast.makeText(context, R.string.error_nokeyfile, Toast.LENGTH_LONG).show() keyFileSelectionView?.error = getString(R.string.error_nokeyfile)
} }
} }
return error return error
@@ -201,7 +226,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
this@AssignMasterKeyDialogFragment.dismiss() this@AssignMasterKeyDialogFragment.dismiss()
} }
} }
.setNegativeButton(R.string.cancel) { _, _ -> } .setNegativeButton(android.R.string.cancel) { _, _ -> }
builder.create().show() builder.create().show()
} }
} }
@@ -216,7 +241,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
keyFileCheckBox!!.isChecked, mKeyFile) keyFileCheckBox!!.isChecked, mKeyFile)
this@AssignMasterKeyDialogFragment.dismiss() this@AssignMasterKeyDialogFragment.dismiss()
} }
.setNegativeButton(R.string.cancel) { _, _ -> } .setNegativeButton(android.R.string.cancel) { _, _ -> }
builder.create().show() builder.create().show()
} }
} }
@@ -224,13 +249,25 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
mKeyFileHelper?.onActivityResultCallback(requestCode, resultCode, data mOpenFileHelper?.onActivityResultCallback(requestCode, resultCode, data
) { uri -> ) { uri ->
UriUtil.parseUriFile(uri)?.let { pathUri -> uri?.let { pathUri ->
keyFileCheckBox?.isChecked = true keyFileCheckBox?.isChecked = true
keyFileView?.text = pathUri.toString() keyFileSelectionView?.uri = pathUri
} }
} }
} }
companion object {
private const val ALLOW_NO_MASTER_KEY_ARG = "ALLOW_NO_MASTER_KEY_ARG"
fun getInstance(allowNoMasterKey: Boolean): AssignMasterKeyDialogFragment {
val fragment = AssignMasterKeyDialogFragment()
val args = Bundle()
args.putBoolean(ALLOW_NO_MASTER_KEY_ARG, allowNoMasterKey)
fragment.arguments = args
return fragment
}
}
} }

View File

@@ -1,213 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.dialogs
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.view.ActionMode
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.EditText
import android.widget.Spinner
import android.widget.TextView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.FilePickerStylishActivity
import com.kunzisoft.keepass.utils.UriUtil
import com.nononsenseapps.filepicker.FilePickerActivity
import com.nononsenseapps.filepicker.Utils
class CreateFileDialogFragment : DialogFragment(), AdapterView.OnItemSelectedListener {
private val FILE_CODE = 3853
private var folderPathView: EditText? = null
private var fileNameView: EditText? = null
private var positiveButton: Button? = null
private var negativeButton: Button? = null
private var mDefinePathDialogListener: DefinePathDialogListener? = null
private var mDatabaseFileExtension: String? = null
private var mUriPath: Uri? = null
interface DefinePathDialogListener {
fun onDefinePathDialogPositiveClick(pathFile: Uri?): Boolean
fun onDefinePathDialogNegativeClick(pathFile: Uri?): Boolean
}
override fun onAttach(activity: Context?) {
super.onAttach(activity)
try {
mDefinePathDialogListener = activity as DefinePathDialogListener?
} catch (e: ClassCastException) {
throw ClassCastException(activity?.toString()
+ " must implement " + DefinePathDialogListener::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
val builder = AlertDialog.Builder(activity)
val inflater = activity.layoutInflater
val rootView = inflater.inflate(R.layout.fragment_file_creation, null)
builder.setView(rootView)
.setTitle(R.string.create_keepass_file)
// Add action buttons
.setPositiveButton(android.R.string.ok) { _, _ -> }
.setNegativeButton(R.string.cancel) { _, _ -> }
// To prevent crash issue #69 https://github.com/Kunzisoft/KeePassDX/issues/69
val actionCopyBarCallback = object : ActionMode.Callback {
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
positiveButton?.isEnabled = false
negativeButton?.isEnabled = false
return true
}
override fun onDestroyActionMode(mode: ActionMode) {
positiveButton?.isEnabled = true
negativeButton?.isEnabled = true
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
return true
}
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return true
}
}
// Folder selection
val browseView = rootView.findViewById<View>(R.id.browse_button)
folderPathView = rootView.findViewById(R.id.folder_path)
folderPathView?.customSelectionActionModeCallback = actionCopyBarCallback
fileNameView = rootView.findViewById(R.id.filename)
fileNameView?.customSelectionActionModeCallback = actionCopyBarCallback
val defaultPath = Environment.getExternalStorageDirectory().path + getString(R.string.database_file_path_default)
folderPathView?.setText(defaultPath)
browseView.setOnClickListener { _ ->
Intent(context, FilePickerStylishActivity::class.java).apply {
putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false)
putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true)
putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR)
putExtra(FilePickerActivity.EXTRA_START_PATH,
Environment.getExternalStorageDirectory().path)
startActivityForResult(this, FILE_CODE)
}
}
// Init path
mUriPath = null
// Extension
mDatabaseFileExtension = getString(R.string.database_file_extension_default)
val spinner = rootView.findViewById<Spinner>(R.id.file_types)
spinner.onItemSelectedListener = this
// Spinner Drop down elements
val fileTypes = resources.getStringArray(R.array.file_types)
val dataAdapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, fileTypes)
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = dataAdapter
// Or text if only one item https://github.com/Kunzisoft/KeePassDX/issues/105
if (fileTypes.size == 1) {
val params = spinner.layoutParams
spinner.visibility = View.GONE
val extensionTextView = TextView(context)
extensionTextView.text = mDatabaseFileExtension
extensionTextView.layoutParams = params
val parentView = spinner.parent as ViewGroup
parentView.addView(extensionTextView)
}
val dialog = builder.create()
dialog.setOnShowListener { _ ->
positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE)
positiveButton?.setOnClickListener { _ ->
mDefinePathDialogListener?.let {
if (it.onDefinePathDialogPositiveClick(buildPath()))
dismiss()
}
}
negativeButton?.setOnClickListener { _->
mDefinePathDialogListener?.let {
if (it.onDefinePathDialogNegativeClick(buildPath())) {
dismiss()
}
}
}
}
return dialog
}
return super.onCreateDialog(savedInstanceState)
}
private fun buildPath(): Uri? {
if (folderPathView != null && fileNameView != null && mDatabaseFileExtension != null) {
var path = Uri.Builder().path(folderPathView!!.text.toString())
.appendPath(fileNameView!!.text.toString() + mDatabaseFileExtension!!)
.build()
context?.let { context ->
path = UriUtil.translateUri(context, path)
}
return path
}
return null
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) {
mUriPath = data?.data
mUriPath?.let {
val file = Utils.getFileForUri(it)
folderPathView?.setText(file.path)
}
}
}
override fun onItemSelected(adapterView: AdapterView<*>, view: View, position: Int, id: Long) {
mDatabaseFileExtension = adapterView.getItemAtPosition(position).toString()
}
override fun onNothingSelected(adapterView: AdapterView<*>) {
// Do nothing
}
}

View File

@@ -0,0 +1,61 @@
package com.kunzisoft.keepass.activities.dialogs
import android.app.DatePickerDialog
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.DialogFragment
class DatePickerFragment : DialogFragment() {
private var mDefaultYear: Int = 2000
private var mDefaultMonth: Int = 1
private var mDefaultDay: Int = 1
private var mListener: DatePickerDialog.OnDateSetListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
try {
mListener = context as DatePickerDialog.OnDateSetListener
} catch (e: ClassCastException) {
throw ClassCastException(context.toString()
+ " must implement " + DatePickerDialog.OnDateSetListener::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Create a new instance of DatePickerDialog and return it
return context?.let {
arguments?.apply {
if (containsKey(DEFAULT_YEAR_BUNDLE_KEY))
mDefaultYear = getInt(DEFAULT_YEAR_BUNDLE_KEY)
if (containsKey(DEFAULT_MONTH_BUNDLE_KEY))
mDefaultMonth = getInt(DEFAULT_MONTH_BUNDLE_KEY)
if (containsKey(DEFAULT_DAY_BUNDLE_KEY))
mDefaultDay = getInt(DEFAULT_DAY_BUNDLE_KEY)
}
DatePickerDialog(it, mListener, mDefaultYear, mDefaultMonth, mDefaultDay)
} ?: super.onCreateDialog(savedInstanceState)
}
companion object {
private const val DEFAULT_YEAR_BUNDLE_KEY = "DEFAULT_YEAR_BUNDLE_KEY"
private const val DEFAULT_MONTH_BUNDLE_KEY = "DEFAULT_MONTH_BUNDLE_KEY"
private const val DEFAULT_DAY_BUNDLE_KEY = "DEFAULT_DAY_BUNDLE_KEY"
fun getInstance(defaultYear: Int,
defaultMonth: Int,
defaultDay: Int): DatePickerFragment {
return DatePickerFragment().apply {
arguments = Bundle().apply {
putInt(DEFAULT_YEAR_BUNDLE_KEY, defaultYear)
putInt(DEFAULT_MONTH_BUNDLE_KEY, defaultMonth)
putInt(DEFAULT_DAY_BUNDLE_KEY, defaultDay)
}
}
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2019 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.activities.dialogs
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.getBundleFromListNodes
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.getListNodesFromBundle
class DeleteNodesDialogFragment : DialogFragment() {
private var mNodesToDelete: List<Node> = ArrayList()
private var mListener: DeleteNodeListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
try {
mListener = context as DeleteNodeListener
} catch (e: ClassCastException) {
throw ClassCastException(context.toString()
+ " must implement " + DeleteNodeListener::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
arguments?.apply {
if (containsKey(DatabaseTaskNotificationService.GROUPS_ID_KEY)
&& containsKey(DatabaseTaskNotificationService.ENTRIES_ID_KEY)) {
mNodesToDelete = getListNodesFromBundle(Database.getInstance(), this)
}
} ?: savedInstanceState?.apply {
if (containsKey(DatabaseTaskNotificationService.GROUPS_ID_KEY)
&& containsKey(DatabaseTaskNotificationService.ENTRIES_ID_KEY)) {
mNodesToDelete = getListNodesFromBundle(Database.getInstance(), savedInstanceState)
}
}
activity?.let { activity ->
// Use the Builder class for convenient dialog construction
val builder = AlertDialog.Builder(activity)
builder.setMessage(getString(R.string.warning_permanently_delete_nodes))
builder.setPositiveButton(android.R.string.yes) { _, _ ->
mListener?.permanentlyDeleteNodes(mNodesToDelete)
}
builder.setNegativeButton(android.R.string.no) { _, _ -> dismiss() }
// Create the AlertDialog object and return it
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putAll(getBundleFromListNodes(mNodesToDelete))
}
interface DeleteNodeListener {
fun permanentlyDeleteNodes(nodes: List<Node>)
}
companion object {
fun getInstance(nodesToDelete: List<Node>): DeleteNodesDialogFragment {
return DeleteNodesDialogFragment().apply {
arguments = getBundleFromListNodes(nodesToDelete)
}
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2019 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.activities.dialogs
import android.app.Dialog
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.R
class DuplicateUuidDialog : DialogFragment() {
var positiveAction: (() -> Unit)? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
// Use the Builder class for convenient dialog construction
val builder = androidx.appcompat.app.AlertDialog.Builder(activity).apply {
val message = getString(R.string.contains_duplicate_uuid) +
"\n\n" + getString(R.string.contains_duplicate_uuid_procedure)
setMessage(message)
setPositiveButton(getString(android.R.string.ok)) { _, _ ->
positiveAction?.invoke()
dismiss()
}
setNegativeButton(getString(android.R.string.cancel)) { _, _ -> dismiss() }
}
// Create the AlertDialog object and return it
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
override fun onPause() {
super.onPause()
this.dismiss()
}
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog
import android.net.Uri
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.view.View
import android.widget.TextView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.fileselect.FileDatabaseModel
import java.text.DateFormat
class FileInformationDialogFragment : DialogFragment() {
private var fileSizeContainerView: View? = null
private var fileModificationContainerView: View? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
val builder = AlertDialog.Builder(activity)
val inflater = activity.layoutInflater
val root = inflater.inflate(R.layout.fragment_file_selection_information, null)
val fileNameView = root.findViewById<TextView>(R.id.file_filename)
val filePathView = root.findViewById<TextView>(R.id.file_path)
fileSizeContainerView = root.findViewById(R.id.file_size_container)
val fileSizeView = root.findViewById<TextView>(R.id.file_size)
fileModificationContainerView = root.findViewById(R.id.file_modification_container)
val fileModificationView = root.findViewById<TextView>(R.id.file_modification)
arguments?.apply {
if (containsKey(FILE_SELECT_BEEN_ARG)) {
(getSerializable(FILE_SELECT_BEEN_ARG) as FileDatabaseModel?)?.let { fileDatabaseModel ->
fileDatabaseModel.fileUri?.let { fileUri ->
filePathView.text = Uri.decode(fileUri.toString())
}
fileNameView.text = fileDatabaseModel.fileName
if (fileDatabaseModel.notFound()) {
hideFileInfo()
} else {
showFileInfo()
fileSizeView.text = fileDatabaseModel.size.toString()
fileModificationView.text = DateFormat.getDateTimeInstance()
.format(fileDatabaseModel.lastModification)
}
} ?: hideFileInfo()
}
}
builder.setView(root)
builder.setPositiveButton(android.R.string.ok) { _, _ -> }
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
private fun showFileInfo() {
fileSizeContainerView?.visibility = View.VISIBLE
fileModificationContainerView?.visibility = View.VISIBLE
}
private fun hideFileInfo() {
fileSizeContainerView?.visibility = View.GONE
fileModificationContainerView?.visibility = View.GONE
}
companion object {
private const val FILE_SELECT_BEEN_ARG = "FILE_SELECT_BEEN_ARG"
fun newInstance(fileDatabaseModel: FileDatabaseModel): FileInformationDialogFragment {
val fileInformationDialogFragment = FileInformationDialogFragment()
val args = Bundle()
args.putSerializable(FILE_SELECT_BEEN_ARG, fileDatabaseModel)
fileInformationDialogFragment.arguments = args
return fileInformationDialogFragment
}
}
}

View File

@@ -1,33 +1,34 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog import android.app.Dialog
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.DialogFragment import androidx.fragment.app.DialogFragment
import android.support.v7.app.AlertDialog import androidx.appcompat.app.AlertDialog
import android.widget.Button import android.widget.Button
import android.widget.TextView
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.Util import com.kunzisoft.keepass.utils.UriUtil
class BrowserDialogFragment : DialogFragment() { class FileManagerDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity -> activity?.let { activity ->
@@ -35,17 +36,13 @@ class BrowserDialogFragment : DialogFragment() {
// Get the layout inflater // Get the layout inflater
val root = activity.layoutInflater.inflate(R.layout.fragment_browser_install, null) val root = activity.layoutInflater.inflate(R.layout.fragment_browser_install, null)
builder.setView(root) builder.setView(root)
.setNegativeButton(R.string.cancel) { _, _ -> } .setNegativeButton(android.R.string.cancel) { _, _ -> }
val market = root.findViewById<Button>(R.id.install_market) val textDescription = root.findViewById<TextView>(R.id.file_manager_install_description)
market.setOnClickListener { textDescription.text = getString(R.string.file_manager_install_description)
Util.gotoUrl(context!!, R.string.filemanager_play_store)
dismiss()
}
val web = root.findViewById<Button>(R.id.install_web) root.findViewById<Button>(R.id.file_manager_button).setOnClickListener {
web.setOnClickListener { UriUtil.gotoUrl(requireContext(), R.string.file_manager_explanation_url)
Util.gotoUrl(context!!, R.string.filemanager_f_droid)
dismiss() dismiss()
} }

View File

@@ -1,20 +1,20 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
@@ -22,14 +22,18 @@ package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.DialogFragment import com.google.android.material.textfield.TextInputLayout
import android.support.v7.app.AlertDialog import androidx.fragment.app.DialogFragment
import androidx.appcompat.app.AlertDialog
import android.view.View import android.view.View
import android.widget.* import android.widget.Button
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.SeekBar
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.password.PasswordGenerator import com.kunzisoft.keepass.password.PasswordGenerator
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.applyFontVisibility import com.kunzisoft.keepass.view.applyFontVisibility
class GeneratePasswordDialogFragment : DialogFragment() { class GeneratePasswordDialogFragment : DialogFragment() {
@@ -37,6 +41,7 @@ class GeneratePasswordDialogFragment : DialogFragment() {
private var root: View? = null private var root: View? = null
private var lengthTextView: EditText? = null private var lengthTextView: EditText? = null
private var passwordInputLayoutView: TextInputLayout? = null
private var passwordView: EditText? = null private var passwordView: EditText? = null
private var uppercaseBox: CompoundButton? = null private var uppercaseBox: CompoundButton? = null
@@ -49,12 +54,12 @@ class GeneratePasswordDialogFragment : DialogFragment() {
private var bracketsBox: CompoundButton? = null private var bracketsBox: CompoundButton? = null
private var extendedBox: CompoundButton? = null private var extendedBox: CompoundButton? = null
override fun onAttach(context: Context?) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
try { try {
mListener = context as GeneratePasswordListener? mListener = context as GeneratePasswordListener
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
throw ClassCastException(context?.toString() throw ClassCastException(context.toString()
+ " must implement " + GeneratePasswordListener::class.java.name) + " must implement " + GeneratePasswordListener::class.java.name)
} }
} }
@@ -65,6 +70,7 @@ class GeneratePasswordDialogFragment : DialogFragment() {
val inflater = activity.layoutInflater val inflater = activity.layoutInflater
root = inflater.inflate(R.layout.fragment_generate_password, null) root = inflater.inflate(R.layout.fragment_generate_password, null)
passwordInputLayoutView = root?.findViewById(R.id.password_input_layout)
passwordView = root?.findViewById(R.id.password) passwordView = root?.findViewById(R.id.password)
passwordView?.applyFontVisibility() passwordView?.applyFontVisibility()
@@ -108,7 +114,7 @@ class GeneratePasswordDialogFragment : DialogFragment() {
dismiss() dismiss()
} }
.setNegativeButton(R.string.cancel) { _, _ -> .setNegativeButton(android.R.string.cancel) { _, _ ->
val bundle = Bundle() val bundle = Bundle()
mListener?.cancelPassword(bundle) mListener?.cancelPassword(bundle)
@@ -161,9 +167,7 @@ class GeneratePasswordDialogFragment : DialogFragment() {
var password = "" var password = ""
try { try {
val length = Integer.valueOf(root?.findViewById<EditText>(R.id.length)?.text.toString()) val length = Integer.valueOf(root?.findViewById<EditText>(R.id.length)?.text.toString())
password = PasswordGenerator(resources).generatePassword(length,
val generator = PasswordGenerator(resources)
password = generator.generatePassword(length,
uppercaseBox?.isChecked == true, uppercaseBox?.isChecked == true,
lowercaseBox?.isChecked == true, lowercaseBox?.isChecked == true,
digitsBox?.isChecked == true, digitsBox?.isChecked == true,
@@ -173,10 +177,11 @@ class GeneratePasswordDialogFragment : DialogFragment() {
specialsBox?.isChecked == true, specialsBox?.isChecked == true,
bracketsBox?.isChecked == true, bracketsBox?.isChecked == true,
extendedBox?.isChecked == true) extendedBox?.isChecked == true)
passwordInputLayoutView?.error = null
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
Toast.makeText(context, R.string.error_wrong_length, Toast.LENGTH_LONG).show() passwordInputLayoutView?.error = getString(R.string.error_wrong_length)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() passwordInputLayoutView?.error = e.message
} }
return password return password
@@ -188,7 +193,6 @@ class GeneratePasswordDialogFragment : DialogFragment() {
} }
companion object { companion object {
const val KEY_PASSWORD_ID = "KEY_PASSWORD_ID" const val KEY_PASSWORD_ID = "KEY_PASSWORD_ID"
} }
} }

View File

@@ -1,20 +1,20 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
@@ -23,9 +23,9 @@ import android.app.Dialog
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.support.design.widget.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import android.support.v4.app.DialogFragment import androidx.fragment.app.DialogFragment
import android.support.v7.app.AlertDialog import androidx.appcompat.app.AlertDialog
import android.widget.Button import android.widget.Button
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
@@ -33,8 +33,8 @@ import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION
import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.UPDATE import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.UPDATE
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.GroupVersioned import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.PwIcon import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.icons.assignDatabaseIcon import com.kunzisoft.keepass.icons.assignDatabaseIcon
class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconPickerListener { class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconPickerListener {
@@ -45,7 +45,7 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
private var editGroupDialogAction: EditGroupDialogAction? = null private var editGroupDialogAction: EditGroupDialogAction? = null
private var nameGroup: String? = null private var nameGroup: String? = null
private var iconGroup: PwIcon? = null private var iconGroup: IconImage? = null
private var nameTextLayoutView: TextInputLayout? = null private var nameTextLayoutView: TextInputLayout? = null
private var nameTextView: TextView? = null private var nameTextView: TextView? = null
@@ -62,15 +62,15 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
} }
} }
override fun onAttach(context: Context?) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
// Verify that the host activity implements the callback interface // Verify that the host activity implements the callback interface
try { try {
// Instantiate the NoticeDialogListener so we can send events to the host // Instantiate the NoticeDialogListener so we can send events to the host
editGroupListener = context as EditGroupListener? editGroupListener = context as EditGroupListener
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
// The activity doesn't implement the interface, throw exception // The activity doesn't implement the interface, throw exception
throw ClassCastException(context?.toString() throw ClassCastException(context.toString()
+ " must implement " + GroupEditDialogFragment::class.java.name) + " must implement " + GroupEditDialogFragment::class.java.name)
} }
@@ -122,7 +122,7 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
val builder = AlertDialog.Builder(activity) val builder = AlertDialog.Builder(activity)
builder.setView(root) builder.setView(root)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)
.setNegativeButton(R.string.cancel) { _, _ -> .setNegativeButton(android.R.string.cancel) { _, _ ->
editGroupListener?.cancelEditGroup( editGroupListener?.cancelEditGroup(
editGroupDialogAction, editGroupDialogAction,
nameTextView?.text?.toString(), nameTextView?.text?.toString(),
@@ -130,9 +130,7 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
} }
iconButtonView?.setOnClickListener { _ -> iconButtonView?.setOnClickListener { _ ->
fragmentManager?.let { IconPickerDialogFragment().show(parentFragmentManager, "IconPickerDialogFragment")
IconPickerDialogFragment().show(it, "IconPickerDialogFragment")
}
} }
return builder.create() return builder.create()
@@ -186,8 +184,8 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
} }
interface EditGroupListener { interface EditGroupListener {
fun approveEditGroup(action: EditGroupDialogAction?, name: String?, icon: PwIcon?) fun approveEditGroup(action: EditGroupDialogAction?, name: String?, icon: IconImage?)
fun cancelEditGroup(action: EditGroupDialogAction?, name: String?, icon: PwIcon?) fun cancelEditGroup(action: EditGroupDialogAction?, name: String?, icon: IconImage?)
} }
companion object { companion object {
@@ -206,7 +204,7 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
return fragment return fragment
} }
fun build(group: GroupVersioned): GroupEditDialogFragment { fun build(group: Group): GroupEditDialogFragment {
val bundle = Bundle() val bundle = Bundle()
bundle.putString(KEY_NAME, group.title) bundle.putString(KEY_NAME, group.title)
bundle.putParcelable(KEY_ICON, group.icon) bundle.putParcelable(KEY_ICON, group.icon)

View File

@@ -1,20 +1,20 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
@@ -24,18 +24,18 @@ import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v4.widget.ImageViewCompat
import android.support.v7.app.AlertDialog
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.BaseAdapter import android.widget.BaseAdapter
import android.widget.GridView import android.widget.GridView
import android.widget.ImageView import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import androidx.core.widget.ImageViewCompat
import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.database.element.PwIconStandard import com.kunzisoft.keepass.database.element.icon.IconImageStandard
import com.kunzisoft.keepass.icons.IconPack import com.kunzisoft.keepass.icons.IconPack
import com.kunzisoft.keepass.icons.IconPackChooser import com.kunzisoft.keepass.icons.IconPackChooser
@@ -45,13 +45,13 @@ class IconPickerDialogFragment : DialogFragment() {
private var iconPickerListener: IconPickerListener? = null private var iconPickerListener: IconPickerListener? = null
private var iconPack: IconPack? = null private var iconPack: IconPack? = null
override fun onAttach(context: Context?) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
try { try {
iconPickerListener = context as IconPickerListener? iconPickerListener = context as IconPickerListener
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
// The activity doesn't implement the interface, throw exception // The activity doesn't implement the interface, throw exception
throw ClassCastException(context!!.toString() throw ClassCastException(context.toString()
+ " must implement " + IconPickerListener::class.java.name) + " must implement " + IconPickerListener::class.java.name)
} }
} }
@@ -60,7 +60,7 @@ class IconPickerDialogFragment : DialogFragment() {
activity?.let { activity -> activity?.let { activity ->
val builder = AlertDialog.Builder(activity) val builder = AlertDialog.Builder(activity)
iconPack = IconPackChooser.getSelectedIconPack(context!!) iconPack = IconPackChooser.getSelectedIconPack(requireContext())
// Inflate and set the layout for the dialog // Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout // Pass null as the parent view because its going in the dialog layout
@@ -72,12 +72,12 @@ class IconPickerDialogFragment : DialogFragment() {
currIconGridView.setOnItemClickListener { _, _, position, _ -> currIconGridView.setOnItemClickListener { _, _, position, _ ->
val bundle = Bundle() val bundle = Bundle()
bundle.putParcelable(KEY_ICON_STANDARD, PwIconStandard(position)) bundle.putParcelable(KEY_ICON_STANDARD, IconImageStandard(position))
iconPickerListener?.iconPicked(bundle) iconPickerListener?.iconPicked(bundle)
dismiss() dismiss()
} }
builder.setNegativeButton(R.string.cancel) { _, _ -> this@IconPickerDialogFragment.dialog.cancel() } builder.setNegativeButton(android.R.string.cancel) { _, _ -> this@IconPickerDialogFragment.dialog?.cancel() }
return builder.create() return builder.create()
} }
@@ -112,7 +112,7 @@ class IconPickerDialogFragment : DialogFragment() {
// Retrieve the textColor to tint the icon // Retrieve the textColor to tint the icon
val ta = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor)) val ta = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor))
ImageViewCompat.setImageTintList(iconImageView, ColorStateList.valueOf(ta.getColor(0, Color.BLACK))) ImageViewCompat.setImageTintList(iconImageView, ColorStateList.valueOf(ta.getColor(0, Color.BLACK)))
ta?.recycle() ta.recycle()
} }
} }
@@ -128,7 +128,7 @@ class IconPickerDialogFragment : DialogFragment() {
private const val KEY_ICON_STANDARD = "KEY_ICON_STANDARD" private const val KEY_ICON_STANDARD = "KEY_ICON_STANDARD"
fun getIconStandardFromBundle(bundle: Bundle): PwIconStandard? { fun getIconStandardFromBundle(bundle: Bundle): IconImageStandard? {
return bundle.getParcelable(KEY_ICON_STANDARD) return bundle.getParcelable(KEY_ICON_STANDARD)
} }

View File

@@ -1,67 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
import android.provider.Settings
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.view.View
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.Util
class KeyboardExplanationDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let {
val builder = AlertDialog.Builder(activity!!)
val inflater = activity!!.layoutInflater
val rootView = inflater.inflate(R.layout.fragment_keyboard_explanation, null)
rootView.findViewById<View>(R.id.keyboards_activate_setting_path1_text)
.setOnClickListener { launchActivateKeyboardSetting() }
rootView.findViewById<View>(R.id.keyboards_activate_setting_path2_text)
.setOnClickListener { launchActivateKeyboardSetting() }
val containerKeyboardSwitcher = rootView.findViewById<View>(R.id.container_keyboard_switcher)
if (BuildConfig.CLOSED_STORE) {
containerKeyboardSwitcher.setOnClickListener { Util.gotoUrl(context!!, R.string.keyboard_switcher_play_store) }
} else {
containerKeyboardSwitcher.setOnClickListener { Util.gotoUrl(context!!, R.string.keyboard_switcher_f_droid) }
}
builder.setView(rootView)
.setPositiveButton(android.R.string.ok) { _, _ -> }
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
private fun launchActivateKeyboardSetting() {
val intent = Intent(Settings.ACTION_INPUT_METHOD_SETTINGS)
intent.addFlags(FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}

View File

@@ -1,20 +1,20 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
@@ -23,7 +23,7 @@ import android.app.AlertDialog
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
class PasswordEncodingDialogFragment : DialogFragment() { class PasswordEncodingDialogFragment : DialogFragment() {
@@ -35,7 +35,7 @@ class PasswordEncodingDialogFragment : DialogFragment() {
val builder = AlertDialog.Builder(activity) val builder = AlertDialog.Builder(activity)
builder.setMessage(activity.getString(R.string.warning_password_encoding)).setTitle(R.string.warning) builder.setMessage(activity.getString(R.string.warning_password_encoding)).setTitle(R.string.warning)
builder.setPositiveButton(android.R.string.ok, positiveButtonClickListener) builder.setPositiveButton(android.R.string.ok, positiveButtonClickListener)
builder.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.cancel() } builder.setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.cancel() }
return builder.create() return builder.create()
} }

View File

@@ -1,36 +1,34 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog import android.app.Dialog
import android.content.ActivityNotFoundException
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.text.Html
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.widget.Toast import androidx.appcompat.app.AlertDialog
import androidx.core.text.HtmlCompat
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.BuildConfig import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.Util import com.kunzisoft.keepass.utils.UriUtil
/** /**
* Custom Dialog that asks the user to download the pro version or make a donation. * Custom Dialog that asks the user to download the pro version or make a donation.
@@ -44,25 +42,16 @@ class ProFeatureDialogFragment : DialogFragment() {
val stringBuilder = SpannableStringBuilder() val stringBuilder = SpannableStringBuilder()
if (BuildConfig.CLOSED_STORE) { if (BuildConfig.CLOSED_STORE) {
// TODO HtmlCompat with androidX stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_ad_free), FROM_HTML_MODE_LEGACY)).append("\n\n")
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_ad_free))).append("\n\n") stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_buy_pro), FROM_HTML_MODE_LEGACY))
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_buy_pro)))
builder.setPositiveButton(R.string.download) { _, _ -> builder.setPositiveButton(R.string.download) { _, _ ->
try { UriUtil.gotoUrl(requireContext(), R.string.app_pro_url)
Util.gotoUrl(context!!, R.string.app_pro_url)
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show()
}
} }
} else { } else {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_feature_generosity))).append("\n\n") stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_feature_generosity), FROM_HTML_MODE_LEGACY)).append("\n\n")
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_donation))) stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_donation), FROM_HTML_MODE_LEGACY))
builder.setPositiveButton(R.string.contribute) { _, _ -> builder.setPositiveButton(R.string.contribute) { _, _ ->
try { UriUtil.gotoUrl(requireContext(), R.string.contribution_url)
Util.gotoUrl(context!!, R.string.contribution_url)
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show()
}
} }
} }
builder.setMessage(stringBuilder) builder.setMessage(stringBuilder)

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.dialogs
import android.app.AlertDialog
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.preference.PreferenceManager
import com.kunzisoft.keepass.R
class ReadOnlyDialog(context: Context) : AlertDialog(context) {
override fun onCreate(savedInstanceState: Bundle) {
val ctx = context
var warning = ctx.getString(R.string.read_only_warning)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
warning = warning + "\n\n" + context.getString(R.string.read_only_kitkat_warning)
}
setMessage(warning)
setButton(BUTTON_POSITIVE, ctx.getText(android.R.string.ok)) { _, _ -> dismiss() }
setButton(BUTTON_NEGATIVE, ctx.getText(R.string.beta_dontask)) { _, _ ->
val prefs = PreferenceManager.getDefaultSharedPreferences(ctx)
val edit = prefs.edit()
edit.putBoolean(ctx.getString(R.string.show_read_only_warning), false)
edit.apply()
dismiss()
}
super.onCreate(savedInstanceState)
}
}

View File

@@ -0,0 +1,401 @@
/*
* Copyright 2019 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.activities.dialogs
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.Spinner
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.model.OtpModel
import com.kunzisoft.keepass.otp.OtpElement
import com.kunzisoft.keepass.otp.OtpElement.Companion.MAX_HOTP_COUNTER
import com.kunzisoft.keepass.otp.OtpElement.Companion.MAX_OTP_DIGITS
import com.kunzisoft.keepass.otp.OtpElement.Companion.MAX_TOTP_PERIOD
import com.kunzisoft.keepass.otp.OtpElement.Companion.MIN_HOTP_COUNTER
import com.kunzisoft.keepass.otp.OtpElement.Companion.MIN_OTP_DIGITS
import com.kunzisoft.keepass.otp.OtpElement.Companion.MIN_TOTP_PERIOD
import com.kunzisoft.keepass.otp.OtpTokenType
import com.kunzisoft.keepass.otp.OtpType
import com.kunzisoft.keepass.otp.TokenCalculator
import java.util.*
class SetOTPDialogFragment : DialogFragment() {
private var mCreateOTPElementListener: CreateOtpListener? = null
private var mOtpElement: OtpElement = OtpElement()
private var otpTypeSpinner: Spinner? = null
private var otpTokenTypeSpinner: Spinner? = null
private var otpSecretContainer: TextInputLayout? = null
private var otpSecretTextView: EditText? = null
private var otpPeriodContainer: TextInputLayout? = null
private var otpPeriodTextView: EditText? = null
private var otpCounterContainer: TextInputLayout? = null
private var otpCounterTextView: EditText? = null
private var otpDigitsContainer: TextInputLayout? = null
private var otpDigitsTextView: EditText? = null
private var otpAlgorithmSpinner: Spinner? = null
private var otpTypeAdapter: ArrayAdapter<OtpType>? = null
private var otpTokenTypeAdapter: ArrayAdapter<OtpTokenType>? = null
private var totpTokenTypeAdapter: ArrayAdapter<OtpTokenType>? = null
private var hotpTokenTypeAdapter: ArrayAdapter<OtpTokenType>? = null
private var otpAlgorithmAdapter: ArrayAdapter<TokenCalculator.HashAlgorithm>? = null
private var mManualEvent = false
private var mOnFocusChangeListener = View.OnFocusChangeListener { _, isFocus ->
if (!isFocus)
mManualEvent = true
}
private var mOnTouchListener = View.OnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
mManualEvent = true
}
}
false
}
private var mSecretWellFormed = false
private var mCounterWellFormed = true
private var mPeriodWellFormed = true
private var mDigitsWellFormed = true
override fun onAttach(context: Context) {
super.onAttach(context)
// Verify that the host activity implements the callback interface
try {
// Instantiate the NoticeDialogListener so we can send events to the host
mCreateOTPElementListener = context as CreateOtpListener
} catch (e: ClassCastException) {
// The activity doesn't implement the interface, throw exception
throw ClassCastException(context.toString()
+ " must implement " + CreateOtpListener::class.java.name)
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Retrieve OTP model from instance state
if (savedInstanceState != null) {
if (savedInstanceState.containsKey(KEY_OTP)) {
savedInstanceState.getParcelable<OtpModel>(KEY_OTP)?.let { otpModel ->
mOtpElement = OtpElement(otpModel)
}
}
} else {
arguments?.apply {
if (containsKey(KEY_OTP)) {
getParcelable<OtpModel?>(KEY_OTP)?.let { otpModel ->
mOtpElement = OtpElement(otpModel)
}
}
}
}
activity?.let { activity ->
val root = activity.layoutInflater.inflate(R.layout.fragment_set_otp, null) as ViewGroup?
otpTypeSpinner = root?.findViewById(R.id.setup_otp_type)
otpTokenTypeSpinner = root?.findViewById(R.id.setup_otp_token_type)
otpSecretContainer = root?.findViewById(R.id.setup_otp_secret_label)
otpSecretTextView = root?.findViewById(R.id.setup_otp_secret)
otpAlgorithmSpinner = root?.findViewById(R.id.setup_otp_algorithm)
otpPeriodContainer= root?.findViewById(R.id.setup_otp_period_label)
otpPeriodTextView = root?.findViewById(R.id.setup_otp_period)
otpCounterContainer= root?.findViewById(R.id.setup_otp_counter_label)
otpCounterTextView = root?.findViewById(R.id.setup_otp_counter)
otpDigitsContainer = root?.findViewById(R.id.setup_otp_digits_label)
otpDigitsTextView = root?.findViewById(R.id.setup_otp_digits)
// To fix init element
// With tab keyboard selection
otpSecretTextView?.onFocusChangeListener = mOnFocusChangeListener
// With finger selection
otpTypeSpinner?.setOnTouchListener(mOnTouchListener)
otpTokenTypeSpinner?.setOnTouchListener(mOnTouchListener)
otpSecretTextView?.setOnTouchListener(mOnTouchListener)
otpAlgorithmSpinner?.setOnTouchListener(mOnTouchListener)
otpPeriodTextView?.setOnTouchListener(mOnTouchListener)
otpCounterTextView?.setOnTouchListener(mOnTouchListener)
otpDigitsTextView?.setOnTouchListener(mOnTouchListener)
// HOTP / TOTP Type selection
val otpTypeArray = OtpType.values()
otpTypeAdapter = ArrayAdapter<OtpType>(activity,
android.R.layout.simple_spinner_item, otpTypeArray).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
otpTypeSpinner?.adapter = otpTypeAdapter
// Otp Token type selection
val hotpTokenTypeArray = OtpTokenType.getHotpTokenTypeValues()
hotpTokenTypeAdapter = ArrayAdapter(activity,
android.R.layout.simple_spinner_item, hotpTokenTypeArray).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
// Proprietary only on closed and full version
val totpTokenTypeArray = OtpTokenType.getTotpTokenTypeValues(
BuildConfig.CLOSED_STORE && BuildConfig.FULL_VERSION)
totpTokenTypeAdapter = ArrayAdapter(activity,
android.R.layout.simple_spinner_item, totpTokenTypeArray).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
otpTokenTypeAdapter = hotpTokenTypeAdapter
otpTokenTypeSpinner?.adapter = otpTokenTypeAdapter
// OTP Algorithm
val otpAlgorithmArray = TokenCalculator.HashAlgorithm.values()
otpAlgorithmAdapter = ArrayAdapter<TokenCalculator.HashAlgorithm>(activity,
android.R.layout.simple_spinner_item, otpAlgorithmArray).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
otpAlgorithmSpinner?.adapter = otpAlgorithmAdapter
// Set the default value of OTP element
upgradeType()
upgradeTokenType()
upgradeParameters()
attachListeners()
val builder = AlertDialog.Builder(activity)
builder.apply {
setTitle(R.string.entry_setup_otp)
setView(root)
.setPositiveButton(android.R.string.ok) {_, _ -> }
.setNegativeButton(android.R.string.cancel) { _, _ ->
}
}
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
override fun onResume() {
super.onResume()
(dialog as AlertDialog).getButton(Dialog.BUTTON_POSITIVE).setOnClickListener {
if (mSecretWellFormed
&& mCounterWellFormed
&& mPeriodWellFormed
&& mDigitsWellFormed) {
mCreateOTPElementListener?.onOtpCreated(mOtpElement)
dismiss()
}
}
}
private fun attachListeners() {
// Set Type listener
otpTypeSpinner?.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (mManualEvent) {
(parent?.selectedItem as OtpType?)?.let {
mOtpElement.type = it
upgradeTokenType()
}
}
}
}
// Set type token listener
otpTokenTypeSpinner?.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (mManualEvent) {
(parent?.selectedItem as OtpTokenType?)?.let {
mOtpElement.tokenType = it
upgradeParameters()
}
}
}
}
// Set algorithm spinner
otpAlgorithmSpinner?.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (mManualEvent) {
(parent?.selectedItem as TokenCalculator.HashAlgorithm?)?.let {
mOtpElement.algorithm = it
}
}
}
}
// Set secret in OtpElement
otpSecretTextView?.addTextChangedListener(object: TextWatcher {
override fun afterTextChanged(s: Editable?) {
s?.toString()?.let { userString ->
try {
mOtpElement.setBase32Secret(userString.toUpperCase(Locale.ENGLISH))
otpSecretContainer?.error = null
} catch (exception: Exception) {
otpSecretContainer?.error = getString(R.string.error_otp_secret_key)
}
mSecretWellFormed = otpSecretContainer?.error == null
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
// Set counter in OtpElement
otpCounterTextView?.addTextChangedListener(object: TextWatcher {
override fun afterTextChanged(s: Editable?) {
if (mManualEvent) {
s?.toString()?.toLongOrNull()?.let {
try {
mOtpElement.counter = it
otpCounterContainer?.error = null
} catch (exception: Exception) {
otpCounterContainer?.error = getString(R.string.error_otp_counter,
MIN_HOTP_COUNTER, MAX_HOTP_COUNTER)
}
mCounterWellFormed = otpCounterContainer?.error == null
}
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
// Set period in OtpElement
otpPeriodTextView?.addTextChangedListener(object: TextWatcher {
override fun afterTextChanged(s: Editable?) {
if (mManualEvent) {
s?.toString()?.toIntOrNull()?.let {
try {
mOtpElement.period = it
otpPeriodContainer?.error = null
} catch (exception: Exception) {
otpPeriodContainer?.error = getString(R.string.error_otp_period,
MIN_TOTP_PERIOD, MAX_TOTP_PERIOD)
}
mPeriodWellFormed = otpPeriodContainer?.error == null
}
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
// Set digits in OtpElement
otpDigitsTextView?.addTextChangedListener(object: TextWatcher {
override fun afterTextChanged(s: Editable?) {
if (mManualEvent) {
s?.toString()?.toIntOrNull()?.let {
try {
mOtpElement.digits = it
otpDigitsContainer?.error = null
} catch (exception: Exception) {
otpDigitsContainer?.error = getString(R.string.error_otp_digits,
MIN_OTP_DIGITS, MAX_OTP_DIGITS)
}
mDigitsWellFormed = otpDigitsContainer?.error == null
}
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
}
private fun upgradeType() {
otpTypeSpinner?.setSelection(OtpType.values().indexOf(mOtpElement.type))
}
private fun upgradeTokenType() {
when (mOtpElement.type) {
OtpType.HOTP -> {
otpPeriodContainer?.visibility = View.GONE
otpCounterContainer?.visibility = View.VISIBLE
otpTokenTypeSpinner?.adapter = hotpTokenTypeAdapter
otpTokenTypeSpinner?.setSelection(OtpTokenType
.getHotpTokenTypeValues().indexOf(mOtpElement.tokenType))
}
OtpType.TOTP -> {
otpPeriodContainer?.visibility = View.VISIBLE
otpCounterContainer?.visibility = View.GONE
otpTokenTypeSpinner?.adapter = totpTokenTypeAdapter
otpTokenTypeSpinner?.setSelection(OtpTokenType
.getTotpTokenTypeValues().indexOf(mOtpElement.tokenType))
}
}
}
private fun upgradeParameters() {
otpAlgorithmSpinner?.setSelection(TokenCalculator.HashAlgorithm.values()
.indexOf(mOtpElement.algorithm))
otpSecretTextView?.apply {
setText(mOtpElement.getBase32Secret())
// Cursor at end
setSelection(this.text.length)
}
otpCounterTextView?.setText(mOtpElement.counter.toString())
otpPeriodTextView?.setText(mOtpElement.period.toString())
otpDigitsTextView?.setText(mOtpElement.digits.toString())
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(KEY_OTP, mOtpElement.otpModel)
}
interface CreateOtpListener {
fun onOtpCreated(otpElement: OtpElement)
}
companion object {
private const val KEY_OTP = "KEY_OTP"
fun build(otpModel: OtpModel? = null): SetOTPDialogFragment {
return SetOTPDialogFragment().apply {
if (otpModel != null) {
arguments = Bundle().apply {
putParcelable(KEY_OTP, otpModel)
}
}
}
}
}
}

View File

@@ -1,20 +1,20 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
@@ -22,14 +22,14 @@ package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.support.annotation.IdRes import androidx.annotation.IdRes
import android.support.v4.app.DialogFragment import androidx.fragment.app.DialogFragment
import android.support.v7.app.AlertDialog import androidx.appcompat.app.AlertDialog
import android.view.View import android.view.View
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.RadioGroup import android.widget.RadioGroup
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.SortNodeEnum import com.kunzisoft.keepass.database.element.SortNodeEnum
class SortDialogFragment : DialogFragment() { class SortDialogFragment : DialogFragment() {
@@ -42,12 +42,14 @@ class SortDialogFragment : DialogFragment() {
private var mAscending: Boolean = true private var mAscending: Boolean = true
private var mRecycleBinBottom: Boolean = true private var mRecycleBinBottom: Boolean = true
override fun onAttach(context: Context?) { private var recycleBinBottomView: CompoundButton? = null
override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
try { try {
mListener = context as SortSelectionListener? mListener = context as SortSelectionListener
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
throw ClassCastException(context!!.toString() throw ClassCastException(context.toString()
+ " must implement " + SortSelectionListener::class.java.name) + " must implement " + SortSelectionListener::class.java.name)
} }
} }
@@ -73,15 +75,17 @@ class SortDialogFragment : DialogFragment() {
} }
} }
mCheckedId = retrieveViewFromEnum(mSortNodeEnum!!) mCheckedId = retrieveViewFromEnum(mSortNodeEnum)
val rootView = activity.layoutInflater.inflate(R.layout.fragment_sort_selection, null) val rootView = activity.layoutInflater.inflate(R.layout.fragment_sort_selection, null)
builder.setTitle(R.string.sort_menu) builder.setTitle(R.string.sort_menu)
builder.setView(rootView) builder.setView(rootView)
// Add action buttons // Add action buttons
.setPositiveButton(android.R.string.ok .setPositiveButton(android.R.string.ok
) { _, _ -> mListener?.onSortSelected(mSortNodeEnum!!, mAscending, mGroupsBefore, mRecycleBinBottom) } ) { _, _ -> mListener?.onSortSelected(mSortNodeEnum,
.setNegativeButton(R.string.cancel) { _, _ -> } SortNodeEnum.SortNodeParameters(mAscending, mGroupsBefore, mRecycleBinBottom))
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
val ascendingView = rootView.findViewById<CompoundButton>(R.id.sort_selection_ascending) val ascendingView = rootView.findViewById<CompoundButton>(R.id.sort_selection_ascending)
// Check if is ascending or descending // Check if is ascending or descending
@@ -93,25 +97,35 @@ class SortDialogFragment : DialogFragment() {
groupsBeforeView.isChecked = mGroupsBefore groupsBeforeView.isChecked = mGroupsBefore
groupsBeforeView.setOnCheckedChangeListener { _, isChecked -> mGroupsBefore = isChecked } groupsBeforeView.setOnCheckedChangeListener { _, isChecked -> mGroupsBefore = isChecked }
val recycleBinBottomView = rootView.findViewById<CompoundButton>(R.id.sort_selection_recycle_bin_bottom) recycleBinBottomView = rootView.findViewById(R.id.sort_selection_recycle_bin_bottom)
if (!recycleBinAllowed) { if (!recycleBinAllowed) {
recycleBinBottomView.visibility = View.GONE recycleBinBottomView?.visibility = View.GONE
} else { } else {
// Check if recycle bin at the bottom // Check if recycle bin at the bottom
recycleBinBottomView.isChecked = mRecycleBinBottom recycleBinBottomView?.isChecked = mRecycleBinBottom
recycleBinBottomView.setOnCheckedChangeListener { _, isChecked -> mRecycleBinBottom = isChecked } recycleBinBottomView?.setOnCheckedChangeListener { _, isChecked -> mRecycleBinBottom = isChecked }
disableRecycleBinBottomOptionIfNaturalOrder()
} }
val sortSelectionRadioGroupView = rootView.findViewById<RadioGroup>(R.id.sort_selection_radio_group) val sortSelectionRadioGroupView = rootView.findViewById<RadioGroup>(R.id.sort_selection_radio_group)
// Check value by default // Check value by default
sortSelectionRadioGroupView.check(mCheckedId) sortSelectionRadioGroupView.check(mCheckedId)
sortSelectionRadioGroupView.setOnCheckedChangeListener { _, checkedId -> mSortNodeEnum = retrieveSortEnumFromViewId(checkedId) } sortSelectionRadioGroupView.setOnCheckedChangeListener { _, checkedId ->
mSortNodeEnum = retrieveSortEnumFromViewId(checkedId)
disableRecycleBinBottomOptionIfNaturalOrder()
}
return builder.create() return builder.create()
} }
return super.onCreateDialog(savedInstanceState) return super.onCreateDialog(savedInstanceState)
} }
private fun disableRecycleBinBottomOptionIfNaturalOrder() {
// Disable recycle bin if natural order
recycleBinBottomView?.isEnabled = mSortNodeEnum != SortNodeEnum.DB
}
@IdRes @IdRes
private fun retrieveViewFromEnum(sortNodeEnum: SortNodeEnum): Int { private fun retrieveViewFromEnum(sortNodeEnum: SortNodeEnum): Int {
return when (sortNodeEnum) { return when (sortNodeEnum) {
@@ -138,10 +152,7 @@ class SortDialogFragment : DialogFragment() {
} }
interface SortSelectionListener { interface SortSelectionListener {
fun onSortSelected(sortNodeEnum: SortNodeEnum, fun onSortSelected(sortNodeEnum: SortNodeEnum, sortNodeParameters: SortNodeEnum.SortNodeParameters)
ascending: Boolean,
groupsBefore: Boolean,
recycleBinBottom: Boolean)
} }
companion object { companion object {

View File

@@ -0,0 +1,57 @@
package com.kunzisoft.keepass.activities.dialogs
import android.app.DatePickerDialog
import android.app.Dialog
import android.app.TimePickerDialog
import android.content.Context
import android.os.Bundle
import android.text.format.DateFormat
import androidx.fragment.app.DialogFragment
class TimePickerFragment : DialogFragment() {
private var defaultHour: Int = 0
private var defaultMinute: Int = 0
private var mListener: TimePickerDialog.OnTimeSetListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
try {
mListener = context as TimePickerDialog.OnTimeSetListener
} catch (e: ClassCastException) {
throw ClassCastException(context.toString()
+ " must implement " + DatePickerDialog.OnDateSetListener::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Create a new instance of DatePickerDialog and return it
return context?.let {
arguments?.apply {
if (containsKey(DEFAULT_HOUR_BUNDLE_KEY))
defaultHour = getInt(DEFAULT_HOUR_BUNDLE_KEY)
if (containsKey(DEFAULT_MINUTE_BUNDLE_KEY))
defaultMinute = getInt(DEFAULT_MINUTE_BUNDLE_KEY)
}
TimePickerDialog(it, mListener, defaultHour, defaultMinute, DateFormat.is24HourFormat(activity))
} ?: super.onCreateDialog(savedInstanceState)
}
companion object {
private const val DEFAULT_HOUR_BUNDLE_KEY = "DEFAULT_HOUR_BUNDLE_KEY"
private const val DEFAULT_MINUTE_BUNDLE_KEY = "DEFAULT_MINUTE_BUNDLE_KEY"
fun getInstance(defaultHour: Int,
defaultMinute: Int): TimePickerFragment {
return TimePickerFragment().apply {
arguments = Bundle().apply {
putInt(DEFAULT_HOUR_BUNDLE_KEY, defaultHour)
putInt(DEFAULT_MINUTE_BUNDLE_KEY, defaultMinute)
}
}
}
}
}

View File

@@ -1,20 +1,20 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
@@ -22,12 +22,12 @@ package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog import android.app.Dialog
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.DialogFragment import androidx.fragment.app.DialogFragment
import android.support.v7.app.AlertDialog import androidx.appcompat.app.AlertDialog
import android.text.Html
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.widget.TextView import android.widget.TextView
import androidx.core.text.HtmlCompat
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
class UnavailableFeatureDialogFragment : DialogFragment() { class UnavailableFeatureDialogFragment : DialogFragment() {
@@ -53,7 +53,7 @@ class UnavailableFeatureDialogFragment : DialogFragment() {
androidNameFromApiNumber(Build.VERSION.SDK_INT, Build.VERSION.RELEASE), androidNameFromApiNumber(Build.VERSION.SDK_INT, Build.VERSION.RELEASE),
androidNameFromApiNumber(minVersionRequired))) androidNameFromApiNumber(minVersionRequired)))
message.append("\n\n") message.append("\n\n")
.append(Html.fromHtml("<a href=\"https://source.android.com/setup/build-numbers\">CodeNames</a>")) .append(HtmlCompat.fromHtml("<a href=\"https://source.android.com/setup/build-numbers\">CodeNames</a>", HtmlCompat.FROM_HTML_MODE_LEGACY))
} else } else
message.append(getString(R.string.unavailable_feature_hardware)) message.append(getString(R.string.unavailable_feature_hardware))

View File

@@ -1,36 +1,33 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.dialogs package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog import android.app.Dialog
import android.content.ActivityNotFoundException
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.text.Html
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.widget.Toast import androidx.appcompat.app.AlertDialog
import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.BuildConfig import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.Util import com.kunzisoft.keepass.utils.UriUtil
/** /**
* Custom Dialog that asks the user to download the pro version or make a donation. * Custom Dialog that asks the user to download the pro version or make a donation.
@@ -45,34 +42,26 @@ class UnderDevelopmentFeatureDialogFragment : DialogFragment() {
val stringBuilder = SpannableStringBuilder() val stringBuilder = SpannableStringBuilder()
if (BuildConfig.CLOSED_STORE) { if (BuildConfig.CLOSED_STORE) {
if (BuildConfig.FULL_VERSION) { if (BuildConfig.FULL_VERSION) {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature_thanks))).append("\n\n") stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_thanks), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_rose))).append("\n\n") .append(HtmlCompat.fromHtml(getString(R.string.html_rose), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_work_hard))).append("\n") .append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_work_hard), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_upgrade))).append(" ") .append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_upgrade), HtmlCompat.FROM_HTML_MODE_LEGACY)).append(" ")
builder.setPositiveButton(android.R.string.ok) { _, _ -> dismiss() } builder.setPositiveButton(android.R.string.ok) { _, _ -> dismiss() }
} else { } else {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature))).append("\n\n") stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_buy_pro))).append("\n") .append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_buy_pro), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_encourage))) .append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_encourage), HtmlCompat.FROM_HTML_MODE_LEGACY))
builder.setPositiveButton(R.string.download) { _, _ -> builder.setPositiveButton(R.string.download) { _, _ ->
try { UriUtil.gotoUrl(requireContext(), R.string.app_pro_url)
Util.gotoUrl(context!!, R.string.app_pro_url)
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show()
}
} }
builder.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() } builder.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() }
} }
} else { } else {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature))).append("\n\n") stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_contibute))).append(" ") .append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_contibute), HtmlCompat.FROM_HTML_MODE_LEGACY)).append(" ")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_encourage))) .append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_encourage), HtmlCompat.FROM_HTML_MODE_LEGACY))
builder.setPositiveButton(R.string.contribute) { _, _ -> builder.setPositiveButton(R.string.contribute) { _, _ ->
try { UriUtil.gotoUrl(requireContext(), R.string.contribution_url)
Util.gotoUrl(context!!, R.string.contribution_url)
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show()
}
} }
builder.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() } builder.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() }
} }

View File

@@ -1,84 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.helpers;
import android.content.Intent;
import android.net.Uri;
import java.lang.reflect.Method;
public class ClipDataCompat {
private static Method getClipDataFromIntent;
private static Method getDescription;
private static Method getItemCount;
private static Method getLabel;
private static Method getItemAt;
private static Method getUri;
private static boolean initSucceded;
static {
try {
Class clipData = Class.forName("android.content.ClipData");
getDescription = clipData.getMethod("getDescription", (Class[])null);
getItemCount = clipData.getMethod("getItemCount", (Class[])null);
getItemAt = clipData.getMethod("getItemAt", new Class[]{int.class});
Class clipDescription = Class.forName("android.content.ClipDescription");
getLabel = clipDescription.getMethod("getLabel", (Class[])null);
Class clipDataItem = Class.forName("android.content.ClipData$Item");
getUri = clipDataItem.getMethod("getUri", (Class[])null);
getClipDataFromIntent = Intent.class.getMethod("getClipData", (Class[])null);
initSucceded = true;
} catch (Exception e) {
initSucceded = false;
}
}
public static Uri getUriFromIntent(Intent i, String key) {
if (initSucceded) {
try {
Object clip = getClipDataFromIntent.invoke(i);
if (clip != null) {
Object clipDescription = getDescription.invoke(clip);
CharSequence label = (CharSequence)getLabel.invoke(clipDescription);
if (label.equals(key)) {
int itemCount = (int) getItemCount.invoke(clip);
if (itemCount == 1) {
Object clipItem = getItemAt.invoke(clip,0);
if (clipItem != null) {
return (Uri)getUri.invoke(clipItem);
}
}
}
}
return null;
} catch (Exception e) {
// Fall through below to backup method if reflection fails
}
}
return i.getParcelableExtra(key);
}
}

View File

@@ -1,6 +1,26 @@
/*
* Copyright 2019 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.activities.helpers package com.kunzisoft.keepass.activities.helpers
import android.app.assist.AssistStructure import android.app.assist.AssistStructure
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.autofill.AutofillHelper
@@ -10,6 +30,12 @@ object EntrySelectionHelper {
private const val EXTRA_ENTRY_SELECTION_MODE = "com.kunzisoft.keepass.extra.ENTRY_SELECTION_MODE" private const val EXTRA_ENTRY_SELECTION_MODE = "com.kunzisoft.keepass.extra.ENTRY_SELECTION_MODE"
private const val DEFAULT_ENTRY_SELECTION_MODE = false private const val DEFAULT_ENTRY_SELECTION_MODE = false
fun startActivityForEntrySelection(context: Context, intent: Intent) {
addEntrySelectionModeExtraInIntent(intent)
// only to avoid visible flickering when redirecting
context.startActivity(intent)
}
fun addEntrySelectionModeExtraInIntent(intent: Intent) { fun addEntrySelectionModeExtraInIntent(intent: Intent) {
intent.putExtra(EXTRA_ENTRY_SELECTION_MODE, true) intent.putExtra(EXTRA_ENTRY_SELECTION_MODE, true)
} }

View File

@@ -1,24 +1,25 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.helpers package com.kunzisoft.keepass.activities.helpers
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.Activity.RESULT_OK import android.app.Activity.RESULT_OK
import android.content.Context import android.content.Context
@@ -26,21 +27,20 @@ import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import com.kunzisoft.keepass.activities.dialogs.BrowserDialogFragment import androidx.fragment.app.Fragment
import com.kunzisoft.keepass.fileselect.StorageAF import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.activities.dialogs.FileManagerDialogFragment
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
class KeyFileHelper { class OpenFileHelper {
private var activity: Activity? = null private var activity: Activity? = null
private var fragment: Fragment? = null private var fragment: Fragment? = null
val openFileOnClickViewListener: OpenFileOnClickViewListener val openFileOnClickViewListener: OpenFileOnClickViewListener
get() = OpenFileOnClickViewListener(null) get() = OpenFileOnClickViewListener()
constructor(context: Activity) { constructor(context: Activity) {
this.activity = context this.activity = context
@@ -52,69 +52,78 @@ class KeyFileHelper {
this.fragment = context this.fragment = context
} }
inner class OpenFileOnClickViewListener(private val dataUri: (() -> Uri)?) : View.OnClickListener { inner class OpenFileOnClickViewListener : View.OnClickListener, View.OnLongClickListener {
override fun onClick(v: View) { private fun onAbstractClick(longClick: Boolean = false) {
try { try {
if (activity != null && StorageAF.useStorageFramework(activity!!)) { if (longClick) {
openActivityWithActionOpenDocument() try {
openActivityWithActionGetContent()
} catch (e: Exception) {
openActivityWithActionOpenDocument()
}
} else { } else {
openActivityWithActionGetContent() try {
openActivityWithActionOpenDocument()
} catch (e: Exception) {
openActivityWithActionGetContent()
}
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Enable to start the file picker activity", e) Log.e(TAG, "Enable to start the file picker activity", e)
// Open browser dialog
// Open File picker if can't open activity if (lookForOpenIntentsFilePicker())
if (lookForOpenIntentsFilePicker(dataUri?.invoke()))
showBrowserDialog() showBrowserDialog()
} }
} }
override fun onClick(v: View) {
onAbstractClick()
}
override fun onLongClick(v: View?): Boolean {
onAbstractClick(true)
return true
}
} }
@SuppressLint("InlinedApi")
private fun openActivityWithActionOpenDocument() { private fun openActivityWithActionOpenDocument() {
val i = Intent(StorageAF.ACTION_OPEN_DOCUMENT) val intentOpenDocument = Intent(APP_ACTION_OPEN_DOCUMENT).apply {
i.addCategory(Intent.CATEGORY_OPENABLE) addCategory(Intent.CATEGORY_OPENABLE)
i.type = "*/*" type = "*/*"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
i.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION
} else {
i.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
} }
if (fragment != null) if (fragment != null)
fragment?.startActivityForResult(i, OPEN_DOC) fragment?.startActivityForResult(intentOpenDocument, OPEN_DOC)
else else
activity?.startActivityForResult(i, OPEN_DOC) activity?.startActivityForResult(intentOpenDocument, OPEN_DOC)
} }
@SuppressLint("InlinedApi")
private fun openActivityWithActionGetContent() { private fun openActivityWithActionGetContent() {
val i = Intent(Intent.ACTION_GET_CONTENT) val intentGetContent = Intent(Intent.ACTION_GET_CONTENT).apply {
i.addCategory(Intent.CATEGORY_OPENABLE) addCategory(Intent.CATEGORY_OPENABLE)
i.type = "*/*" type = "*/*"
flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
}
if (fragment != null) if (fragment != null)
fragment?.startActivityForResult(i, GET_CONTENT) fragment?.startActivityForResult(intentGetContent, GET_CONTENT)
else else
activity?.startActivityForResult(i, GET_CONTENT) activity?.startActivityForResult(intentGetContent, GET_CONTENT)
} }
fun getOpenFileOnClickViewListener(dataUri: () -> Uri): OpenFileOnClickViewListener { private fun lookForOpenIntentsFilePicker(): Boolean {
return OpenFileOnClickViewListener(dataUri)
}
private fun lookForOpenIntentsFilePicker(dataUri: Uri?): Boolean {
var showBrowser = false var showBrowser = false
try { try {
if (isIntentAvailable(activity!!, OPEN_INTENTS_FILE_BROWSE)) { if (isIntentAvailable(activity!!, OPEN_INTENTS_FILE_BROWSE)) {
val intent = Intent(OPEN_INTENTS_FILE_BROWSE) val intent = Intent(OPEN_INTENTS_FILE_BROWSE)
// Get file path parent if possible
if (dataUri != null
&& dataUri.toString().isNotEmpty()
&& dataUri.scheme == "file") {
intent.data = dataUri
} else {
Log.w(javaClass.name, "Unable to read the URI")
}
if (fragment != null) if (fragment != null)
fragment?.startActivityForResult(intent, FILE_BROWSE) fragment?.startActivityForResult(intent, FILE_BROWSE)
else else
@@ -155,11 +164,10 @@ class KeyFileHelper {
*/ */
private fun showBrowserDialog() { private fun showBrowserDialog() {
try { try {
val browserDialogFragment = BrowserDialogFragment() val fileManagerDialogFragment = FileManagerDialogFragment()
if (fragment != null && fragment!!.fragmentManager != null) fragment?.let {
browserDialogFragment.show(fragment!!.fragmentManager!!, "browserDialog") fileManagerDialogFragment.show(it.parentFragmentManager, "browserDialog")
else if (activity!!.fragmentManager != null) } ?: fileManagerDialogFragment.show((activity as FragmentActivity).supportFragmentManager, "browserDialog")
browserDialogFragment.show((activity as FragmentActivity).supportFragmentManager, "browserDialog")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Can't open BrowserDialog", e) Log.e(TAG, "Can't open BrowserDialog", e)
} }
@@ -182,7 +190,7 @@ class KeyFileHelper {
val filename = data?.dataString val filename = data?.dataString
var keyUri: Uri? = null var keyUri: Uri? = null
if (filename != null) { if (filename != null) {
keyUri = UriUtil.parseUriFile(filename) keyUri = UriUtil.parse(filename)
} }
keyFileCallback?.invoke(keyUri) keyFileCallback?.invoke(keyUri)
} }
@@ -191,23 +199,18 @@ class KeyFileHelper {
GET_CONTENT, OPEN_DOC -> { GET_CONTENT, OPEN_DOC -> {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
if (data != null) { if (data != null) {
var uri = data.data val uri = data.data
if (uri != null) { if (uri != null) {
if (StorageAF.useStorageFramework(activity!!)) { try {
try { // try to persist read and write permissions
// try to persist read and write permissions if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { activity?.contentResolver?.apply {
activity?.contentResolver?.apply { takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
takePersistableUriPermission(uri!!, Intent.FLAG_GRANT_READ_URI_PERMISSION) takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
takePersistableUriPermission(uri!!, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
} }
} catch (e: Exception) {
// nop
} }
} } catch (e: Exception) {
if (requestCode == GET_CONTENT) { // nop
uri = UriUtil.translateUri(activity!!, uri)
} }
keyFileCallback?.invoke(uri) keyFileCallback?.invoke(uri)
} }
@@ -221,7 +224,13 @@ class KeyFileHelper {
companion object { companion object {
private const val TAG = "KeyFileHelper" private const val TAG = "OpenFileHelper"
private var APP_ACTION_OPEN_DOCUMENT: String = try {
Intent::class.java.getField("ACTION_OPEN_DOCUMENT").get(null) as String
} catch (e: Exception) {
"android.intent.action.OPEN_DOCUMENT"
}
const val OPEN_INTENTS_FILE_BROWSE = "org.openintents.action.PICK_FILE" const val OPEN_INTENTS_FILE_BROWSE = "org.openintents.action.PICK_FILE"

View File

@@ -1,3 +1,22 @@
/*
* Copyright 2019 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.activities.helpers package com.kunzisoft.keepass.activities.helpers
import android.content.Context import android.content.Context

View File

@@ -1,88 +0,0 @@
package com.kunzisoft.keepass.activities.helpers
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.AsyncTask
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
import com.kunzisoft.keepass.utils.UriUtil
import java.io.File
import java.lang.ref.WeakReference
class UriIntentInitTask(private val weakContext: WeakReference<Context>,
private val uriIntentInitTaskCallback: UriIntentInitTaskCallback,
private val isKeyFileNeeded: Boolean)
: AsyncTask<Intent, Void, Int>() {
private var databaseUri: Uri? = null
private var keyFileUri: Uri? = null
override fun doInBackground(vararg args: Intent): Int? {
val intent = args[0]
val action = intent.action
// If is a view intent
if (action != null && action == VIEW_INTENT) {
val incoming = intent.data
databaseUri = incoming
keyFileUri = ClipDataCompat.getUriFromIntent(intent, KEY_KEYFILE)
if (incoming == null) {
return R.string.error_can_not_handle_uri
} else if (incoming.scheme == "file") {
val fileName = incoming.path
if (fileName?.isNotEmpty() == true) {
// No file name
return R.string.file_not_found
}
val dbFile = File(fileName)
if (!dbFile.exists()) {
// File does not exist
return R.string.file_not_found
}
if (keyFileUri == null) {
keyFileUri = getKeyFileUri(databaseUri)
}
} else if (incoming.scheme == "content") {
if (keyFileUri == null) {
keyFileUri = getKeyFileUri(databaseUri)
}
} else {
return R.string.error_can_not_handle_uri
}
} else {
databaseUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_FILENAME))
keyFileUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_KEYFILE))
if (keyFileUri == null || keyFileUri!!.toString().isEmpty()) {
keyFileUri = getKeyFileUri(databaseUri)
}
}
return null
}
public override fun onPostExecute(result: Int?) {
uriIntentInitTaskCallback.onPostInitTask(databaseUri, keyFileUri, result)
}
private fun getKeyFileUri(databaseUri: Uri?): Uri? {
return if (isKeyFileNeeded) {
FileDatabaseHistory.getInstance(weakContext).getKeyFileUriByDatabaseUri(databaseUri!!)
} else {
null
}
}
companion object {
const val KEY_FILENAME = "fileName"
const val KEY_KEYFILE = "keyFile"
private const val VIEW_INTENT = "android.intent.action.VIEW"
}
}

View File

@@ -1,26 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.helpers
import android.net.Uri
interface UriIntentInitTaskCallback {
fun onPostInitTask(databaseFileUri: Uri?, keyFileUri: Uri?, errorStringId: Int?)
}

View File

@@ -1,30 +1,25 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.lock package com.kunzisoft.keepass.activities.lock
import android.app.Activity
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
@@ -32,12 +27,11 @@ import android.view.ViewGroup
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
import com.kunzisoft.keepass.magikeyboard.MagikIME
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.LOCK_ACTION import com.kunzisoft.keepass.utils.*
abstract class LockingActivity : StylishActivity() { abstract class LockingActivity : StylishActivity() {
@@ -51,46 +45,53 @@ abstract class LockingActivity : StylishActivity() {
const val TIMEOUT_ENABLE_KEY_DEFAULT = true const val TIMEOUT_ENABLE_KEY_DEFAULT = true
} }
protected var timeoutEnable: Boolean = true protected var mTimeoutEnable: Boolean = true
private var lockReceiver: LockReceiver? = null private var mLockReceiver: LockReceiver? = null
private var exitLock: Boolean = false private var mExitLock: Boolean = false
// Force readOnly if Entry Selection mode // Force readOnly if Entry Selection mode
protected var readOnly: Boolean = false protected var mReadOnly: Boolean = false
get() { get() {
return field || selectionMode return field || mSelectionMode
} }
protected var selectionMode: Boolean = false protected var mSelectionMode: Boolean = false
protected var mAutoSaveEnable: Boolean = true
var mProgressDialogThread: ProgressDialogThread? = null
private set
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (savedInstanceState != null if (savedInstanceState != null
&& savedInstanceState.containsKey(TIMEOUT_ENABLE_KEY)) { && savedInstanceState.containsKey(TIMEOUT_ENABLE_KEY)) {
timeoutEnable = savedInstanceState.getBoolean(TIMEOUT_ENABLE_KEY) mTimeoutEnable = savedInstanceState.getBoolean(TIMEOUT_ENABLE_KEY)
} else { } else {
if (intent != null) if (intent != null)
timeoutEnable = intent.getBooleanExtra(TIMEOUT_ENABLE_KEY, TIMEOUT_ENABLE_KEY_DEFAULT) mTimeoutEnable = intent.getBooleanExtra(TIMEOUT_ENABLE_KEY, TIMEOUT_ENABLE_KEY_DEFAULT)
} }
if (timeoutEnable) { if (mTimeoutEnable) {
lockReceiver = LockReceiver() mLockReceiver = LockReceiver {
val intentFilter = IntentFilter().apply { closeDatabase()
addAction(Intent.ACTION_SCREEN_OFF) // Add onActivityForResult response
addAction(LOCK_ACTION) setResult(RESULT_EXIT_LOCK)
finish()
} }
registerReceiver(lockReceiver, intentFilter) registerLockReceiver(mLockReceiver)
} }
exitLock = false mExitLock = false
readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, intent) mReadOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, intent)
mProgressDialogThread = ProgressDialogThread(this)
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_EXIT_LOCK) { if (resultCode == RESULT_EXIT_LOCK) {
exitLock = true mExitLock = true
if (Database.getInstance().loaded) { if (Database.getInstance().loaded) {
lockAndExit() lockAndExit()
} }
@@ -100,10 +101,15 @@ abstract class LockingActivity : StylishActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
// To refresh when back to normal workflow from selection workflow mProgressDialogThread?.registerProgressTask()
selectionMode = EntrySelectionHelper.retrieveEntrySelectionModeFromIntent(intent)
if (timeoutEnable) { // To refresh when back to normal workflow from selection workflow
mSelectionMode = EntrySelectionHelper.retrieveEntrySelectionModeFromIntent(intent)
mAutoSaveEnable = PreferencesUtil.isAutoSaveDatabaseEnabled(this)
invalidateOptionsMenu()
if (mTimeoutEnable) {
// End activity if database not loaded // End activity if database not loaded
if (!Database.getInstance().loaded) { if (!Database.getInstance().loaded) {
finish() finish()
@@ -115,53 +121,35 @@ abstract class LockingActivity : StylishActivity() {
// If the time is out -> close the Activity // If the time is out -> close the Activity
TimeoutHelper.checkTimeAndLockIfTimeout(this) TimeoutHelper.checkTimeAndLockIfTimeout(this)
// If onCreate already record time // If onCreate already record time
if (!exitLock) if (!mExitLock)
TimeoutHelper.recordTime(this) TimeoutHelper.recordTime(this)
} }
invalidateOptionsMenu()
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
ReadOnlyHelper.onSaveInstanceState(outState, readOnly) ReadOnlyHelper.onSaveInstanceState(outState, mReadOnly)
outState.putBoolean(TIMEOUT_ENABLE_KEY, timeoutEnable) outState.putBoolean(TIMEOUT_ENABLE_KEY, mTimeoutEnable)
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
} }
override fun onPause() { override fun onPause() {
mProgressDialogThread?.unregisterProgressTask()
super.onPause() super.onPause()
if (timeoutEnable) { if (mTimeoutEnable) {
// If the time is out during our navigation in activity -> close the Activity // If the time is out during our navigation in activity -> close the Activity
TimeoutHelper.checkTimeAndLockIfTimeout(this) TimeoutHelper.checkTimeAndLockIfTimeout(this)
} }
} }
override fun onDestroy() { override fun onDestroy() {
unregisterLockReceiver(mLockReceiver)
super.onDestroy() super.onDestroy()
if (lockReceiver != null)
unregisterReceiver(lockReceiver)
}
inner class LockReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// If allowed, lock and exit
if (!TimeoutHelper.temporarilyDisableTimeout) {
intent.action?.let {
when (it) {
Intent.ACTION_SCREEN_OFF -> if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this@LockingActivity)) {
lockAndExit()
}
LOCK_ACTION -> lockAndExit()
}
}
}
}
} }
protected fun lockAndExit() { protected fun lockAndExit() {
lock() sendBroadcast(Intent(LOCK_ACTION))
} }
/** /**
@@ -184,7 +172,7 @@ abstract class LockingActivity : StylishActivity() {
} }
override fun onBackPressed() { override fun onBackPressed() {
if (timeoutEnable) { if (mTimeoutEnable) {
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this) { TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this) {
super.onBackPressed() super.onBackPressed()
} }
@@ -193,20 +181,3 @@ abstract class LockingActivity : StylishActivity() {
} }
} }
} }
fun Activity.lock() {
// Stop the Magikeyboard service
stopService(Intent(this, KeyboardEntryNotificationService::class.java))
MagikIME.removeEntry(this)
Log.i(Activity::class.java.name, "Shutdown " + localClassName +
" after inactivity or manual lock")
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).apply {
cancelAll()
}
// Clear data
Database.getInstance().closeAndClear(applicationContext.filesDir)
// Add onActivityForResult response
setResult(LockingActivity.RESULT_EXIT_LOCK)
finish()
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.lock
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.view.WindowManager
/**
* Locking Hide Activity that sets FLAG_SECURE to prevent screenshots, and from
* appearing in the recent app preview
*/
abstract class LockingHideActivity : LockingActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Several gingerbread devices have problems with FLAG_SECURE
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
}
/* (non-Javadoc) Workaround for HTC Linkify issues
* @see android.app.Activity#startActivity(android.content.Intent)
*/
override fun startActivity(intent: Intent) {
try {
if (intent.component != null && intent.component!!.shortClassName == ".HtcLinkifyDispatcherActivity") {
intent.component = null
}
super.startActivity(intent)
} catch (e: ActivityNotFoundException) {
/* Catch the bad HTC implementation case */
super.startActivity(Intent.createChooser(intent, null))
}
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.stylish
import android.os.Bundle
import android.support.annotation.StyleRes
import android.util.Log
import com.nononsenseapps.filepicker.FilePickerActivity
/**
* FilePickerActivity class with a style compatibility
*/
class FilePickerStylishActivity : FilePickerActivity() {
@StyleRes
private var themeId: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
this.themeId = Stylish.getFilePickerThemeId(this)
setTheme(themeId)
super.onCreate(savedInstanceState)
}
override fun onResume() {
super.onResume()
if (Stylish.getFilePickerThemeId(this) != this.themeId) {
Log.d(this.javaClass.name, "Theme change detected, restarting activity")
this.recreate()
}
}
}

View File

@@ -1,27 +1,27 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.stylish package com.kunzisoft.keepass.activities.stylish
import android.content.Context import android.content.Context
import android.support.annotation.StyleRes import androidx.annotation.StyleRes
import android.support.v7.preference.PreferenceManager import androidx.preference.PreferenceManager
import android.util.Log import android.util.Log
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
@@ -61,6 +61,7 @@ object Stylish {
return when (themeString) { return when (themeString) {
context.getString(R.string.list_style_name_night) -> R.style.KeepassDXStyle_Night context.getString(R.string.list_style_name_night) -> R.style.KeepassDXStyle_Night
context.getString(R.string.list_style_name_black) -> R.style.KeepassDXStyle_Black
context.getString(R.string.list_style_name_dark) -> R.style.KeepassDXStyle_Dark context.getString(R.string.list_style_name_dark) -> R.style.KeepassDXStyle_Dark
context.getString(R.string.list_style_name_blue) -> R.style.KeepassDXStyle_Blue context.getString(R.string.list_style_name_blue) -> R.style.KeepassDXStyle_Blue
context.getString(R.string.list_style_name_red) -> R.style.KeepassDXStyle_Red context.getString(R.string.list_style_name_red) -> R.style.KeepassDXStyle_Red
@@ -68,15 +69,4 @@ object Stylish {
else -> R.style.KeepassDXStyle_Light else -> R.style.KeepassDXStyle_Light
} }
} }
@StyleRes
fun getFilePickerThemeId(context: Context): Int {
return when {
themeString.equals(context.getString(R.string.list_style_name_dark)) -> R.style.KeepassDXStyle_FilePickerStyle_Dark
themeString.equals(context.getString(R.string.list_style_name_blue)) -> R.style.KeepassDXStyle_FilePickerStyle_Blue
themeString.equals(context.getString(R.string.list_style_name_red)) -> R.style.KeepassDXStyle_FilePickerStyle_Red
themeString.equals(context.getString(R.string.list_style_name_purple)) -> R.style.KeepassDXStyle_FilePickerStyle_Purple
else -> R.style.KeepassDXStyle_FilePickerStyle
}
}
} }

View File

@@ -1,38 +1,64 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.stylish package com.kunzisoft.keepass.activities.stylish
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.annotation.StyleRes import androidx.annotation.StyleRes
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.util.Log import android.util.Log
import android.view.WindowManager
/**
* Stylish Hide Activity that apply a dynamic style and sets FLAG_SECURE to prevent screenshots / from
* appearing in the recent app preview
*/
abstract class StylishActivity : AppCompatActivity() { abstract class StylishActivity : AppCompatActivity() {
@StyleRes @StyleRes
private var themeId: Int = 0 private var themeId: Int = 0
/* (non-Javadoc) Workaround for HTC Linkify issues
* @see android.app.Activity#startActivity(android.content.Intent)
*/
override fun startActivity(intent: Intent) {
try {
intent.component?.let {
if (it.shortClassName == ".HtcLinkifyDispatcherActivity")
intent.component = null
}
super.startActivity(intent)
} catch (e: ActivityNotFoundException) {
/* Catch the bad HTC implementation case */
super.startActivity(Intent.createChooser(intent, null))
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
this.themeId = Stylish.getThemeId(this) this.themeId = Stylish.getThemeId(this)
setTheme(themeId) setTheme(themeId)
// Several gingerbread devices have problems with FLAG_SECURE
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
} }
override fun onResume() { override fun onResume() {

View File

@@ -1,20 +1,20 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.activities.stylish package com.kunzisoft.keepass.activities.stylish
@@ -23,9 +23,9 @@ import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.annotation.StyleRes import androidx.annotation.StyleRes
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.support.v7.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -36,18 +36,16 @@ abstract class StylishFragment : Fragment() {
protected var themeId: Int = 0 protected var themeId: Int = 0
protected var contextThemed: Context? = null protected var contextThemed: Context? = null
override fun onAttach(context: Context?) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
if (context != null) { this.themeId = Stylish.getThemeId(context)
this.themeId = Stylish.getThemeId(context)
}
contextThemed = ContextThemeWrapper(context, themeId) contextThemed = ContextThemeWrapper(context, themeId)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// To fix status bar color // To fix status bar color
if (activity != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val window = activity!!.window val window = requireActivity().window
val attrColorPrimaryDark = intArrayOf(android.R.attr.colorPrimaryDark) val attrColorPrimaryDark = intArrayOf(android.R.attr.colorPrimaryDark)
val taColorPrimaryDark = contextThemed?.theme?.obtainStyledAttributes(attrColorPrimaryDark) val taColorPrimaryDark = contextThemed?.theme?.obtainStyledAttributes(attrColorPrimaryDark)

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2019 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.adapters
import android.content.Context
import android.text.format.Formatter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.model.AttachmentState
import com.kunzisoft.keepass.model.EntryAttachment
class EntryAttachmentsAdapter(val context: Context) : RecyclerView.Adapter<EntryAttachmentsAdapter.EntryBinariesViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
var entryAttachmentsList: MutableList<EntryAttachment> = ArrayList()
var onItemClickListener: ((item: EntryAttachment, position: Int)->Unit)? = null
private val mDatabase = Database.getInstance()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EntryBinariesViewHolder {
return EntryBinariesViewHolder(inflater.inflate(R.layout.item_attachment, parent, false))
}
override fun onBindViewHolder(holder: EntryBinariesViewHolder, position: Int) {
val entryAttachment = entryAttachmentsList[position]
holder.binaryFileTitle.text = entryAttachment.name
holder.binaryFileSize.text = Formatter.formatFileSize(context,
entryAttachment.binaryAttachment.length())
holder.binaryFileCompression.apply {
if (mDatabase.compressionAlgorithm == CompressionAlgorithm.GZip
|| entryAttachment.binaryAttachment.isCompressed == true) {
text = CompressionAlgorithm.GZip.getName(context.resources)
visibility = View.VISIBLE
} else {
text = ""
visibility = View.GONE
}
}
holder.binaryFileProgress.apply {
visibility = when (entryAttachment.downloadState) {
AttachmentState.NULL, AttachmentState.COMPLETE, AttachmentState.ERROR -> View.GONE
AttachmentState.START, AttachmentState.IN_PROGRESS -> View.VISIBLE
}
progress = entryAttachment.downloadProgression
}
holder.itemView.setOnClickListener {
onItemClickListener?.invoke(entryAttachment, position)
}
}
override fun getItemCount(): Int {
return entryAttachmentsList.size
}
fun updateProgress(entryAttachment: EntryAttachment) {
val indexEntryAttachment = entryAttachmentsList.indexOfLast { current -> current.name == entryAttachment.name }
if (indexEntryAttachment != -1) {
entryAttachmentsList[indexEntryAttachment] = entryAttachment
notifyItemChanged(indexEntryAttachment)
}
}
fun clear() {
entryAttachmentsList.clear()
}
inner class EntryBinariesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var binaryFileTitle: TextView = itemView.findViewById(R.id.item_attachment_title)
var binaryFileSize: TextView = itemView.findViewById(R.id.item_attachment_size)
var binaryFileCompression: TextView = itemView.findViewById(R.id.item_attachment_compression)
var binaryFileProgress: ProgressBar = itemView.findViewById(R.id.item_attachment_progress)
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2019 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.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.Entry
class EntryHistoryAdapter(val context: Context) : RecyclerView.Adapter<EntryHistoryAdapter.EntryHistoryViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
var entryHistoryList: MutableList<Entry> = ArrayList()
var onItemClickListener: ((item: Entry, position: Int)->Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EntryHistoryViewHolder {
return EntryHistoryViewHolder(inflater.inflate(R.layout.item_list_entry_history, parent, false))
}
override fun onBindViewHolder(holder: EntryHistoryViewHolder, position: Int) {
val entryHistory = entryHistoryList[position]
holder.lastModifiedView.text = entryHistory.lastModificationTime.getDateTimeString(context.resources)
holder.titleView.text = entryHistory.title
holder.usernameView.text = entryHistory.username
holder.urlView.text = entryHistory.url
holder.itemView.setOnClickListener {
onItemClickListener?.invoke(entryHistory, position)
}
}
override fun getItemCount(): Int {
return entryHistoryList.size
}
fun clear() {
entryHistoryList.clear()
}
inner class EntryHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var lastModifiedView: TextView = itemView.findViewById(R.id.entry_history_last_modified)
var titleView: TextView = itemView.findViewById(R.id.entry_history_title)
var usernameView: TextView = itemView.findViewById(R.id.entry_history_username)
var urlView: TextView = itemView.findViewById(R.id.entry_history_url)
}
}

View File

@@ -1,7 +1,26 @@
package com.kunzisoft.keepass.magikeyboard.adapter /*
* Copyright 2019 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.adapters
import android.content.Context import android.content.Context
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -15,10 +34,9 @@ import java.util.ArrayList
class FieldsAdapter(context: Context) : RecyclerView.Adapter<FieldsAdapter.FieldViewHolder>() { class FieldsAdapter(context: Context) : RecyclerView.Adapter<FieldsAdapter.FieldViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context) private val inflater: LayoutInflater = LayoutInflater.from(context)
var fields: MutableList<Field> = ArrayList() private var fields: MutableList<Field> = ArrayList()
var onItemClickListener: OnItemClickListener? = null var onItemClickListener: OnItemClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FieldViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FieldViewHolder {
val view = inflater.inflate(R.layout.keyboard_popup_fields_item, parent, false) val view = inflater.inflate(R.layout.keyboard_popup_fields_item, parent, false)
return FieldViewHolder(view) return FieldViewHolder(view)
@@ -34,6 +52,11 @@ class FieldsAdapter(context: Context) : RecyclerView.Adapter<FieldsAdapter.Field
return fields.size return fields.size
} }
fun setFields(fieldsToAdd: List<Field>) {
fields.clear()
fields.addAll(fieldsToAdd)
}
fun clear() { fun clear() {
fields.clear() fields.clear()
} }

View File

@@ -1,43 +1,52 @@
/* /*
* Copyright 2019 Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePassDX.
* *
* KeePass DX is free software: you can redistribute it and/or modify * KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* KeePass DX is distributed in the hope that it will be useful, * KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.adapters package com.kunzisoft.keepass.adapters
import android.content.Context import android.content.Context
import android.net.Uri import android.graphics.Color
import android.support.annotation.ColorInt import android.graphics.PorterDuff
import android.support.v7.widget.RecyclerView import androidx.annotation.ColorInt
import androidx.recyclerview.widget.RecyclerView
import android.util.TypedValue import android.util.TypedValue
import android.view.* import android.view.*
import android.widget.EditText
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import android.widget.ViewSwitcher
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.fileselect.FileDatabaseModel import com.kunzisoft.keepass.app.database.FileDatabaseHistoryEntity
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.utils.FileDatabaseInfo
import com.kunzisoft.keepass.utils.UriUtil
class FileDatabaseHistoryAdapter(private val context: Context, private val listFiles: List<String>) class FileDatabaseHistoryAdapter(private val context: Context)
: RecyclerView.Adapter<FileDatabaseHistoryAdapter.FileDatabaseHistoryViewHolder>() { : RecyclerView.Adapter<FileDatabaseHistoryAdapter.FileDatabaseHistoryViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context) private val inflater: LayoutInflater = LayoutInflater.from(context)
private var fileItemOpenListener: FileItemOpenListener? = null private var fileItemOpenListener: ((FileDatabaseHistoryEntity)->Unit)? = null
private var fileSelectClearListener: FileSelectClearListener? = null private var fileSelectClearListener: ((FileDatabaseHistoryEntity)->Boolean)? = null
private var fileInformationShowListener: FileInformationShowListener? = null private var saveAliasListener: ((FileDatabaseHistoryEntity)->Unit)? = null
private val listDatabaseFiles = ArrayList<FileDatabaseHistoryEntity>()
private var mExpandedPosition = -1
private var mPreviousExpandedPosition = -1
@ColorInt @ColorInt
private val defaultColor: Int private val defaultColor: Int
@@ -45,7 +54,6 @@ class FileDatabaseHistoryAdapter(private val context: Context, private val listF
private val warningColor: Int private val warningColor: Int
init { init {
val typedValue = TypedValue() val typedValue = TypedValue()
val theme = context.theme val theme = context.theme
theme.resolveAttribute(R.attr.colorAccent, typedValue, true) theme.resolveAttribute(R.attr.colorAccent, typedValue, true)
@@ -60,91 +68,135 @@ class FileDatabaseHistoryAdapter(private val context: Context, private val listF
} }
override fun onBindViewHolder(holder: FileDatabaseHistoryViewHolder, position: Int) { override fun onBindViewHolder(holder: FileDatabaseHistoryViewHolder, position: Int) {
val fileDatabaseModel = FileDatabaseModel(context, listFiles[position]) // Get info from position
// Context menu creation val fileHistoryEntity = listDatabaseFiles[position]
holder.fileContainer.setOnCreateContextMenuListener(ContextMenuBuilder(fileDatabaseModel)) val fileDatabaseInfo = FileDatabaseInfo(context, fileHistoryEntity.databaseUri)
// Click item to open file // Click item to open file
if (fileItemOpenListener != null) if (fileItemOpenListener != null)
holder.fileContainer.setOnClickListener(FileItemClickListener(position)) holder.fileContainer.setOnClickListener {
// Assign file name fileItemOpenListener?.invoke(fileHistoryEntity)
if (PreferencesUtil.isFullFilePathEnable(context)) }
holder.fileName.text = Uri.decode(fileDatabaseModel.fileUri.toString())
else // File alias
holder.fileName.text = fileDatabaseModel.fileName holder.fileAlias.text = fileDatabaseInfo.retrieveDatabaseAlias(fileHistoryEntity.databaseAlias)
holder.fileName.textSize = PreferencesUtil.getListTextSize(context)
// File path
holder.filePath.text = UriUtil.decode(fileDatabaseInfo.fileUri?.toString())
if (fileDatabaseInfo.exists) {
holder.fileInformation.clearColorFilter()
} else {
holder.fileInformation.setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY)
}
// Modification
fileDatabaseInfo.getModificationString()?.let {
holder.fileModification.text = it
holder.fileModification.visibility = View.VISIBLE
} ?: run {
holder.fileModification.visibility = View.GONE
}
// Size
fileDatabaseInfo.getSizeString()?.let {
holder.fileSize.text = it
holder.fileSize.visibility = View.VISIBLE
} ?: run {
holder.fileSize.visibility = View.GONE
}
// Click on information // Click on information
if (fileInformationShowListener != null) val isExpanded = position == mExpandedPosition
holder.fileInformation.setOnClickListener(FileInformationClickListener(fileDatabaseModel)) //This line hides or shows the layout in question
holder.fileExpandContainer.visibility = if (isExpanded) View.VISIBLE else View.GONE
// Save alias modification
holder.fileAliasCloseButton.setOnClickListener {
// Change the alias
fileHistoryEntity.databaseAlias = holder.fileAliasEdit.text.toString()
saveAliasListener?.invoke(fileHistoryEntity)
// Finish save mode
holder.fileMainSwitcher.showPrevious()
// Refresh current position to show alias
notifyItemChanged(position)
}
// Open alias modification
holder.fileModifyButton.setOnClickListener {
holder.fileAliasEdit.setText(holder.fileAlias.text)
holder.fileMainSwitcher.showNext()
}
holder.fileDeleteButton.setOnClickListener {
fileSelectClearListener?.invoke(fileHistoryEntity)
}
if (isExpanded) {
mPreviousExpandedPosition = position
}
holder.fileInformation.setOnClickListener {
mExpandedPosition = if (isExpanded) -1 else position
// Notify change
if (mPreviousExpandedPosition < itemCount)
notifyItemChanged(mPreviousExpandedPosition)
notifyItemChanged(position)
}
// Refresh View / Close alias modification if not contains fileAlias
if (holder.fileMainSwitcher.currentView.findViewById<View>(R.id.file_alias)
!= holder.fileAlias)
holder.fileMainSwitcher.showPrevious()
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return listFiles.size return listDatabaseFiles.size
} }
fun setOnItemClickListener(fileItemOpenListener: FileItemOpenListener) { fun clearDatabaseFileHistoryList() {
this.fileItemOpenListener = fileItemOpenListener listDatabaseFiles.clear()
} }
fun setFileSelectClearListener(fileSelectClearListener: FileSelectClearListener) { fun addDatabaseFileHistoryList(listFileDatabaseHistoryToAdd: List<FileDatabaseHistoryEntity>) {
this.fileSelectClearListener = fileSelectClearListener listDatabaseFiles.clear()
listDatabaseFiles.addAll(listFileDatabaseHistoryToAdd)
} }
fun setFileInformationShowListener(fileInformationShowListener: FileInformationShowListener) { fun deleteDatabaseFileHistory(fileDatabaseHistoryToDelete: FileDatabaseHistoryEntity) {
this.fileInformationShowListener = fileInformationShowListener listDatabaseFiles.remove(fileDatabaseHistoryToDelete)
} }
interface FileItemOpenListener { fun setOnFileDatabaseHistoryOpenListener(listener : ((FileDatabaseHistoryEntity)->Unit)?) {
fun onFileItemOpenListener(itemPosition: Int) this.fileItemOpenListener = listener
} }
interface FileSelectClearListener { fun setOnFileDatabaseHistoryDeleteListener(listener : ((FileDatabaseHistoryEntity)->Boolean)?) {
fun onFileSelectClearListener(fileDatabaseModel: FileDatabaseModel): Boolean this.fileSelectClearListener = listener
} }
interface FileInformationShowListener { fun setOnSaveAliasListener(listener : ((FileDatabaseHistoryEntity)->Unit)?) {
fun onClickFileInformation(fileDatabaseModel: FileDatabaseModel) this.saveAliasListener = listener
}
private inner class FileItemClickListener(private val position: Int) : View.OnClickListener {
override fun onClick(v: View) {
fileItemOpenListener?.onFileItemOpenListener(position)
}
}
private inner class FileInformationClickListener(private val fileDatabaseModel: FileDatabaseModel) : View.OnClickListener {
override fun onClick(view: View) {
fileInformationShowListener?.onClickFileInformation(fileDatabaseModel)
}
}
private inner class ContextMenuBuilder(private val fileDatabaseModel: FileDatabaseModel) : View.OnCreateContextMenuListener {
private val mOnMyActionClickListener = MenuItem.OnMenuItemClickListener { item ->
if (fileSelectClearListener == null)
return@OnMenuItemClickListener false
when (item.itemId) {
MENU_CLEAR -> fileSelectClearListener!!.onFileSelectClearListener(fileDatabaseModel)
else -> false
}
}
override fun onCreateContextMenu(contextMenu: ContextMenu?, view: View?, contextMenuInfo: ContextMenu.ContextMenuInfo?) {
contextMenu?.add(Menu.NONE, MENU_CLEAR, Menu.NONE, R.string.remove_from_filelist)
?.setOnMenuItemClickListener(mOnMyActionClickListener)
}
} }
inner class FileDatabaseHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class FileDatabaseHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var fileContainer: View = itemView.findViewById(R.id.file_container) var fileContainer: ViewGroup = itemView.findViewById(R.id.file_container_basic_info)
var fileName: TextView = itemView.findViewById(R.id.file_filename)
var fileAlias: TextView = itemView.findViewById(R.id.file_alias)
var fileInformation: ImageView = itemView.findViewById(R.id.file_information) var fileInformation: ImageView = itemView.findViewById(R.id.file_information)
}
companion object { var fileMainSwitcher: ViewSwitcher = itemView.findViewById(R.id.file_main_switcher)
var fileAliasEdit: EditText = itemView.findViewById(R.id.file_alias_edit)
var fileAliasCloseButton: ImageView = itemView.findViewById(R.id.file_alias_save)
private const val MENU_CLEAR = 1 var fileExpandContainer: ViewGroup = itemView.findViewById(R.id.file_expand_container)
var fileModifyButton: ImageView = itemView.findViewById(R.id.file_modify_button)
var fileDeleteButton: ImageView = itemView.findViewById(R.id.file_delete_button)
var filePath: TextView = itemView.findViewById(R.id.file_path)
var fileModification: TextView = itemView.findViewById(R.id.file_modification)
var fileSize: TextView = itemView.findViewById(R.id.file_size)
} }
} }

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