Compare commits
791 Commits
1.07
...
Branch_b0f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ce77a2729 | ||
|
|
b0f61038e3 | ||
|
|
d0749324c9 | ||
|
|
a43f72d18a | ||
|
|
3d691088be | ||
|
|
bca3becadb | ||
|
|
d937fbe7ad | ||
|
|
c13e00871d | ||
|
|
b5b716da4c | ||
|
|
45d2a8e0b2 | ||
|
|
d47c948699 | ||
|
|
fdf36babba | ||
|
|
d47411264e | ||
|
|
b9ebad7942 | ||
|
|
2878cc1cb5 | ||
|
|
9277d76f37 | ||
|
|
b7b1301334 | ||
|
|
954dd73ffa | ||
|
|
7aeab98c5b | ||
|
|
51123d60d3 | ||
|
|
395d4b5a97 | ||
|
|
654f025f64 | ||
|
|
8a9ccea5d3 | ||
|
|
92e00075c6 | ||
|
|
624e78e734 | ||
|
|
76bb8822fa | ||
|
|
e3cba097fa | ||
|
|
c812fca8c1 | ||
|
|
f86f4ee18c | ||
|
|
dafed9786e | ||
|
|
cd3a119053 | ||
|
|
f143a171aa | ||
|
|
ee93d7df82 | ||
|
|
61a3bcc53d | ||
|
|
17a46eb92c | ||
|
|
14b121a8ba | ||
|
|
41b57417e5 | ||
|
|
bb492c3043 | ||
|
|
b683dca67c | ||
|
|
b6e6024bda | ||
|
|
2b3a95e21d | ||
|
|
de9c27f1f5 | ||
|
|
85c9cb26b1 | ||
|
|
8ffa27e93d | ||
|
|
ea743fc2c5 | ||
|
|
7fab9aab00 | ||
|
|
444e224d41 | ||
|
|
9fde19f1ce | ||
|
|
4e085b5dca | ||
|
|
06ab58cc07 | ||
|
|
9dae1746ec | ||
|
|
db87191f1f | ||
|
|
76abe79b6d | ||
|
|
970bc29e01 | ||
|
|
79a3262659 | ||
|
|
3add6d64ef | ||
|
|
43168b18d1 | ||
|
|
6970b42456 | ||
|
|
a545b96601 | ||
|
|
59b66409e8 | ||
|
|
25c50bbcaf | ||
|
|
a9a5df00d2 | ||
|
|
5f4d9c29dc | ||
|
|
31e158e39b | ||
|
|
25af5844be | ||
|
|
f79bb49af1 | ||
|
|
2756bae656 | ||
|
|
b574b55f84 | ||
|
|
76ef1c4033 | ||
|
|
409deaee9d | ||
|
|
8396e18283 | ||
|
|
5e952e06ff | ||
|
|
7b6dc3d773 | ||
|
|
1759b67921 | ||
|
|
1307b0e5d1 | ||
|
|
c1af6994bf | ||
|
|
d09c15e5b8 | ||
|
|
ea98b170d9 | ||
|
|
c67f4e45c7 | ||
|
|
c21a5afe94 | ||
|
|
49de16f0f3 | ||
|
|
6d60afadf4 | ||
|
|
c6751af075 | ||
|
|
d41aa389a7 | ||
|
|
f95986fd7a | ||
|
|
ac522bce43 | ||
|
|
6e0729c3b3 | ||
|
|
9ce674da2b | ||
|
|
da8a8e7e77 | ||
|
|
566a1088bd | ||
|
|
11f1f07653 | ||
|
|
45194bb26f | ||
|
|
1f6440189f | ||
|
|
60e98d87d8 | ||
|
|
f560124088 | ||
|
|
7521d0b787 | ||
|
|
c45e52f571 | ||
|
|
275920e5be | ||
|
|
7045c6ee8d | ||
|
|
7a7b65e8c6 | ||
|
|
261526b4d1 | ||
|
|
bc74c91a2c | ||
|
|
ab47543df0 | ||
|
|
3a601468a9 | ||
|
|
f2002e2b90 | ||
|
|
29031167d6 | ||
|
|
7305ac47c3 | ||
|
|
2aec6c9e4b | ||
|
|
5a2cdf3cda | ||
|
|
a943df63fc | ||
|
|
ca771abebe | ||
|
|
010db6148d | ||
|
|
8f065f480c | ||
|
|
f163f9382f | ||
|
|
e752244b5f | ||
|
|
4bbb958a05 | ||
|
|
d1ddfed8dd | ||
|
|
bdb39f203a | ||
|
|
1bcb6aabe7 | ||
|
|
90b09b0f7e | ||
|
|
4e6e0a0e73 | ||
|
|
ad4c764adb | ||
|
|
2265432ec4 | ||
|
|
bb45e60a3f | ||
|
|
b1ff92d63e | ||
|
|
016864edfc | ||
|
|
dc5133e676 | ||
|
|
1fbd10c918 | ||
|
|
4cd219ab32 | ||
|
|
871e84fec9 | ||
|
|
08ce132587 | ||
|
|
4c58d8df72 | ||
|
|
bf554d2417 | ||
|
|
631cd8e35f | ||
|
|
f46c4d8054 | ||
|
|
0010713b78 | ||
|
|
f5bfd4f8f2 | ||
|
|
6d04d2f579 | ||
|
|
0d3cef1626 | ||
|
|
4d251becb0 | ||
|
|
372d4c4bf3 | ||
|
|
2dbda89973 | ||
|
|
2f55bbddd8 | ||
|
|
928773b183 | ||
|
|
bc9803cbc4 | ||
|
|
fca8f5efbc | ||
|
|
f09fe4f7e2 | ||
|
|
de3bc0ff6c | ||
|
|
c4a1b6a82e | ||
|
|
bd08dd54e2 | ||
|
|
b1f93fdb75 | ||
|
|
51599b3f1a | ||
|
|
1b753877a9 | ||
|
|
d33ffa9d22 | ||
|
|
df710e523a | ||
|
|
7d218e0706 | ||
|
|
88127a4858 | ||
|
|
63f2a6f902 | ||
|
|
d8286eb639 | ||
|
|
668fa46a98 | ||
|
|
e16a671a26 | ||
|
|
7f89ef358c | ||
|
|
e0df319279 | ||
|
|
61c7a260b4 | ||
|
|
f26fce63ad | ||
|
|
74c3c96b43 | ||
|
|
765b020152 | ||
|
|
59c05235f6 | ||
|
|
92a4131c88 | ||
|
|
540e14bbe7 | ||
|
|
0ebd355edd | ||
|
|
68d4dfed04 | ||
|
|
37a482d2e4 | ||
|
|
b79cc65728 | ||
|
|
78977c7e31 | ||
|
|
1957fc5345 | ||
|
|
49cefff75d | ||
|
|
2ebb145b7b | ||
|
|
0a6f1394e4 | ||
|
|
1310913281 | ||
|
|
c245c5ade3 | ||
|
|
9e6f45a59b | ||
|
|
f6a78c8890 | ||
|
|
76717fa3cb | ||
|
|
a19230393d | ||
|
|
0b89969a33 | ||
|
|
bbe97230d2 | ||
|
|
ee0161737c | ||
|
|
e7e0b91703 | ||
|
|
dbcdf71bbe | ||
|
|
779615c09a | ||
|
|
1c3a1ce53c | ||
|
|
981c524e3f | ||
|
|
44a7d68f31 | ||
|
|
ae858e3dc4 | ||
|
|
22fd23a31d | ||
|
|
2babe87d6d | ||
|
|
95a8b3fe34 | ||
|
|
32c526a26a | ||
|
|
b2adfad2ee | ||
|
|
ee5a8534a7 | ||
|
|
80ba3b969d | ||
|
|
83bd6911c8 | ||
|
|
6c66f6199c | ||
|
|
c11731541c | ||
|
|
609593ebd0 | ||
|
|
85b867a5ca | ||
|
|
a3690051eb | ||
|
|
ecb44a2611 | ||
|
|
7898ea9ba7 | ||
|
|
90a2029f3d | ||
|
|
1d9a9ea658 | ||
|
|
e88f80eda3 | ||
|
|
025e2f415d | ||
|
|
e113ca79de | ||
|
|
f988c44440 | ||
|
|
37cda26e27 | ||
|
|
65c2da9afa | ||
|
|
434b7b756b | ||
|
|
9dfcab6c1c | ||
|
|
9f39f4e377 | ||
|
|
58da4284ac | ||
|
|
55787ff6cf | ||
|
|
3b93610e43 | ||
|
|
40f3066ee0 | ||
|
|
2108aac41c | ||
|
|
68f8300395 | ||
|
|
325a61912e | ||
|
|
7363c88fce | ||
|
|
0b0f95c65c | ||
|
|
354675f1e5 | ||
|
|
7a769f283b | ||
|
|
3dc67d5978 | ||
|
|
d3a0d71857 | ||
|
|
72f7534a5f | ||
|
|
27a733cbed | ||
|
|
92a0ce98bb | ||
|
|
84c9632508 | ||
|
|
22c2b406db | ||
|
|
bc374a1317 | ||
|
|
0bf82a999a | ||
|
|
453a8459ac | ||
|
|
53f6ddad22 | ||
|
|
14bd128931 | ||
|
|
b0b8daa03d | ||
|
|
940cd70f64 | ||
|
|
d8cdef0622 | ||
|
|
44a7cfe32e | ||
|
|
8584352e21 | ||
|
|
1c39d027e0 | ||
|
|
ce82e2cdcf | ||
|
|
a8492f75cd | ||
|
|
03afb38aa3 | ||
|
|
acc2acce2c | ||
|
|
3a612864f8 | ||
|
|
7e1789a3f4 | ||
|
|
69dea794e1 | ||
|
|
4e024bd8b1 | ||
|
|
6078e511aa | ||
|
|
58574cd824 | ||
|
|
635fa99cf2 | ||
|
|
074d71cef7 | ||
|
|
027075b1b2 | ||
|
|
898ad42f96 | ||
|
|
c87ffeff71 | ||
|
|
40f0744adc | ||
|
|
eba555f260 | ||
|
|
e7db208a40 | ||
|
|
69399bb328 | ||
|
|
749e928d66 | ||
|
|
a3b8690609 | ||
|
|
340e6794ca | ||
|
|
7a23152cee | ||
|
|
d00f71884a | ||
|
|
587014159d | ||
|
|
e2cb3691e4 | ||
|
|
a40be3b658 | ||
|
|
598d7c699a | ||
|
|
23c9b9f2d9 | ||
|
|
22aeff708a | ||
|
|
e9b7b935ee | ||
|
|
eb0d9ec482 | ||
|
|
cfd0d72100 | ||
|
|
f615dc265a | ||
|
|
53c4971c4e | ||
|
|
3a98fa9f53 | ||
|
|
6ed5386c12 | ||
|
|
a998a7f92b | ||
|
|
3218989b13 | ||
|
|
3aff954063 | ||
|
|
11e824c133 | ||
|
|
a26662a813 | ||
|
|
007838197b | ||
|
|
0ad82d5b91 | ||
|
|
52e47a62d2 | ||
|
|
cbaaf3ee54 | ||
|
|
26f26ee35d | ||
|
|
0102ac77e3 | ||
|
|
d24101bee2 | ||
|
|
36cced5ce7 | ||
|
|
87eb537ebe | ||
|
|
86fdc17d2a | ||
|
|
bd8763c62f | ||
|
|
bc917d41ad | ||
|
|
68b60b427a | ||
|
|
c9e64c53ec | ||
|
|
6ba6c6bac7 | ||
|
|
f2967e1d8f | ||
|
|
60783ca88e | ||
|
|
8d7c1371df | ||
|
|
090bfe9091 | ||
|
|
3ad2ba9f33 | ||
|
|
1036def599 | ||
|
|
2acad69940 | ||
|
|
1f18011a5b | ||
|
|
3da63b378c | ||
|
|
0852aa7429 | ||
|
|
8a863ca636 | ||
|
|
83db5c205d | ||
|
|
760e73959c | ||
|
|
e516e06a53 | ||
|
|
a954461be3 | ||
|
|
5998369af6 | ||
|
|
f41a2604c4 | ||
|
|
6fc578d286 | ||
|
|
f2604a5fec | ||
|
|
642026156a | ||
|
|
708c511a98 | ||
|
|
2c75b5886f | ||
|
|
0ef7f5a955 | ||
|
|
28856c01cd | ||
|
|
a47bd03e94 | ||
|
|
c5551ddeb5 | ||
|
|
020f274ef3 | ||
|
|
e2f8f04ace | ||
|
|
6d61d885c1 | ||
|
|
e98cd16032 | ||
|
|
4ec8bdda2f | ||
|
|
16df1624f8 | ||
|
|
cb978e012b | ||
|
|
b7ea02696b | ||
|
|
49cb8874ca | ||
|
|
1384256d87 | ||
|
|
705b2fcd9a | ||
|
|
906ca77973 | ||
|
|
7dc5c734d0 | ||
|
|
a3c1e311f7 | ||
|
|
4d246a250d | ||
|
|
8d2c41f558 | ||
|
|
3b06c4c14a | ||
|
|
f98dcb813d | ||
|
|
17cf31e8cf | ||
|
|
6771c378bf | ||
|
|
013aa4d071 | ||
|
|
b887e16628 | ||
|
|
7774ed65bd | ||
|
|
c85f996aa3 | ||
|
|
64d9cc00d1 | ||
|
|
a0efbecee4 | ||
|
|
cceb4c6677 | ||
|
|
66e448a648 | ||
|
|
eb134fd41a | ||
|
|
857d6f9008 | ||
|
|
32396c4534 | ||
|
|
2df46b6647 | ||
|
|
9f6ad37547 | ||
|
|
65955843e5 | ||
|
|
9e9b5fb9dd | ||
|
|
f96fc67bc2 | ||
|
|
07ebd0b4ca | ||
|
|
64e955c512 | ||
|
|
161753b195 | ||
|
|
f92adb847a | ||
|
|
98f780c277 | ||
|
|
3c512e74db | ||
|
|
9b752dd28f | ||
|
|
3cf2719c29 | ||
|
|
be420c0394 | ||
|
|
1b1d995b9d | ||
|
|
6410f41fa2 | ||
|
|
f90a45b1e6 | ||
|
|
9ca5569122 | ||
|
|
a6b4a35973 | ||
|
|
06cb6bac3e | ||
|
|
5241aeea9f | ||
|
|
6b69ad20ad | ||
|
|
a79d2ec323 | ||
|
|
209a0fe2da | ||
|
|
277f439f70 | ||
|
|
d9f29fdcaa | ||
|
|
a9141d3fe2 | ||
|
|
4ce55e88fa | ||
|
|
c930d388dc | ||
|
|
12c7911cf4 | ||
|
|
06377ff497 | ||
|
|
7a1938e515 | ||
|
|
445166d220 | ||
|
|
3963501d0c | ||
|
|
acb51d7859 | ||
|
|
4dbfb76ced | ||
|
|
4188464652 | ||
|
|
fb552a8d62 | ||
|
|
c998e4e34f | ||
|
|
ab46015a5a | ||
|
|
dd10740bf2 | ||
|
|
44f26685e4 | ||
|
|
3caa56ae8c | ||
|
|
1ca4fbb078 | ||
|
|
bdf5270fa0 | ||
|
|
e3e865b108 | ||
|
|
33f3736798 | ||
|
|
a86fbcee44 | ||
|
|
cdbf8a2811 | ||
|
|
da4fae4e04 | ||
|
|
ddeefc4ab6 | ||
|
|
33702064a5 | ||
|
|
b72995c8bf | ||
|
|
868eb1c4fb | ||
|
|
d93140efc6 | ||
|
|
0f59d46ede | ||
|
|
812b5f22f9 | ||
|
|
d1b5feae0d | ||
|
|
0861fa9e03 | ||
|
|
c3fe9f513d | ||
|
|
b513669f4d | ||
|
|
1ad74fdacc | ||
|
|
9a24ede989 | ||
|
|
7ea8dfa8ce | ||
|
|
670068eeba | ||
|
|
70310f1970 | ||
|
|
4463c148a5 | ||
|
|
c982b45134 | ||
|
|
d209a2c7f8 | ||
|
|
75c73193a2 | ||
|
|
fc99f6774d | ||
|
|
d40fab86c9 | ||
|
|
5c0520e093 | ||
|
|
1bb9ead202 | ||
|
|
3afd8915f0 | ||
|
|
ba3f2d2823 | ||
|
|
24989d80ac | ||
|
|
d2c16158cc | ||
|
|
8514fd6266 | ||
|
|
30d1534c01 | ||
|
|
4f0800d4fc | ||
|
|
41fa396318 | ||
|
|
d94af84002 | ||
|
|
63b92cc35a | ||
|
|
3238d9fbc5 | ||
|
|
780d4c95fe | ||
|
|
c3b27c75bf | ||
|
|
23c7031672 | ||
|
|
9e98d09a93 | ||
|
|
06e64f7347 | ||
|
|
92594db78d | ||
|
|
c574f2d6bd | ||
|
|
ee156cbfd6 | ||
|
|
73d12daf04 | ||
|
|
e4179a934b | ||
|
|
afd309d9a8 | ||
|
|
1a24b00c5e | ||
|
|
445e4d8b31 | ||
|
|
224ca98d0e | ||
|
|
13419c9fb7 | ||
|
|
00646bc84b | ||
|
|
cb4b686335 | ||
|
|
5e900b62c8 | ||
|
|
74c94d37e5 | ||
|
|
588814d1a6 | ||
|
|
572b858f81 | ||
|
|
1a9fc48490 | ||
|
|
2dd131e6c3 | ||
|
|
d1ad2a681f | ||
|
|
e2e7666c4f | ||
|
|
665639fdca | ||
|
|
8bb930c1fc | ||
|
|
b3b9d8d105 | ||
|
|
82c409252b | ||
|
|
14c63a03c4 | ||
|
|
0c7ad8b16c | ||
|
|
1bf2d8219a | ||
|
|
a72264ef36 | ||
|
|
1acc399568 | ||
|
|
160960437c | ||
|
|
61992adcca | ||
|
|
10e0ae8200 | ||
|
|
eb58b8576e | ||
|
|
9b4d2d6ed7 | ||
|
|
6417e9dec5 | ||
|
|
f5ac217f13 | ||
|
|
a3d3b14dd1 | ||
|
|
8fccce8684 | ||
|
|
4a847cd74a | ||
|
|
f4c1db39ad | ||
|
|
d9ff177427 | ||
|
|
95e3566c5f | ||
|
|
76e02ff514 | ||
|
|
8aca9a2d82 | ||
|
|
3abab2072a | ||
|
|
ca9e479dc5 | ||
|
|
4d230a7b3e | ||
|
|
974cc59e6c | ||
|
|
9f09b144f0 | ||
|
|
9d4dad5fee | ||
|
|
46ffa93d93 | ||
|
|
8b709bc070 | ||
|
|
c1284c760a | ||
|
|
6529a94d3a | ||
|
|
f5e91a0524 | ||
|
|
f63bd3016b | ||
|
|
09cd590796 | ||
|
|
da728c4039 | ||
|
|
14b7b8cec1 | ||
|
|
3695c04933 | ||
|
|
f0f3bb6ede | ||
|
|
7824326c5b | ||
|
|
a65d263ae2 | ||
|
|
f3e628be6b | ||
|
|
62e8dc7767 | ||
|
|
8e8cfa704a | ||
|
|
8b2eff0194 | ||
|
|
d4d063b97a | ||
|
|
d5ec33e1bd | ||
|
|
0726f22ec7 | ||
|
|
531ab93388 | ||
|
|
a7ad00d402 | ||
|
|
1f18848b4e | ||
|
|
38179d9ce3 | ||
|
|
b86c493cf8 | ||
|
|
0cc2df85da | ||
|
|
3157bac5f6 | ||
|
|
5cb1709f5d | ||
|
|
e6834b13a5 | ||
|
|
8a17daa002 | ||
|
|
1b09189195 | ||
|
|
7f21fa2c40 | ||
|
|
e0e4fc85f7 | ||
|
|
b3e57db9f8 | ||
|
|
f8d93a5de7 | ||
|
|
c5423ac650 | ||
|
|
6e93d17d14 | ||
|
|
127bc1be5c | ||
|
|
8136b09992 | ||
|
|
2ffac3268e | ||
|
|
c7056e9d40 | ||
|
|
3fb8b4e7ff | ||
|
|
c728a7802d | ||
|
|
e7d3049456 | ||
|
|
316675c77e | ||
|
|
c836d3de71 | ||
|
|
230429135a | ||
|
|
0be8092382 | ||
|
|
eb2019e568 | ||
|
|
bffbb17271 | ||
|
|
16b997096d | ||
|
|
3b23f2a4ed | ||
|
|
87f86fd2e8 | ||
|
|
396509579a | ||
|
|
91175f19c4 | ||
|
|
7c22ca0f91 | ||
|
|
5eb416d8d9 | ||
|
|
12e374bd2f | ||
|
|
9b50f0e77c | ||
|
|
6aa89287b1 | ||
|
|
4b9ebdc87c | ||
|
|
336b45667e | ||
|
|
3c33a1126b | ||
|
|
836cdfb16c | ||
|
|
8512bae997 | ||
|
|
6fbe8c5dee | ||
|
|
2132ea416b | ||
|
|
24081b9223 | ||
|
|
220cc98559 | ||
|
|
ef8b9b0685 | ||
|
|
e8dd47369c | ||
|
|
537eaddecd | ||
|
|
183c171da6 | ||
|
|
58c6114840 | ||
|
|
df5162ce56 | ||
|
|
ae61fe892d | ||
|
|
a78de3b4b9 | ||
|
|
f26a864e83 | ||
|
|
0e0b03e6f2 | ||
|
|
e5595f13eb | ||
|
|
1b12c16685 | ||
|
|
048055daac | ||
|
|
f2590f1fed | ||
|
|
2eb7b22100 | ||
|
|
f169750d1a | ||
|
|
fbb361fa7a | ||
|
|
902faca13c | ||
|
|
7e476a5785 | ||
|
|
c78c5aad02 | ||
|
|
42c49645da | ||
|
|
9427fb9ecc | ||
|
|
61878f293f | ||
|
|
2bd1727076 | ||
|
|
f0fbf7bf8b | ||
|
|
4a1fccc395 | ||
|
|
54c0497933 | ||
|
|
49a99e9f15 | ||
|
|
134fd5b2c2 | ||
|
|
696f9b3550 | ||
|
|
aeda42cb71 | ||
|
|
7ba8400c38 | ||
|
|
02f10f07e8 | ||
|
|
83cc32afaa | ||
|
|
5b8382643a | ||
|
|
003cb8719a | ||
|
|
c625ebc128 | ||
|
|
89c0b02327 | ||
|
|
94bb17a301 | ||
|
|
4f29b921e5 | ||
|
|
8d5dbb0f3d | ||
|
|
db321e45da | ||
|
|
8e254a341c | ||
|
|
847a52ab08 | ||
|
|
94e69e4e84 | ||
|
|
8834526cac | ||
|
|
788365687e | ||
|
|
4c3d328140 | ||
|
|
2588f74f7c | ||
|
|
b70a4ef428 | ||
|
|
026a903251 | ||
|
|
3557113ed8 | ||
|
|
acd8c27a13 | ||
|
|
3c186eb113 | ||
|
|
841eb34224 | ||
|
|
ef583ba7ec | ||
|
|
fa0e06df75 | ||
|
|
c26ee7271b | ||
|
|
f37c0a9124 | ||
|
|
80fbd656a7 | ||
|
|
3fb5749c86 | ||
|
|
1692130559 | ||
|
|
cf77a9eae2 | ||
|
|
26151af48a | ||
|
|
c3b858f0fd | ||
|
|
914e788ad8 | ||
|
|
074178621c | ||
|
|
25d1b6b695 | ||
|
|
f121c73c4b | ||
|
|
3f79e7677c | ||
|
|
ca573f27be | ||
|
|
056a3342bf | ||
|
|
eaa661cf09 | ||
|
|
b2d791d6ea | ||
|
|
f1ea1935c5 | ||
|
|
d9c101debe | ||
|
|
e3a720a69d | ||
|
|
4f3f18a0ad | ||
|
|
10a3a8324b | ||
|
|
a2dab72b25 | ||
|
|
ca0a381d8c | ||
|
|
a503e85a57 | ||
|
|
0d668561b4 | ||
|
|
c8d39a2c15 | ||
|
|
6146ac90c1 | ||
|
|
2bb00c948d | ||
|
|
01eadd986c | ||
|
|
b482ea4ecc | ||
|
|
19bb98c857 | ||
|
|
225afb85e4 | ||
|
|
2873ffdff7 | ||
|
|
52ba506138 | ||
|
|
798f70a706 | ||
|
|
d731d55a7a | ||
|
|
1bfcea0227 | ||
|
|
71a307bfef | ||
|
|
7a16a8eaff | ||
|
|
c1c2ccd940 | ||
|
|
8a993b7dcb | ||
|
|
977393a9aa | ||
|
|
4f36de9900 | ||
|
|
ef658eb4cd | ||
|
|
6f77577482 | ||
|
|
bfdb1a8f62 | ||
|
|
0935d70ae4 | ||
|
|
24e4e2b960 | ||
|
|
e608d7463e | ||
|
|
47453f1471 | ||
|
|
362780f59a | ||
|
|
a2a511b3c0 | ||
|
|
78cde8ab62 | ||
|
|
46b779c9d5 | ||
|
|
23129bf95a | ||
|
|
fba137bb3f | ||
|
|
e50a73b4bf | ||
|
|
31afe11d0e | ||
|
|
5dc1d047ac | ||
|
|
19e0cf406c | ||
|
|
0bc1eece0a | ||
|
|
f2e93e915b | ||
|
|
8db6a99194 | ||
|
|
1490ebce03 | ||
|
|
cd189e01dc | ||
|
|
15467248ee | ||
|
|
23008ab5b8 | ||
|
|
041fbb10a1 | ||
|
|
9ba0e59df5 | ||
|
|
0bb9f57919 | ||
|
|
106da869c1 | ||
|
|
d34a3352f2 | ||
|
|
4b5c922f11 | ||
|
|
aacb415364 | ||
|
|
bdc8bcfe5c | ||
|
|
154238edf0 | ||
|
|
eba789c200 | ||
|
|
63cb67a03c | ||
|
|
1c299c6d5d | ||
|
|
486c5310d7 | ||
|
|
5ef4a97d5f | ||
|
|
03adb8f730 | ||
|
|
abdcd0b3f0 | ||
|
|
6e225808a9 | ||
|
|
382c96f587 | ||
|
|
80cb0119c7 | ||
|
|
e7bc5072c0 | ||
|
|
a65057679c | ||
|
|
025186d22f | ||
|
|
e17217206f | ||
|
|
c64ca44457 | ||
|
|
c6e9a16274 | ||
|
|
f945380a87 | ||
|
|
bb9664eafc | ||
|
|
eaca802ecc | ||
|
|
2e6ca574b6 | ||
|
|
d4c61ce932 | ||
|
|
7387ebe182 | ||
|
|
d6f305cfa6 | ||
|
|
73b9fbb435 | ||
|
|
51c2f313d4 | ||
|
|
0a390107a3 | ||
|
|
077bf997c6 | ||
|
|
48cb473ecb | ||
|
|
40dc542302 | ||
|
|
28bb4894b2 | ||
|
|
774d17dd25 | ||
|
|
790637f1bd | ||
|
|
99fb5d3b8e | ||
|
|
6676e62579 | ||
|
|
d735992f5c | ||
|
|
8a32cfc185 | ||
|
|
285fafc300 | ||
|
|
0c90433a34 | ||
|
|
d6070368b7 | ||
|
|
b42389500f | ||
|
|
daca972fc2 | ||
|
|
8447e59d1f | ||
|
|
07ff8cd662 | ||
|
|
dbc734eee5 | ||
|
|
eaa95fdba4 | ||
|
|
3ba1169e9c | ||
|
|
0117b53ea2 | ||
|
|
a2ec04b641 | ||
|
|
8fdcef9c42 | ||
|
|
d311de764d | ||
|
|
e9b16b2f70 | ||
|
|
21a81b977b | ||
|
|
9ce1c2b075 | ||
|
|
b974cbd14c | ||
|
|
1d96282fd9 | ||
|
|
48efda6bd9 | ||
|
|
aba3e0033f | ||
|
|
5bf61a63cc | ||
|
|
48095c0433 | ||
|
|
3e1599119a | ||
|
|
f488e8a7ab | ||
|
|
b5c17bf7bc | ||
|
|
77e994e9fd | ||
|
|
bbd3fc4d1d | ||
|
|
2a57b94e08 | ||
|
|
366a37b765 | ||
|
|
a3806f7a81 | ||
|
|
cf28e373f7 | ||
|
|
8d80295e07 | ||
|
|
1f7ffe6ea0 | ||
|
|
a740753175 | ||
|
|
c70f996915 | ||
|
|
707dfc2a0b | ||
|
|
77db3362fc | ||
|
|
c8e0687288 | ||
|
|
f61a6191ec | ||
|
|
2648236bb4 | ||
|
|
382e23f545 | ||
|
|
2eddbe3669 | ||
|
|
5e41517178 | ||
|
|
177c709e0a | ||
|
|
699eb824a1 | ||
|
|
6b19e305f3 | ||
|
|
8b1b4044b3 |
@@ -4,3 +4,7 @@ files:
|
|||||||
/src/keepass2android/Resources/values-%two_letters_code%/%original_file_name%
|
/src/keepass2android/Resources/values-%two_letters_code%/%original_file_name%
|
||||||
translate_attributes: '0'
|
translate_attributes: '0'
|
||||||
content_segmentation: '0'
|
content_segmentation: '0'
|
||||||
|
languages_mapping:
|
||||||
|
two_letters_code:
|
||||||
|
zh-CN: zh
|
||||||
|
zh-TW: zh-rTW
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Keepass2Android's apk is pretty big, e.g. when comparing to Keepassdroid. The ma
|
|||||||
|
|
||||||
Here's a list of what is contained in the Keepass2Android 0.9.1 application package:
|
Here's a list of what is contained in the Keepass2Android 0.9.1 application package:
|
||||||
|
|
||||||
{{
|
```
|
||||||
Mono for Android
|
Mono for Android
|
||||||
.net dlls 5.0 MB
|
.net dlls 5.0 MB
|
||||||
Runtime 2.5 MB
|
Runtime 2.5 MB
|
||||||
@@ -22,4 +22,4 @@ Java/Mono bindings 0.5 MB
|
|||||||
rest 0.3 MB
|
rest 0.3 MB
|
||||||
|
|
||||||
TOTAL 13 MB
|
TOTAL 13 MB
|
||||||
}}
|
```
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
<h1 align="center"><img src="/src/keepass2android/Resources/mipmap-xxxhdpi/ic_launcher_online.png" align="center" width="100" alt="Keepass2Android Logo">Keepass2Android</h1>
|
||||||
|
|
||||||
|
|
||||||
# What is Keepass2Android?
|
# What is Keepass2Android?
|
||||||
Keepass2Android is a password manager app. It allows to store and retrieve passwords and other sensitive information in a file called "database". This database is secured with a so-called master password. The master password typically is a strong password and can be complemented with a second factor for additional security.
|
Keepass2Android is a password manager app. It allows to store and retrieve passwords and other sensitive information in a file called "database". This database is secured with a so-called master password. The master password typically is a strong password and can be complemented with a second factor for additional security.
|
||||||
The password database file can be synchronized across different devices. This works best using one of the built-in cloud storage options, but can also be performed with third-party apps. Keepass2Android is compatible with Keepass 1 and Keepass 2 on Windows and KeepassX on Linux.
|
The password database file can be synchronized across different devices. This works best using one of the built-in cloud storage options, but can also be performed with third-party apps. Keepass2Android is compatible with Keepass 1 and Keepass 2 on Windows and KeepassX on Linux.
|
||||||
|
|||||||
@@ -81,10 +81,6 @@
|
|||||||
</XamarinComponentReference>
|
</XamarinComponentReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AdalBindings\AdalBindings.csproj">
|
|
||||||
<Project>{0b109c0e-0514-4340-8779-5bd6a0dde84e}</Project>
|
|
||||||
<Name>AdalBindings</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" />
|
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCloudBindings", "PCloudBindings\PCloudBindings.csproj", "{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCloudBindings", "PCloudBindings\PCloudBindings.csproj", "{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdalBindings", "AdalBindings\AdalBindings.csproj", "{0B109C0E-0514-4340-8779-5BD6A0DDE84E}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -317,30 +315,6 @@ Global
|
|||||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Win32.Build.0 = ReleaseNoNet|Any CPU
|
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Win32.Build.0 = ReleaseNoNet|Any CPU
|
||||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
|
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
|
||||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.Build.0 = ReleaseNoNet|Any CPU
|
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.Build.0 = ReleaseNoNet|Any CPU
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Debug|Win32.Build.0 = Debug|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Release|Win32.ActiveCfg = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Release|Win32.Build.0 = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{0B109C0E-0514-4340-8779-5BD6A0DDE84E}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using keepass2android;
|
||||||
#if KeePassUAP
|
#if KeePassUAP
|
||||||
using Org.BouncyCastle.Crypto;
|
using Org.BouncyCastle.Crypto;
|
||||||
using Org.BouncyCastle.Crypto.Engines;
|
using Org.BouncyCastle.Crypto.Engines;
|
||||||
@@ -144,6 +144,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
|
|||||||
public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
|
public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
|
||||||
ulong uNumRounds)
|
ulong uNumRounds)
|
||||||
{
|
{
|
||||||
|
Kp2aLog.Log("Warning: transforming key managed. Expect this to be slow!");
|
||||||
#if KeePassUAP
|
#if KeePassUAP
|
||||||
KeyParameter kp = new KeyParameter(pbKeySeed32);
|
KeyParameter kp = new KeyParameter(pbKeySeed32);
|
||||||
AesEngine aes = new AesEngine();
|
AesEngine aes = new AesEngine();
|
||||||
|
|||||||
@@ -38,9 +38,11 @@ namespace KeePassLib.Keys
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// <summary>
|
// /// <summary>
|
||||||
// /// Clear the key and securely erase all security-critical information.
|
// /// Clear the key and securely erase all security-critical information.
|
||||||
// /// </summary>
|
// /// </summary>
|
||||||
// void Clear();
|
// void Clear();
|
||||||
|
|
||||||
|
uint GetMinKdbxVersion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,12 @@ namespace KeePassLib.Keys
|
|||||||
get { return m_pbKey; }
|
get { return m_pbKey; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public KcpCustomKey(string strName, byte[] pbKeyData, bool bPerformHash)
|
public uint GetMinKdbxVersion()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KcpCustomKey(string strName, byte[] pbKeyData, bool bPerformHash)
|
||||||
{
|
{
|
||||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||||
Debug.Assert(pbKeyData != null); if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
|
Debug.Assert(pbKeyData != null); if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
|
||||||
|
|||||||
@@ -64,7 +64,12 @@ namespace KeePassLib.Keys
|
|||||||
get { return m_pbKeyData; }
|
get { return m_pbKeyData; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOConnectionInfo Ioc
|
public uint GetMinKdbxVersion()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo Ioc
|
||||||
{
|
{
|
||||||
get { return m_ioc; }
|
get { return m_ioc; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,12 @@ namespace KeePassLib.Keys
|
|||||||
get { return m_pbKeyData; }
|
get { return m_pbKeyData; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public KcpPassword(byte[] pbPasswordUtf8)
|
public uint GetMinKdbxVersion()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KcpPassword(byte[] pbPasswordUtf8)
|
||||||
{
|
{
|
||||||
SetKey(pbPasswordUtf8);
|
SetKey(pbPasswordUtf8);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,12 @@ namespace KeePassLib.Keys
|
|||||||
get { return m_pbKeyData; }
|
get { return m_pbKeyData; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public uint GetMinKdbxVersion()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
/// Construct a user account key.
|
/// Construct a user account key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public KcpUserAccount()
|
public KcpUserAccount()
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
if (!File.Exists(LogFilename))
|
if (!File.Exists(LogFilename))
|
||||||
{
|
{
|
||||||
File.Create(LogFilename);
|
File.Create(LogFilename).Dispose();
|
||||||
_logToFile = true;
|
_logToFile = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -100,8 +100,7 @@ namespace keepass2android
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
while (File.Exists(LogFilename + "." + count))
|
while (File.Exists(LogFilename + "." + count))
|
||||||
count++;
|
count++;
|
||||||
if (count > 0)
|
File.Move(LogFilename, LogFilename + "." + count);
|
||||||
File.Move(LogFilename, LogFilename + "." + count);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ namespace KeePassLib.Native
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Kp2aLog.Log(e.Message);
|
Kp2aLog.Log(e.ToString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -485,12 +485,6 @@ namespace KeePassLib
|
|||||||
set { m_pbHashOfLastIO = value; }
|
set { m_pbHashOfLastIO = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UseFileTransactions
|
|
||||||
{
|
|
||||||
get { return m_bUseFileTransactions; }
|
|
||||||
set { m_bUseFileTransactions = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UseFileLocks
|
public bool UseFileLocks
|
||||||
{
|
{
|
||||||
get { return m_bUseFileLocks; }
|
get { return m_bUseFileLocks; }
|
||||||
|
|||||||
@@ -360,5 +360,12 @@ namespace KeePassLib.Serialization
|
|||||||
m_ioCredProtMode = IOCredProtMode.None;
|
m_ioCredProtMode = IOCredProtMode.None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsSameFileAs(IOConnectionInfo other)
|
||||||
|
{
|
||||||
|
if (other == null)
|
||||||
|
return false;
|
||||||
|
return Path == other.Path && UserName == other.UserName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ using System.Diagnostics;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
@@ -126,8 +127,8 @@ namespace KeePassLib.Serialization
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const uint FileVersion32 = 0x00040000;
|
private const uint FileVersion32 = 0x00040000;
|
||||||
|
|
||||||
internal const uint FileVersion32_4 = 0x00040000; // First of 4.x series
|
public const uint FileVersion32_4 = 0x00040000; // First of 4.x series
|
||||||
internal const uint FileVersion32_3 = 0x00030001; // Old format 3.1
|
public const uint FileVersion32_3 = 0x00030001; // Old format 3.1
|
||||||
|
|
||||||
private const uint FileVersionCriticalMask = 0xFFFF0000;
|
private const uint FileVersionCriticalMask = 0xFFFF0000;
|
||||||
|
|
||||||
@@ -372,16 +373,19 @@ namespace KeePassLib.Serialization
|
|||||||
{
|
{
|
||||||
if(m_uForceVersion != 0) return m_uForceVersion;
|
if(m_uForceVersion != 0) return m_uForceVersion;
|
||||||
|
|
||||||
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
|
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
|
||||||
|
uint minVersionForKeys = m_pwDatabase.MasterKey.UserKeys.Select(key => key.GetMinKdbxVersion()).Max();
|
||||||
AesKdf kdfAes = new AesKdf();
|
|
||||||
|
AesKdf kdfAes = new AesKdf();
|
||||||
if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
|
if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
|
||||||
return FileVersion32;
|
return Math.Max(FileVersion32, minVersionForKeys);
|
||||||
|
|
||||||
if(m_pwDatabase.PublicCustomData.Count > 0)
|
if(m_pwDatabase.PublicCustomData.Count > 0)
|
||||||
return FileVersion32;
|
return Math.Max(FileVersion32, minVersionForKeys);
|
||||||
|
|
||||||
bool bCustomData = false;
|
|
||||||
|
|
||||||
|
bool bCustomData = false;
|
||||||
GroupHandler gh = delegate(PwGroup pg)
|
GroupHandler gh = delegate(PwGroup pg)
|
||||||
{
|
{
|
||||||
if(pg == null) { Debug.Assert(false); return true; }
|
if(pg == null) { Debug.Assert(false); return true; }
|
||||||
@@ -396,9 +400,10 @@ namespace KeePassLib.Serialization
|
|||||||
};
|
};
|
||||||
gh(m_pwDatabase.RootGroup);
|
gh(m_pwDatabase.RootGroup);
|
||||||
m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
|
m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
|
||||||
if(bCustomData) return FileVersion32;
|
if(bCustomData)
|
||||||
|
return Math.Max(FileVersion32, minVersionForKeys);
|
||||||
|
|
||||||
return FileVersion32_3; // KDBX 3.1 is sufficient
|
return Math.Max(FileVersion32_3, minVersionForKeys); ; // KDBX 3.1 is sufficient
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComputeKeys(out byte[] pbCipherKey, int cbCipherKey,
|
private void ComputeKeys(out byte[] pbCipherKey, int cbCipherKey,
|
||||||
|
|||||||
47
src/Kp2aBusinessLogic/ElementAndDatabaseId.cs
Normal file
47
src/Kp2aBusinessLogic/ElementAndDatabaseId.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Runtime;
|
||||||
|
using Android.Views;
|
||||||
|
using Android.Widget;
|
||||||
|
using keepass2android.Io;
|
||||||
|
using KeePassLib;
|
||||||
|
using KeePassLib.Interfaces;
|
||||||
|
using KeePassLib.Utility;
|
||||||
|
|
||||||
|
namespace keepass2android
|
||||||
|
{
|
||||||
|
public class ElementAndDatabaseId
|
||||||
|
{
|
||||||
|
private const char Separator = '+';
|
||||||
|
|
||||||
|
public ElementAndDatabaseId(Database db, IStructureItem element)
|
||||||
|
{
|
||||||
|
DatabaseId = db.IocAsHexString();
|
||||||
|
ElementIdString = element.Uuid.ToHexString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ElementAndDatabaseId(string fullId)
|
||||||
|
{
|
||||||
|
string[] parts = fullId.Split(Separator);
|
||||||
|
if (parts.Length != 2)
|
||||||
|
throw new Exception("Invalid full id " + fullId);
|
||||||
|
DatabaseId = parts[0];
|
||||||
|
ElementIdString = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DatabaseId { get; set; }
|
||||||
|
public string ElementIdString { get; set; }
|
||||||
|
public PwUuid ElementId { get { return new PwUuid(MemUtil.HexStringToByteArray(ElementIdString));} }
|
||||||
|
|
||||||
|
public string FullId
|
||||||
|
{
|
||||||
|
get { return DatabaseId + Separator + ElementIdString; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
@@ -9,6 +10,7 @@ using KeePassLib;
|
|||||||
using KeePassLib.Keys;
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
using keepass2android.Io;
|
using keepass2android.Io;
|
||||||
|
using KeePassLib.Interfaces;
|
||||||
#if !NoNet
|
#if !NoNet
|
||||||
using Keepass2android.Javafilestorage;
|
using Keepass2android.Javafilestorage;
|
||||||
#endif
|
#endif
|
||||||
@@ -33,31 +35,41 @@ namespace keepass2android
|
|||||||
/// This also contains methods which are UI specific and should be replacable for testing.
|
/// This also contains methods which are UI specific and should be replacable for testing.
|
||||||
public interface IKp2aApp : ICertificateValidationHandler
|
public interface IKp2aApp : ICertificateValidationHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Locks the currently open database, quicklocking if available (unless false is passed for allowQuickUnlock)
|
/// Locks all currently open databases, quicklocking if available (unless false is passed for allowQuickUnlock)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void LockDatabase(bool allowQuickUnlock = true);
|
void Lock(bool allowQuickUnlock);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Loads the specified data as the currently open database, as unlocked.
|
|
||||||
/// </summary>
|
|
||||||
void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey,
|
|
||||||
ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the current database
|
/// Loads the specified data as the currently open database, as unlocked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Database GetDb();
|
Database LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat, bool makeCurrent);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tell the app that the file from ioc was opened with keyfile.
|
HashSet<PwGroup> DirtyGroups { get; }
|
||||||
/// </summary>
|
|
||||||
void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, string displayName = "");
|
void MarkAllGroupsAsDirty();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the current database
|
||||||
|
/// </summary>
|
||||||
|
Database CurrentDb { get; }
|
||||||
|
|
||||||
|
IEnumerable<Database> OpenDatabases { get; }
|
||||||
|
void CloseDatabase(Database db);
|
||||||
|
|
||||||
|
Database FindDatabaseForElement(IStructureItem element);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tell the app that the file from ioc was opened with keyfile.
|
||||||
|
/// </summary>
|
||||||
|
void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, bool updateTimestamp, string displayName = "");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new database and returns it
|
/// Creates a new database and returns it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Database CreateNewDatabase();
|
Database CreateNewDatabase(bool makeCurrent);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the user-displayable string identified by stringKey
|
/// Returns the user-displayable string identified by stringKey
|
||||||
@@ -76,7 +88,8 @@ namespace keepass2android
|
|||||||
EventHandler<DialogClickEventArgs> yesHandler,
|
EventHandler<DialogClickEventArgs> yesHandler,
|
||||||
EventHandler<DialogClickEventArgs> noHandler,
|
EventHandler<DialogClickEventArgs> noHandler,
|
||||||
EventHandler<DialogClickEventArgs> cancelHandler,
|
EventHandler<DialogClickEventArgs> cancelHandler,
|
||||||
Context ctx);
|
Context ctx,
|
||||||
|
string messageSuffix = "");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asks the user the question "messageKey" with the options Yes/No/Cancel, but the yes/no strings can be selected freely, calls the handler corresponding to the answer.
|
/// Asks the user the question "messageKey" with the options Yes/No/Cancel, but the yes/no strings can be selected freely, calls the handler corresponding to the answer.
|
||||||
@@ -86,7 +99,8 @@ namespace keepass2android
|
|||||||
EventHandler<DialogClickEventArgs> yesHandler,
|
EventHandler<DialogClickEventArgs> yesHandler,
|
||||||
EventHandler<DialogClickEventArgs> noHandler,
|
EventHandler<DialogClickEventArgs> noHandler,
|
||||||
EventHandler<DialogClickEventArgs> cancelHandler,
|
EventHandler<DialogClickEventArgs> cancelHandler,
|
||||||
Context ctx);
|
Context ctx,
|
||||||
|
string messageSuffix = "");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a Handler object which can run tasks on the UI thread
|
/// Returns a Handler object which can run tasks on the UI thread
|
||||||
@@ -111,6 +125,10 @@ namespace keepass2android
|
|||||||
bool CheckForDuplicateUuids { get; }
|
bool CheckForDuplicateUuids { get; }
|
||||||
#if !NoNet
|
#if !NoNet
|
||||||
ICertificateErrorHandler CertificateErrorHandler { get; }
|
ICertificateErrorHandler CertificateErrorHandler { get; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,8 +207,7 @@ namespace keepass2android.Io
|
|||||||
|
|
||||||
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
//TODO: required for OTP Aux file retrieval
|
return IoUtil.GetParentPath(ioc);
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||||
|
|||||||
@@ -255,7 +255,8 @@ namespace keepass2android.Io
|
|||||||
if (ioc.IsLocalFile())
|
if (ioc.IsLocalFile())
|
||||||
{
|
{
|
||||||
bool requiresPermission = !(ioc.Path.StartsWith(activity.Activity.FilesDir.CanonicalPath)
|
bool requiresPermission = !(ioc.Path.StartsWith(activity.Activity.FilesDir.CanonicalPath)
|
||||||
|| ioc.Path.StartsWith(IoUtil.GetInternalDirectory(activity.Activity).CanonicalPath));
|
|| ioc.Path.StartsWith(IoUtil.GetInternalDirectory(activity.Activity).CanonicalPath)
|
||||||
|
|| ioc.Path.StartsWith(IoUtil.GetInternalDirectory(activity.Activity).CanonicalPath));
|
||||||
|
|
||||||
var extDirectory = activity.Activity.GetExternalFilesDir(null);
|
var extDirectory = activity.Activity.GetExternalFilesDir(null);
|
||||||
if ((extDirectory != null) && (ioc.Path.StartsWith(extDirectory.CanonicalPath)))
|
if ((extDirectory != null) && (ioc.Path.StartsWith(extDirectory.CanonicalPath)))
|
||||||
|
|||||||
@@ -65,22 +65,28 @@ namespace keepass2android.Io
|
|||||||
|
|
||||||
protected readonly OfflineSwitchableFileStorage _cachedStorage;
|
protected readonly OfflineSwitchableFileStorage _cachedStorage;
|
||||||
private readonly ICacheSupervisor _cacheSupervisor;
|
private readonly ICacheSupervisor _cacheSupervisor;
|
||||||
private readonly string _streamCacheDir;
|
private readonly string _legacyCacheDir;
|
||||||
|
private readonly string _cacheDir;
|
||||||
|
|
||||||
public CachingFileStorage(IFileStorage cachedStorage, string cacheDir, ICacheSupervisor cacheSupervisor)
|
public CachingFileStorage(IFileStorage cachedStorage, Context cacheDirContext, ICacheSupervisor cacheSupervisor)
|
||||||
{
|
{
|
||||||
_cachedStorage = new OfflineSwitchableFileStorage(cachedStorage);
|
_cachedStorage = new OfflineSwitchableFileStorage(cachedStorage);
|
||||||
_cacheSupervisor = cacheSupervisor;
|
_cacheSupervisor = cacheSupervisor;
|
||||||
_streamCacheDir = cacheDir + Java.IO.File.Separator + "OfflineCache" + Java.IO.File.Separator;
|
_legacyCacheDir = cacheDirContext.CacheDir.Path + Java.IO.File.Separator + "OfflineCache" + Java.IO.File.Separator;
|
||||||
if (!Directory.Exists(_streamCacheDir))
|
if (!Directory.Exists(_legacyCacheDir))
|
||||||
Directory.CreateDirectory(_streamCacheDir);
|
Directory.CreateDirectory(_legacyCacheDir);
|
||||||
|
|
||||||
}
|
_cacheDir = IoUtil.GetInternalDirectory(cacheDirContext).Path + Java.IO.File.Separator + "OfflineCache" + Java.IO.File.Separator;
|
||||||
|
if (!Directory.Exists(_cacheDir))
|
||||||
|
Directory.CreateDirectory(_cacheDir);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearCache()
|
public void ClearCache()
|
||||||
{
|
{
|
||||||
IoUtil.DeleteDir(new Java.IO.File(_streamCacheDir), true);
|
IoUtil.DeleteDir(new Java.IO.File(_legacyCacheDir), true);
|
||||||
}
|
IoUtil.DeleteDir(new Java.IO.File(_cacheDir), true);
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<string> SupportedProtocols { get { return _cachedStorage.SupportedProtocols; } }
|
public IEnumerable<string> SupportedProtocols { get { return _cachedStorage.SupportedProtocols; } }
|
||||||
|
|
||||||
@@ -105,7 +111,11 @@ namespace keepass2android.Io
|
|||||||
{
|
{
|
||||||
SHA256Managed sha256 = new SHA256Managed();
|
SHA256Managed sha256 = new SHA256Managed();
|
||||||
string iocAsHexString = MemUtil.ByteArrayToHexString(sha256.ComputeHash(Encoding.Unicode.GetBytes(ioc.Path.ToCharArray())))+".cache";
|
string iocAsHexString = MemUtil.ByteArrayToHexString(sha256.ComputeHash(Encoding.Unicode.GetBytes(ioc.Path.ToCharArray())))+".cache";
|
||||||
return _streamCacheDir + iocAsHexString;
|
if (File.Exists(_legacyCacheDir + iocAsHexString))
|
||||||
|
return _legacyCacheDir + iocAsHexString;
|
||||||
|
|
||||||
|
return _cacheDir + iocAsHexString;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsCached(IOConnectionInfo ioc)
|
public bool IsCached(IOConnectionInfo ioc)
|
||||||
@@ -168,7 +178,9 @@ namespace keepass2android.Io
|
|||||||
if (!IsCached(ioc))
|
if (!IsCached(ioc))
|
||||||
throw;
|
throw;
|
||||||
|
|
||||||
Kp2aLog.Log("couldn't open from remote " + ioc.Path);
|
#if DEBUG
|
||||||
|
Kp2aLog.Log("couldn't open from remote " + ioc.Path);
|
||||||
|
#endif
|
||||||
Kp2aLog.Log(ex.ToString());
|
Kp2aLog.Log(ex.ToString());
|
||||||
|
|
||||||
_cacheSupervisor.CouldntOpenFromRemote(ioc, ex);
|
_cacheSupervisor.CouldntOpenFromRemote(ioc, ex);
|
||||||
|
|||||||
@@ -233,7 +233,6 @@ namespace keepass2android.Io
|
|||||||
|
|
||||||
public FileDescription GetFileDescription(IOConnectionInfo ioc)
|
public FileDescription GetFileDescription(IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
Kp2aLog.Log("GetFileDescription "+ioc.Path);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return ConvertToFileDescription(Jfs.GetFileEntry(IocToPath(ioc)));
|
return ConvertToFileDescription(Jfs.GetFileEntry(IocToPath(ioc)));
|
||||||
@@ -302,7 +301,9 @@ namespace keepass2android.Io
|
|||||||
|
|
||||||
public void OnResume(IFileStorageSetupActivity activity)
|
public void OnResume(IFileStorageSetupActivity activity)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
Kp2aLog.Log("JFS/OnResume Ioc.Path=" +activity.Ioc.Path+". Path="+((IJavaFileStorageFileStorageSetupActivity)activity).Path);
|
Kp2aLog.Log("JFS/OnResume Ioc.Path=" +activity.Ioc.Path+". Path="+((IJavaFileStorageFileStorageSetupActivity)activity).Path);
|
||||||
|
#endif
|
||||||
_jfs.OnResume(((IJavaFileStorageFileStorageSetupActivity) activity));
|
_jfs.OnResume(((IJavaFileStorageFileStorageSetupActivity) activity));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,4 +367,4 @@ namespace keepass2android.Io
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
<Compile Include="database\CheckDatabaseForChanges.cs" />
|
<Compile Include="database\CheckDatabaseForChanges.cs" />
|
||||||
<Compile Include="database\edit\AddTemplateEntries.cs" />
|
<Compile Include="database\edit\AddTemplateEntries.cs" />
|
||||||
<Compile Include="database\edit\CopyEntry.cs" />
|
<Compile Include="database\edit\CopyEntry.cs" />
|
||||||
<Compile Include="database\edit\DeleteMultipleItems.cs" />
|
<Compile Include="database\edit\DeleteMultipleItemsFromOneDatabase.cs" />
|
||||||
<Compile Include="database\edit\EditGroup.cs" />
|
<Compile Include="database\edit\EditGroup.cs" />
|
||||||
<Compile Include="database\edit\MoveElements.cs" />
|
<Compile Include="database\edit\MoveElements.cs" />
|
||||||
<Compile Include="database\KdbDatabaseFormat.cs" />
|
<Compile Include="database\KdbDatabaseFormat.cs" />
|
||||||
@@ -103,6 +103,7 @@
|
|||||||
<Compile Include="DataExchange\Formats\KeePassKdb2x.cs" />
|
<Compile Include="DataExchange\Formats\KeePassKdb2x.cs" />
|
||||||
<Compile Include="DataExchange\Formats\KeePassXml2x.cs" />
|
<Compile Include="DataExchange\Formats\KeePassXml2x.cs" />
|
||||||
<Compile Include="DataExchange\PwExportInfo.cs" />
|
<Compile Include="DataExchange\PwExportInfo.cs" />
|
||||||
|
<Compile Include="ElementAndDatabaseId.cs" />
|
||||||
<Compile Include="Io\AndroidContentStorage.cs" />
|
<Compile Include="Io\AndroidContentStorage.cs" />
|
||||||
<Compile Include="Io\BuiltInFileStorage.cs" />
|
<Compile Include="Io\BuiltInFileStorage.cs" />
|
||||||
<Compile Include="Io\CachingFileStorage.cs" />
|
<Compile Include="Io\CachingFileStorage.cs" />
|
||||||
@@ -156,10 +157,6 @@
|
|||||||
<Compile Include="Utils\Spr\SprEngine.PickChars.cs" />
|
<Compile Include="Utils\Spr\SprEngine.PickChars.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AdalBindings\AdalBindings.csproj">
|
|
||||||
<Project>{0b109c0e-0514-4340-8779-5bd6a0dde84e}</Project>
|
|
||||||
<Name>AdalBindings</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
|
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
|
||||||
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
|
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
|
||||||
<Name>JavaFileStorageBindings</Name>
|
<Name>JavaFileStorageBindings</Name>
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ namespace keepass2android
|
|||||||
|
|
||||||
public void UpdateMessage (String message)
|
public void UpdateMessage (String message)
|
||||||
{
|
{
|
||||||
_message = message;
|
Kp2aLog.Log("status message: " + message);
|
||||||
|
_message = message;
|
||||||
if ( _app!= null && _progressDialog != null && _handler != null ) {
|
if ( _app!= null && _progressDialog != null && _handler != null ) {
|
||||||
_handler.Post(() => {_progressDialog.SetMessage(message); } );
|
_handler.Post(() => {_progressDialog.SetMessage(message); } );
|
||||||
}
|
}
|
||||||
@@ -60,6 +61,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
public void UpdateSubMessage(String submessage)
|
public void UpdateSubMessage(String submessage)
|
||||||
{
|
{
|
||||||
|
Kp2aLog.Log("status submessage: " + submessage);
|
||||||
_submessage = submessage;
|
_submessage = submessage;
|
||||||
if (_app != null && _progressDialog != null && _handler != null)
|
if (_app != null && _progressDialog != null && _handler != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace keepass2android
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// EqualityComparer implementation to compare PwGroups based on their Id
|
/// EqualityComparer implementation to compare PwGroups based on their Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class PwGroupEqualityFromIdComparer: IEqualityComparer<PwGroup>
|
public class PwGroupEqualityFromIdComparer: IEqualityComparer<PwGroup>
|
||||||
{
|
{
|
||||||
#region IEqualityComparer implementation
|
#region IEqualityComparer implementation
|
||||||
public bool Equals (PwGroup x, PwGroup y)
|
public bool Equals (PwGroup x, PwGroup y)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ namespace keepass2android
|
|||||||
PwGroup pgResults = new PwGroup(true, true, strGroupName, PwIcon.EMailSearch) {IsVirtual = true};
|
PwGroup pgResults = new PwGroup(true, true, strGroupName, PwIcon.EMailSearch) {IsVirtual = true};
|
||||||
if (String.IsNullOrWhiteSpace(host))
|
if (String.IsNullOrWhiteSpace(host))
|
||||||
return pgResults;
|
return pgResults;
|
||||||
foreach (PwEntry entry in database.Entries.Values)
|
foreach (PwEntry entry in database.EntriesById.Values)
|
||||||
{
|
{
|
||||||
string otherUrl = entry.Strings.ReadSafe(PwDefs.UrlField);
|
string otherUrl = entry.Strings.ReadSafe(PwDefs.UrlField);
|
||||||
otherUrl = SprEngine.Compile(otherUrl, new SprContext(entry, database.KpDatabase, SprCompileFlags.References));
|
otherUrl = SprEngine.Compile(otherUrl, new SprContext(entry, database.KpDatabase, SprCompileFlags.References));
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ namespace keepass2android
|
|||||||
ReadOnlyReason_PreKitKat,
|
ReadOnlyReason_PreKitKat,
|
||||||
ReadOnlyReason_ReadOnlyFlag,
|
ReadOnlyReason_ReadOnlyFlag,
|
||||||
ReadOnlyReason_ReadOnlyKitKat,
|
ReadOnlyReason_ReadOnlyKitKat,
|
||||||
ReadOnlyReason_LocalBackup
|
ReadOnlyReason_LocalBackup,
|
||||||
|
Ok,
|
||||||
|
cancel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IOConnectionInfo ioc = _app.GetDb().Ioc;
|
IOConnectionInfo ioc = _app.CurrentDb.Ioc;
|
||||||
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
||||||
if (fileStorage is CachingFileStorage)
|
if (fileStorage is CachingFileStorage)
|
||||||
{
|
{
|
||||||
@@ -49,7 +49,7 @@ namespace keepass2android
|
|||||||
hashingRemoteStream.CopyTo(remoteData);
|
hashingRemoteStream.CopyTo(remoteData);
|
||||||
hashingRemoteStream.Close();
|
hashingRemoteStream.Close();
|
||||||
|
|
||||||
if (!MemUtil.ArraysEqual(_app.GetDb().KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash))
|
if (!MemUtil.ArraysEqual(_app.CurrentDb.KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash))
|
||||||
{
|
{
|
||||||
_app.TriggerReload(_context);
|
_app.TriggerReload(_context);
|
||||||
Finish(true);
|
Finish(true);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ using KeePassLib;
|
|||||||
using KeePassLib.Keys;
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
using keepass2android.Io;
|
using keepass2android.Io;
|
||||||
|
using KeePassLib.Interfaces;
|
||||||
using KeePassLib.Utility;
|
using KeePassLib.Utility;
|
||||||
using Exception = System.Exception;
|
using Exception = System.Exception;
|
||||||
using String = System.String;
|
using String = System.String;
|
||||||
@@ -33,27 +34,23 @@ using String = System.String;
|
|||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
|
|
||||||
public class Database {
|
public class Database
|
||||||
|
{
|
||||||
|
public HashSet<IStructureItem> Elements = new HashSet<IStructureItem>();
|
||||||
public Dictionary<PwUuid, PwGroup> Groups = new Dictionary<PwUuid, PwGroup>(new PwUuidEqualityComparer());
|
public Dictionary<PwUuid, PwGroup> GroupsById = new Dictionary<PwUuid, PwGroup>(new PwUuidEqualityComparer());
|
||||||
public Dictionary<PwUuid, PwEntry> Entries = new Dictionary<PwUuid, PwEntry>(new PwUuidEqualityComparer());
|
public Dictionary<PwUuid, PwEntry> EntriesById = new Dictionary<PwUuid, PwEntry>(new PwUuidEqualityComparer());
|
||||||
public HashSet<PwGroup> Dirty = new HashSet<PwGroup>(new PwGroupEqualityFromIdComparer());
|
|
||||||
public PwGroup Root;
|
public PwGroup Root;
|
||||||
public PwDatabase KpDatabase;
|
public PwDatabase KpDatabase;
|
||||||
public IOConnectionInfo Ioc
|
public IOConnectionInfo Ioc
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return KpDatabase == null ? null : KpDatabase.IOConnectionInfo;
|
|
||||||
|
return KpDatabase?.IOConnectionInfo;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Information about the last opened entry. Includes the entry but also transformed fields.
|
|
||||||
/// </summary>
|
|
||||||
public PwEntryOutput LastOpenedEntry { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// if an OTP key was used, this property tells the location of the OTP auxiliary file.
|
/// if an OTP key was used, this property tells the location of the OTP auxiliary file.
|
||||||
/// Must be set after loading.
|
/// Must be set after loading.
|
||||||
@@ -73,31 +70,14 @@ namespace keepass2android
|
|||||||
_app = app;
|
_app = app;
|
||||||
CanWrite = true; //default
|
CanWrite = true; //default
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _loaded;
|
|
||||||
|
|
||||||
private bool _reloadRequested;
|
private IDatabaseFormat _databaseFormat = new KdbxDatabaseFormat(KdbxFormat.Default);
|
||||||
private IDatabaseFormat _databaseFormat = new KdbxDatabaseFormat(KdbxFormat.Default);
|
|
||||||
|
|
||||||
public bool ReloadRequested
|
public bool ReloadRequested { get; set; }
|
||||||
{
|
|
||||||
get { return _reloadRequested; }
|
|
||||||
set { _reloadRequested = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Loaded {
|
public bool DidOpenFileChange()
|
||||||
get { return _loaded;}
|
|
||||||
set { _loaded = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DidOpenFileChange()
|
|
||||||
{
|
{
|
||||||
if (Loaded == false)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return _app.GetFileStorage(Ioc).CheckForFileChangeFast(Ioc, LastFileVersion);
|
return _app.GetFileStorage(Ioc).CheckForFileChangeFast(Ioc, LastFileVersion);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -112,32 +92,20 @@ namespace keepass2android
|
|||||||
Stream s = databaseData ?? fileStorage.OpenFileForRead(iocInfo);
|
Stream s = databaseData ?? fileStorage.OpenFileForRead(iocInfo);
|
||||||
var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo);
|
var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo);
|
||||||
PopulateDatabaseFromStream(pwDatabase, s, iocInfo, compositeKey, status, databaseFormat);
|
PopulateDatabaseFromStream(pwDatabase, s, iocInfo, compositeKey, status, databaseFormat);
|
||||||
try
|
LastFileVersion = fileVersion;
|
||||||
{
|
|
||||||
LastFileVersion = fileVersion;
|
|
||||||
|
|
||||||
status.UpdateSubMessage("");
|
status.UpdateSubMessage("");
|
||||||
|
|
||||||
Root = pwDatabase.RootGroup;
|
Root = pwDatabase.RootGroup;
|
||||||
PopulateGlobals(Root);
|
PopulateGlobals(Root);
|
||||||
|
|
||||||
|
|
||||||
KpDatabase = pwDatabase;
|
KpDatabase = pwDatabase;
|
||||||
SearchHelper = new SearchDbHelper(app);
|
SearchHelper = new SearchDbHelper(app);
|
||||||
|
|
||||||
_databaseFormat = databaseFormat;
|
_databaseFormat = databaseFormat;
|
||||||
|
|
||||||
CanWrite = databaseFormat.CanWrite && !fileStorage.IsReadOnly(iocInfo);
|
CanWrite = databaseFormat.CanWrite && !fileStorage.IsReadOnly(iocInfo);
|
||||||
Loaded = true;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -219,8 +187,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
public void SaveData() {
|
public void SaveData() {
|
||||||
|
|
||||||
KpDatabase.UseFileTransactions = _app.GetBooleanPreference(PreferenceKey.UseFileTransactions);
|
using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
|
||||||
using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, KpDatabase.UseFileTransactions))
|
|
||||||
{
|
{
|
||||||
DatabaseFormat.Save(KpDatabase, trans.OpenFile());
|
DatabaseFormat.Save(KpDatabase, trans.OpenFile());
|
||||||
|
|
||||||
@@ -239,14 +206,18 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
if (checkForDuplicateUuids)
|
if (checkForDuplicateUuids)
|
||||||
{
|
{
|
||||||
if (Entries.ContainsKey(e.Uuid))
|
if (EntriesById.ContainsKey(e.Uuid))
|
||||||
{
|
{
|
||||||
throw new DuplicateUuidsException("Same UUID for entries '"+Entries[e.Uuid].Strings.ReadSafe(PwDefs.TitleField)+"' and '"+e.Strings.ReadSafe(PwDefs.TitleField)+"'.");
|
throw new DuplicateUuidsException("Same UUID for entries '"+EntriesById[e.Uuid].Strings.ReadSafe(PwDefs.TitleField)+"' and '"+e.Strings.ReadSafe(PwDefs.TitleField)+"'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Entries [e.Uuid] = e;
|
EntriesById [e.Uuid] = e;
|
||||||
|
Elements.Add(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GroupsById[currentGroup.Uuid] = currentGroup;
|
||||||
|
Elements.Add(currentGroup);
|
||||||
foreach (PwGroup g in childGroups)
|
foreach (PwGroup g in childGroups)
|
||||||
{
|
{
|
||||||
if (checkForDuplicateUuids)
|
if (checkForDuplicateUuids)
|
||||||
@@ -258,7 +229,6 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
* */
|
* */
|
||||||
}
|
}
|
||||||
Groups[g.Uuid] = g;
|
|
||||||
PopulateGlobals(g);
|
PopulateGlobals(g);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,33 +236,15 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
PopulateGlobals(currentGroup, _app.CheckForDuplicateUuids);
|
PopulateGlobals(currentGroup, _app.CheckForDuplicateUuids);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear() {
|
|
||||||
_loaded = false;
|
|
||||||
|
|
||||||
Groups.Clear();
|
|
||||||
Entries.Clear();
|
|
||||||
Dirty.Clear();
|
|
||||||
DrawableFactory.Clear();
|
|
||||||
|
|
||||||
Root = null;
|
|
||||||
KpDatabase = null;
|
|
||||||
|
|
||||||
CanWrite = true;
|
|
||||||
_reloadRequested = false;
|
|
||||||
OtpAuxFileIoc = null;
|
|
||||||
LastOpenedEntry = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MarkAllGroupsAsDirty() {
|
|
||||||
foreach ( PwGroup group in Groups.Values ) {
|
|
||||||
Dirty.Add(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
public void UpdateGlobals()
|
||||||
|
{
|
||||||
|
EntriesById.Clear();
|
||||||
|
GroupsById.Clear();
|
||||||
|
Elements.Clear();
|
||||||
|
PopulateGlobals(Root);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -216,9 +216,16 @@ namespace keepass2android
|
|||||||
|
|
||||||
private DateTime JavaTimeToCSharp(long javatime)
|
private DateTime JavaTimeToCSharp(long javatime)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var utcTime = new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
||||||
|
return TimeZoneInfo.ConvertTimeFromUtc(utcTime, TimeZoneInfo.Local);
|
||||||
|
}
|
||||||
|
catch (ArgumentOutOfRangeException)
|
||||||
|
{
|
||||||
|
return DateTime.MinValue;
|
||||||
|
}
|
||||||
|
|
||||||
var utcTime = new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
|
||||||
return TimeZoneInfo.ConvertTimeFromUtc(utcTime, TimeZoneInfo.Local);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ namespace keepass2android
|
|||||||
public class PwEntryOutput
|
public class PwEntryOutput
|
||||||
{
|
{
|
||||||
private readonly PwEntry _entry;
|
private readonly PwEntry _entry;
|
||||||
private readonly PwDatabase _db;
|
private readonly Database _db;
|
||||||
private readonly ProtectedStringDictionary _outputStrings = new ProtectedStringDictionary();
|
private readonly ProtectedStringDictionary _outputStrings = new ProtectedStringDictionary();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs the PwEntryOutput by replacing the placeholders
|
/// Constructs the PwEntryOutput by replacing the placeholders
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PwEntryOutput(PwEntry entry, PwDatabase db)
|
public PwEntryOutput(PwEntry entry, Database db)
|
||||||
{
|
{
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
_db = db;
|
_db = db;
|
||||||
@@ -34,7 +34,7 @@ namespace keepass2android
|
|||||||
string GetStringAndReplacePlaceholders(string key)
|
string GetStringAndReplacePlaceholders(string key)
|
||||||
{
|
{
|
||||||
String value = Entry.Strings.ReadSafe(key);
|
String value = Entry.Strings.ReadSafe(key);
|
||||||
value = SprEngine.Compile(value, new SprContext(Entry, _db, SprCompileFlags.All));
|
value = SprEngine.Compile(value, new SprContext(Entry, _db.KpDatabase, SprCompileFlags.All));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IOConnectionInfo ioc = _app.GetDb().Ioc;
|
IOConnectionInfo ioc = _app.CurrentDb.Ioc;
|
||||||
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
||||||
if (!(fileStorage is CachingFileStorage))
|
if (!(fileStorage is CachingFileStorage))
|
||||||
{
|
{
|
||||||
@@ -70,10 +70,12 @@ namespace keepass2android
|
|||||||
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
||||||
}
|
}
|
||||||
_saveDb = null;
|
_saveDb = null;
|
||||||
}), false, remoteData);
|
}), _app.CurrentDb, false, remoteData);
|
||||||
_saveDb.Run();
|
_saveDb.Run();
|
||||||
|
|
||||||
_app.GetDb().MarkAllGroupsAsDirty();
|
_app.CurrentDb.UpdateGlobals();
|
||||||
|
|
||||||
|
_app.MarkAllGroupsAsDirty();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -103,6 +105,7 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
Kp2aLog.LogUnexpectedError(e);
|
||||||
Finish(false, e.Message);
|
Finish(false, e.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace keepass2android
|
|||||||
public class AddEntry : RunnableOnFinish {
|
public class AddEntry : RunnableOnFinish {
|
||||||
protected Database Db
|
protected Database Db
|
||||||
{
|
{
|
||||||
get { return _app.GetDb(); }
|
get { return _app.CurrentDb; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
@@ -37,13 +37,13 @@ namespace keepass2android
|
|||||||
return new AddEntry(ctx, app, entry, parentGroup, finish);
|
return new AddEntry(ctx, app, entry, parentGroup, finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AddEntry(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
|
public AddEntry(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
_parentGroup = parentGroup;
|
_parentGroup = parentGroup;
|
||||||
_app = app;
|
_app = app;
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
|
|
||||||
_onFinishToRun = new AfterAdd(ctx, app.GetDb(), entry, OnFinishToRun);
|
_onFinishToRun = new AfterAdd(ctx, app.CurrentDb, entry, app,OnFinishToRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
@@ -68,12 +68,13 @@ namespace keepass2android
|
|||||||
private class AfterAdd : OnFinish {
|
private class AfterAdd : OnFinish {
|
||||||
private readonly Database _db;
|
private readonly Database _db;
|
||||||
private readonly PwEntry _entry;
|
private readonly PwEntry _entry;
|
||||||
|
private readonly IKp2aApp _app;
|
||||||
|
|
||||||
public AfterAdd(Activity activity, Database db, PwEntry entry, OnFinish finish):base(activity, finish) {
|
public AfterAdd(Activity activity, Database db, PwEntry entry, IKp2aApp app, OnFinish finish):base(activity, finish) {
|
||||||
_db = db;
|
_db = db;
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
|
_app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -83,11 +84,12 @@ namespace keepass2android
|
|||||||
PwGroup parent = _entry.ParentGroup;
|
PwGroup parent = _entry.ParentGroup;
|
||||||
|
|
||||||
// Mark parent group dirty
|
// Mark parent group dirty
|
||||||
_db.Dirty.Add(parent);
|
_app.DirtyGroups.Add(parent);
|
||||||
|
|
||||||
// Add entry to global
|
// Add entry to global
|
||||||
_db.Entries[_entry.Uuid] = _entry;
|
_db.EntriesById[_entry.Uuid] = _entry;
|
||||||
|
_db.Elements.Add(_entry);
|
||||||
|
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
|
StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
|
||||||
|
|||||||
@@ -26,13 +26,16 @@ namespace keepass2android
|
|||||||
public class AddGroup : RunnableOnFinish {
|
public class AddGroup : RunnableOnFinish {
|
||||||
internal Database Db
|
internal Database Db
|
||||||
{
|
{
|
||||||
get { return _app.GetDb(); }
|
get { return _app.CurrentDb; }
|
||||||
}
|
}
|
||||||
private IKp2aApp _app;
|
|
||||||
|
public IKp2aApp App { get => _app; }
|
||||||
|
|
||||||
|
private IKp2aApp _app;
|
||||||
private readonly String _name;
|
private readonly String _name;
|
||||||
private readonly int _iconId;
|
private readonly int _iconId;
|
||||||
private readonly PwUuid _groupCustomIconId;
|
private readonly PwUuid _groupCustomIconId;
|
||||||
internal PwGroup Group;
|
public PwGroup Group;
|
||||||
internal PwGroup Parent;
|
internal PwGroup Parent;
|
||||||
protected bool DontSave;
|
protected bool DontSave;
|
||||||
readonly Activity _ctx;
|
readonly Activity _ctx;
|
||||||
@@ -67,9 +70,11 @@ namespace keepass2android
|
|||||||
Group.CustomIconUuid = _groupCustomIconId;
|
Group.CustomIconUuid = _groupCustomIconId;
|
||||||
}
|
}
|
||||||
Parent.AddGroup(Group, true);
|
Parent.AddGroup(Group, true);
|
||||||
|
_app.CurrentDb.GroupsById[Group.Uuid] = Group;
|
||||||
|
_app.CurrentDb.Elements.Add(Group);
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, DontSave);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, DontSave);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
@@ -86,13 +91,15 @@ namespace keepass2android
|
|||||||
|
|
||||||
if ( Success ) {
|
if ( Success ) {
|
||||||
// Mark parent group dirty
|
// Mark parent group dirty
|
||||||
_addGroup.Db.Dirty.Add(_addGroup.Parent);
|
_addGroup.App.DirtyGroups.Add(_addGroup.Parent);
|
||||||
|
|
||||||
// Add group to global list
|
// Add group to global list
|
||||||
_addGroup.Db.Groups[_addGroup.Group.Uuid] = _addGroup.Group;
|
_addGroup.Db.GroupsById[_addGroup.Group.Uuid] = _addGroup.Group;
|
||||||
|
_addGroup.Db.Elements.Add(_addGroup.Group);
|
||||||
} else {
|
} else {
|
||||||
StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
|
StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
|
||||||
_addGroup.Parent.Groups.Remove(_addGroup.Group);
|
_addGroup.Parent.Groups.Remove(_addGroup.Group);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Run();
|
base.Run();
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
public class AddTemplateEntries : RunnableOnFinish {
|
public class AddTemplateEntries : RunnableOnFinish {
|
||||||
|
|
||||||
class TemplateEntry
|
public class TemplateEntry
|
||||||
{
|
{
|
||||||
public UiStringKey Title { get; set; }
|
public UiStringKey Title { get; set; }
|
||||||
public PwIcon Icon { get; set; }
|
public PwIcon Icon { get; set; }
|
||||||
@@ -47,11 +47,12 @@ namespace keepass2android
|
|||||||
void AddToEntry(IKp2aApp app, PwEntry entry, int position);
|
void AddToEntry(IKp2aApp app, PwEntry entry, int position);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum FieldType
|
public enum FieldType
|
||||||
{
|
{
|
||||||
Inline, ProtectedInline
|
Inline, ProtectedInline
|
||||||
}
|
}
|
||||||
internal enum SpecialFieldKey
|
|
||||||
|
public enum SpecialFieldKey
|
||||||
{
|
{
|
||||||
ExpDate, OverrideUrl, Tags
|
ExpDate, OverrideUrl, Tags
|
||||||
}
|
}
|
||||||
@@ -125,7 +126,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
protected Database Db
|
protected Database Db
|
||||||
{
|
{
|
||||||
get { return _app.GetDb(); }
|
get { return _app.CurrentDb; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
@@ -140,7 +141,7 @@ namespace keepass2android
|
|||||||
//_onFinishToRun = new AfterAdd(this, OnFinishToRun);
|
//_onFinishToRun = new AfterAdd(this, OnFinishToRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
|
public static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
|
||||||
{
|
{
|
||||||
new TemplateEntry()
|
new TemplateEntry()
|
||||||
{
|
{
|
||||||
@@ -285,12 +286,23 @@ namespace keepass2android
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static bool ContainsAllTemplates(IKp2aApp app)
|
public static bool ContainsAllTemplates(Database db)
|
||||||
{
|
{
|
||||||
return TemplateEntries.All(t => app.GetDb().Entries.ContainsKey(t.Uuid));
|
return TemplateEntries.All(t =>
|
||||||
|
{
|
||||||
|
string hexId = t.Uuid.ToHexString();
|
||||||
|
|
||||||
|
return db.EntriesById.Any(kvp => kvp.Key.Equals(t.Uuid) ||
|
||||||
|
kvp.Value.Strings.ReadSafe(TemplateIdStringKey) == hexId);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Run() {
|
public static string TemplateIdStringKey
|
||||||
|
{
|
||||||
|
get { return "KP2A_TemplateId"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Run() {
|
||||||
StatusLogger.UpdateMessage(UiStringKey.AddingEntry);
|
StatusLogger.UpdateMessage(UiStringKey.AddingEntry);
|
||||||
|
|
||||||
List<PwEntry> addedEntries;
|
List<PwEntry> addedEntries;
|
||||||
@@ -298,10 +310,10 @@ namespace keepass2android
|
|||||||
|
|
||||||
if (addedEntries.Any())
|
if (addedEntries.Any())
|
||||||
{
|
{
|
||||||
_app.GetDb().Dirty.Add(templateGroup);
|
_app.DirtyGroups.Add(templateGroup);
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
@@ -315,26 +327,28 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
PwGroup templateGroup;
|
PwGroup templateGroup;
|
||||||
if (!_app.GetDb().Groups.TryGetValue(_app.GetDb().KpDatabase.EntryTemplatesGroup, out templateGroup))
|
if (!_app.CurrentDb.GroupsById.TryGetValue(_app.CurrentDb.KpDatabase.EntryTemplatesGroup, out templateGroup))
|
||||||
{
|
{
|
||||||
//create template group
|
//create template group
|
||||||
templateGroup = new PwGroup(true, true, _app.GetResourceString(UiStringKey.TemplateGroupName), PwIcon.Folder);
|
templateGroup = new PwGroup(true, true, _app.GetResourceString(UiStringKey.TemplateGroupName), PwIcon.Folder);
|
||||||
_app.GetDb().KpDatabase.RootGroup.AddGroup(templateGroup, true);
|
_app.CurrentDb.KpDatabase.RootGroup.AddGroup(templateGroup, true);
|
||||||
_app.GetDb().KpDatabase.EntryTemplatesGroup = templateGroup.Uuid;
|
_app.CurrentDb.KpDatabase.EntryTemplatesGroup = templateGroup.Uuid;
|
||||||
_app.GetDb().KpDatabase.EntryTemplatesGroupChanged = DateTime.Now;
|
_app.CurrentDb.KpDatabase.EntryTemplatesGroupChanged = DateTime.Now;
|
||||||
_app.GetDb().Dirty.Add(_app.GetDb().KpDatabase.RootGroup);
|
_app.DirtyGroups.Add(_app.CurrentDb.KpDatabase.RootGroup);
|
||||||
_app.GetDb().Groups[templateGroup.Uuid] = templateGroup;
|
_app.CurrentDb.GroupsById[templateGroup.Uuid] = templateGroup;
|
||||||
|
_app.CurrentDb.Elements.Add(templateGroup);
|
||||||
|
|
||||||
}
|
}
|
||||||
addedEntries = new List<PwEntry>();
|
addedEntries = new List<PwEntry>();
|
||||||
|
|
||||||
foreach (var template in TemplateEntries)
|
foreach (var template in TemplateEntries)
|
||||||
{
|
{
|
||||||
if (_app.GetDb().Entries.ContainsKey(template.Uuid))
|
if (_app.CurrentDb.EntriesById.ContainsKey(template.Uuid))
|
||||||
continue;
|
continue;
|
||||||
PwEntry entry = CreateEntry(template);
|
PwEntry entry = CreateEntry(template);
|
||||||
templateGroup.AddEntry(entry, true);
|
templateGroup.AddEntry(entry, true);
|
||||||
addedEntries.Add(entry);
|
addedEntries.Add(entry);
|
||||||
_app.GetDb().Entries[entry.Uuid] = entry;
|
_app.CurrentDb.EntriesById[entry.Uuid] = entry;
|
||||||
}
|
}
|
||||||
return templateGroup;
|
return templateGroup;
|
||||||
}
|
}
|
||||||
@@ -373,8 +387,12 @@ namespace keepass2android
|
|||||||
base.Run();
|
base.Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static bool IsTemplateId(PwUuid pwUuid)
|
||||||
|
{
|
||||||
|
return TemplateEntries.Any(te => te.Uuid.Equals(pwUuid));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,23 +27,22 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
|
|
||||||
public class CreateDb : RunnableOnFinish {
|
public class CreateDb : RunnableOnFinish {
|
||||||
|
private readonly IOConnectionInfo _ioc;
|
||||||
private const ulong DefaultEncryptionRounds = PwDefs.DefaultKeyEncryptionRounds;
|
|
||||||
|
|
||||||
private readonly IOConnectionInfo _ioc;
|
|
||||||
private readonly bool _dontSave;
|
private readonly bool _dontSave;
|
||||||
private readonly Activity _ctx;
|
private readonly Activity _ctx;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private CompositeKey _key;
|
private CompositeKey _key;
|
||||||
|
private readonly bool _makeCurrent;
|
||||||
|
|
||||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave): base(ctx, finish) {
|
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, bool makeCurrent): base(ctx, finish) {
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
_ioc = ioc;
|
_ioc = ioc;
|
||||||
_dontSave = dontSave;
|
_dontSave = dontSave;
|
||||||
_app = app;
|
_makeCurrent = makeCurrent;
|
||||||
|
_app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, CompositeKey key)
|
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, CompositeKey key, bool makeCurrent)
|
||||||
: base(ctx, finish)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
@@ -51,12 +50,13 @@ namespace keepass2android
|
|||||||
_dontSave = dontSave;
|
_dontSave = dontSave;
|
||||||
_app = app;
|
_app = app;
|
||||||
_key = key;
|
_key = key;
|
||||||
|
_makeCurrent = makeCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void Run() {
|
public override void Run() {
|
||||||
StatusLogger.UpdateMessage(UiStringKey.progress_create);
|
StatusLogger.UpdateMessage(UiStringKey.progress_create);
|
||||||
Database db = _app.CreateNewDatabase();
|
Database db = _app.CreateNewDatabase(_makeCurrent);
|
||||||
|
|
||||||
db.KpDatabase = new KeePassLib.PwDatabase();
|
db.KpDatabase = new KeePassLib.PwDatabase();
|
||||||
|
|
||||||
@@ -74,7 +74,6 @@ namespace keepass2android
|
|||||||
|
|
||||||
// Set Database state
|
// Set Database state
|
||||||
db.Root = db.KpDatabase.RootGroup;
|
db.Root = db.KpDatabase.RootGroup;
|
||||||
db.Loaded = true;
|
|
||||||
db.SearchHelper = new SearchDbHelper(_app);
|
db.SearchHelper = new SearchDbHelper(_app);
|
||||||
|
|
||||||
// Add a couple default groups
|
// Add a couple default groups
|
||||||
@@ -88,12 +87,14 @@ namespace keepass2android
|
|||||||
addTemplates.AddTemplates(out addedEntries);
|
addTemplates.AddTemplates(out addedEntries);
|
||||||
|
|
||||||
// Commit changes
|
// Commit changes
|
||||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave);
|
SaveDb save = new SaveDb(_ctx, _app, db, OnFinishToRun, _dontSave);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
_onFinishToRun = null;
|
_onFinishToRun = null;
|
||||||
save.Run();
|
save.Run();
|
||||||
|
|
||||||
|
db.UpdateGlobals();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
public DeleteEntry(Activity activiy, IKp2aApp app, PwEntry entry, OnFinish finish):base(activiy, finish, app) {
|
public DeleteEntry(Activity activiy, IKp2aApp app, PwEntry entry, OnFinish finish):base(activiy, finish, app) {
|
||||||
Ctx = activiy;
|
Ctx = activiy;
|
||||||
Db = app.GetDb();
|
Db = app.FindDatabaseForElement(entry);
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return App.GetDb().DatabaseFormat.CanRecycle && CanRecycleGroup(_entry.ParentGroup);
|
return Db.DatabaseFormat.CanRecycle && CanRecycleGroup(_entry.ParentGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace keepass2android
|
|||||||
*/
|
*/
|
||||||
private void SetMembers(Activity activity, IKp2aApp app, PwGroup group, bool dontSave)
|
private void SetMembers(Activity activity, IKp2aApp app, PwGroup group, bool dontSave)
|
||||||
{
|
{
|
||||||
base.SetMembers(activity, app.GetDb());
|
base.SetMembers(activity, app.FindDatabaseForElement(group));
|
||||||
|
|
||||||
_group = group;
|
_group = group;
|
||||||
DontSave = dontSave;
|
DontSave = dontSave;
|
||||||
@@ -58,7 +58,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return App.GetDb().DatabaseFormat.CanRecycle && CanRecycleGroup(_group);
|
return Db.DatabaseFormat.CanRecycle && CanRecycleGroup(_group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ using KeePassLib.Interfaces;
|
|||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public class DeleteMultipleItems : DeleteRunnable
|
public class DeleteMultipleItemsFromOneDatabase : DeleteRunnable
|
||||||
{
|
{
|
||||||
private readonly List<IStructureItem> _elementsToDelete;
|
private readonly List<IStructureItem> _elementsToDelete;
|
||||||
private readonly bool _canRecycle;
|
private readonly bool _canRecycle;
|
||||||
|
|
||||||
public DeleteMultipleItems(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
|
public DeleteMultipleItemsFromOneDatabase(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
|
||||||
: base(activity, finish, app)
|
: base(activity, finish, app)
|
||||||
{
|
{
|
||||||
_elementsToDelete = elementsToDelete;
|
_elementsToDelete = elementsToDelete;
|
||||||
@@ -21,12 +21,13 @@ namespace keepass2android
|
|||||||
//determine once. The property is queried for each delete operation, but might return false
|
//determine once. The property is queried for each delete operation, but might return false
|
||||||
//after one entry/group is deleted (and thus in recycle bin and thus can't be recycled anymore)
|
//after one entry/group is deleted (and thus in recycle bin and thus can't be recycled anymore)
|
||||||
_canRecycle = DetermineCanRecycle();
|
_canRecycle = DetermineCanRecycle();
|
||||||
|
ShowDatabaseIocInStatus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DetermineCanRecycle()
|
private bool DetermineCanRecycle()
|
||||||
{
|
{
|
||||||
Android.Util.Log.Debug("KP2A", "CanRecycle?");
|
Android.Util.Log.Debug("KP2A", "CanRecycle?");
|
||||||
if (!App.GetDb().DatabaseFormat.CanRecycle)
|
if (!Db.DatabaseFormat.CanRecycle)
|
||||||
{
|
{
|
||||||
Android.Util.Log.Debug("KP2A", "CanRecycle? No because of DB format.");
|
Android.Util.Log.Debug("KP2A", "CanRecycle? No because of DB format.");
|
||||||
return false;
|
return false;
|
||||||
@@ -100,7 +100,8 @@ namespace keepass2android
|
|||||||
};
|
};
|
||||||
|
|
||||||
Db.KpDatabase.RootGroup.AddGroup(pgRecycleBin, true);
|
Db.KpDatabase.RootGroup.AddGroup(pgRecycleBin, true);
|
||||||
Db.Groups[pgRecycleBin.Uuid] = pgRecycleBin;
|
Db.GroupsById[pgRecycleBin.Uuid] = pgRecycleBin;
|
||||||
|
Db.Elements.Add(pgRecycleBin);
|
||||||
Db.KpDatabase.RecycleBinUuid = pgRecycleBin.Uuid;
|
Db.KpDatabase.RecycleBinUuid = pgRecycleBin.Uuid;
|
||||||
|
|
||||||
bGroupListUpdateRequired = true;
|
bGroupListUpdateRequired = true;
|
||||||
@@ -121,24 +122,27 @@ namespace keepass2android
|
|||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
if (CanRecycle)
|
string messageSuffix = ShowDatabaseIocInStatus ? "(" + App.GetFileStorage(Db.Ioc).GetDisplayName(Db.Ioc) + ")" : "";
|
||||||
|
|
||||||
|
if (CanRecycle)
|
||||||
{
|
{
|
||||||
App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
|
App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
|
||||||
QuestionRecycleResourceId,
|
QuestionRecycleResourceId,
|
||||||
(dlgSender, dlgEvt) =>
|
(dlgSender, dlgEvt) =>
|
||||||
{
|
{
|
||||||
DeletePermanently = true;
|
DeletePermanently = true;
|
||||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||||
pt.Run();
|
pt.Run();
|
||||||
|
|
||||||
},
|
},
|
||||||
(dlgSender, dlgEvt) =>
|
(dlgSender, dlgEvt) =>
|
||||||
{
|
{
|
||||||
DeletePermanently = false;
|
DeletePermanently = false;
|
||||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||||
pt.Run();
|
pt.Run();
|
||||||
},
|
},
|
||||||
(dlgSender, dlgEvt) => { },
|
(dlgSender, dlgEvt) => { },
|
||||||
Ctx);
|
Ctx, messageSuffix);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -149,12 +153,12 @@ namespace keepass2android
|
|||||||
QuestionNoRecycleResourceId,
|
QuestionNoRecycleResourceId,
|
||||||
(dlgSender, dlgEvt) =>
|
(dlgSender, dlgEvt) =>
|
||||||
{
|
{
|
||||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||||
pt.Run();
|
pt.Run();
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
(dlgSender, dlgEvt) => { },
|
(dlgSender, dlgEvt) => { },
|
||||||
Ctx);
|
Ctx, messageSuffix);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -182,7 +186,8 @@ namespace keepass2android
|
|||||||
PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
|
PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
|
||||||
pd.DeletedObjects.Add(pdo);
|
pd.DeletedObjects.Add(pdo);
|
||||||
touchedGroups.Add(pgParent);
|
touchedGroups.Add(pgParent);
|
||||||
Db.Entries.Remove(pe.Uuid);
|
Db.EntriesById.Remove(pe.Uuid);
|
||||||
|
Db.Elements.Remove(pe);
|
||||||
}
|
}
|
||||||
else // Recycle
|
else // Recycle
|
||||||
{
|
{
|
||||||
@@ -215,31 +220,41 @@ namespace keepass2android
|
|||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
foreach (var g in touchedGroups)
|
foreach (var g in touchedGroups)
|
||||||
Db.Dirty.Add(g);
|
App.DirtyGroups.Add(g);
|
||||||
foreach (var g in permanentlyDeletedGroups)
|
foreach (var g in permanentlyDeletedGroups)
|
||||||
{
|
{
|
||||||
//remove groups from global lists if present there
|
//remove groups from global lists if present there
|
||||||
Db.Dirty.Remove(g);
|
App.DirtyGroups.Remove(g);
|
||||||
Db.Groups.Remove(g.Uuid);
|
Db.GroupsById.Remove(g.Uuid);
|
||||||
|
Db.Elements.Remove(g);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Let's not bother recovering from a failure to save. It is too much work.
|
// Let's not bother recovering from a failure to save. It is too much work.
|
||||||
App.LockDatabase(false);
|
App.Lock(false);
|
||||||
}
|
}
|
||||||
}, OnFinishToRun);
|
}, OnFinishToRun);
|
||||||
|
|
||||||
// Commit database
|
// Commit database
|
||||||
SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, false);
|
SaveDb save = new SaveDb(Ctx, App, Db, OnFinishToRun, false);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.ShowDatabaseIocInStatus = ShowDatabaseIocInStatus;
|
||||||
|
|
||||||
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups);
|
public bool ShowDatabaseIocInStatus
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups);
|
||||||
|
|
||||||
public abstract UiStringKey StatusMessage { get; }
|
public abstract UiStringKey StatusMessage { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,12 @@ namespace keepass2android
|
|||||||
public class EditGroup : RunnableOnFinish {
|
public class EditGroup : RunnableOnFinish {
|
||||||
internal Database Db
|
internal Database Db
|
||||||
{
|
{
|
||||||
get { return _app.GetDb(); }
|
get { return _app.FindDatabaseForElement(Group); }
|
||||||
}
|
}
|
||||||
private IKp2aApp _app;
|
|
||||||
|
public IKp2aApp App { get => _app; }
|
||||||
|
|
||||||
|
private IKp2aApp _app;
|
||||||
private readonly String _name;
|
private readonly String _name;
|
||||||
private readonly PwIcon _iconId;
|
private readonly PwIcon _iconId;
|
||||||
private readonly PwUuid _customIconId;
|
private readonly PwUuid _customIconId;
|
||||||
@@ -57,7 +60,7 @@ namespace keepass2android
|
|||||||
Group.Touch(true);
|
Group.Touch(true);
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
|
SaveDb save = new SaveDb(_ctx, _app, Db, OnFinishToRun);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
@@ -76,10 +79,10 @@ namespace keepass2android
|
|||||||
|
|
||||||
if ( Success ) {
|
if ( Success ) {
|
||||||
// Mark parent group dirty
|
// Mark parent group dirty
|
||||||
_editGroup.Db.Dirty.Add(_editGroup.Group.ParentGroup);
|
_editGroup.App.DirtyGroups.Add(_editGroup.Group.ParentGroup);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
_editGroup._app.LockDatabase(false);
|
_editGroup._app.Lock(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Run();
|
base.Run();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
|
using keepass2android.database.edit;
|
||||||
using KeePassLib;
|
using KeePassLib;
|
||||||
using KeePassLib.Keys;
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
@@ -36,20 +37,25 @@ namespace keepass2android
|
|||||||
private readonly bool _rememberKeyfile;
|
private readonly bool _rememberKeyfile;
|
||||||
IDatabaseFormat _format;
|
IDatabaseFormat _format;
|
||||||
|
|
||||||
public LoadDb(Activity activity, IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish): base(activity, finish)
|
public LoadDb(Activity activity, IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish, bool updateLastUsageTimestamp, bool makeCurrent): base(activity, finish)
|
||||||
{
|
{
|
||||||
_app = app;
|
_app = app;
|
||||||
_ioc = ioc;
|
_ioc = ioc;
|
||||||
_databaseData = databaseData;
|
_databaseData = databaseData;
|
||||||
_compositeKey = compositeKey;
|
_compositeKey = compositeKey;
|
||||||
_keyfileOrProvider = keyfileOrProvider;
|
_keyfileOrProvider = keyfileOrProvider;
|
||||||
|
_updateLastUsageTimestamp = updateLastUsageTimestamp;
|
||||||
|
_makeCurrent = makeCurrent;
|
||||||
|
|
||||||
|
|
||||||
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
|
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected bool success = false;
|
||||||
public override void Run()
|
private bool _updateLastUsageTimestamp;
|
||||||
|
private readonly bool _makeCurrent;
|
||||||
|
|
||||||
|
public override void Run()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -78,6 +84,10 @@ namespace keepass2android
|
|||||||
//ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess:
|
//ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess:
|
||||||
_format = new KdbxDatabaseFormat(KdbpFile.GetFormatToUse(_ioc));
|
_format = new KdbxDatabaseFormat(KdbpFile.GetFormatToUse(_ioc));
|
||||||
TryLoad(databaseStream);
|
TryLoad(databaseStream);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
success = true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -89,7 +99,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
Kp2aLog.Log("KeyFileException");
|
Kp2aLog.Log("KeyFileException");
|
||||||
Finish(false, /*TODO Localize: use Keepass error text KPRes.KeyFileError (including "or invalid format")*/
|
Finish(false, /*TODO Localize: use Keepass error text KPRes.KeyFileError (including "or invalid format")*/
|
||||||
_app.GetResourceString(UiStringKey.keyfile_does_not_exist), Exception);
|
_app.GetResourceString(UiStringKey.keyfile_does_not_exist), false, Exception);
|
||||||
}
|
}
|
||||||
catch (AggregateException e)
|
catch (AggregateException e)
|
||||||
{
|
{
|
||||||
@@ -100,20 +110,20 @@ namespace keepass2android
|
|||||||
// Override the message shown with the last (hopefully most recent) inner exception
|
// Override the message shown with the last (hopefully most recent) inner exception
|
||||||
Kp2aLog.LogUnexpectedError(innerException);
|
Kp2aLog.LogUnexpectedError(innerException);
|
||||||
}
|
}
|
||||||
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + message, Exception);
|
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + message, false, Exception);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (DuplicateUuidsException e)
|
catch (DuplicateUuidsException e)
|
||||||
{
|
{
|
||||||
Kp2aLog.Log(e.ToString());
|
Kp2aLog.Log(e.ToString());
|
||||||
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError) + " " + e.Message + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional), Exception);
|
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError) + " " + e.Message + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional), false, Exception);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
if (!(e is InvalidCompositeKeyException))
|
if (!(e is InvalidCompositeKeyException))
|
||||||
Kp2aLog.LogUnexpectedError(e);
|
Kp2aLog.LogUnexpectedError(e);
|
||||||
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message, Exception);
|
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message, false, Exception);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +135,7 @@ namespace keepass2android
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Exception Exception { get; set; }
|
public Exception Exception { get; set; }
|
||||||
|
|
||||||
private void TryLoad(MemoryStream databaseStream)
|
Database TryLoad(MemoryStream databaseStream)
|
||||||
{
|
{
|
||||||
//create a copy of the stream so we can try again if we get an exception which indicates we should change parameters
|
//create a copy of the stream so we can try again if we get an exception which indicates we should change parameters
|
||||||
//This is not optimal in terms of (short-time) memory usage but is hard to avoid because the Keepass library closes streams also in case of errors.
|
//This is not optimal in terms of (short-time) memory usage but is hard to avoid because the Keepass library closes streams also in case of errors.
|
||||||
@@ -138,19 +148,16 @@ namespace keepass2android
|
|||||||
//now let's go:
|
//now let's go:
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format);
|
Database newDb = _app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format, _makeCurrent);
|
||||||
Kp2aLog.Log("LoadDB OK");
|
Kp2aLog.Log("LoadDB OK");
|
||||||
|
|
||||||
//make sure the stored access time for the actual file is more recent than that of its backup
|
|
||||||
Thread.Sleep(10);
|
|
||||||
SaveFileData(_ioc, _keyfileOrProvider);
|
|
||||||
|
|
||||||
Finish(true, _format.SuccessMessage);
|
Finish(true, _format.SuccessMessage);
|
||||||
|
return newDb;
|
||||||
}
|
}
|
||||||
catch (OldFormatException)
|
catch (OldFormatException)
|
||||||
{
|
{
|
||||||
_format = new KdbDatabaseFormat(_app);
|
_format = new KdbDatabaseFormat(_app);
|
||||||
TryLoad(databaseStream);
|
return TryLoad(databaseStream);
|
||||||
}
|
}
|
||||||
catch (InvalidCompositeKeyException)
|
catch (InvalidCompositeKeyException)
|
||||||
{
|
{
|
||||||
@@ -162,7 +169,7 @@ namespace keepass2android
|
|||||||
//retry without password:
|
//retry without password:
|
||||||
_compositeKey.RemoveUserKey(passwordKey);
|
_compositeKey.RemoveUserKey(passwordKey);
|
||||||
//retry:
|
//retry:
|
||||||
TryLoad(databaseStream);
|
return TryLoad(databaseStream);
|
||||||
}
|
}
|
||||||
else throw;
|
else throw;
|
||||||
}
|
}
|
||||||
@@ -175,7 +182,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
keyfileOrProvider = "";
|
keyfileOrProvider = "";
|
||||||
}
|
}
|
||||||
_app.StoreOpenedFileAsRecent(ioc, keyfileOrProvider);
|
_app.StoreOpenedFileAsRecent(ioc, keyfileOrProvider, _updateLastUsageTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
@@ -50,10 +51,19 @@ namespace keepass2android.database.edit
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HashSet<Database> removeDatabases = new HashSet<Database>();
|
||||||
|
Database addDatabase = _app.FindDatabaseForElement(_targetGroup);
|
||||||
|
if (addDatabase == null)
|
||||||
|
{
|
||||||
|
Finish(false, "Did not find target database. Did you lock it?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var elementToMove in _elementsToMove)
|
foreach (var elementToMove in _elementsToMove)
|
||||||
{
|
{
|
||||||
|
|
||||||
_app.GetDb().Dirty.Add(elementToMove.ParentGroup);
|
_app.DirtyGroups.Add(elementToMove.ParentGroup);
|
||||||
|
|
||||||
|
|
||||||
PwGroup pgParent = elementToMove.ParentGroup;
|
PwGroup pgParent = elementToMove.ParentGroup;
|
||||||
if (pgParent != _targetGroup)
|
if (pgParent != _targetGroup)
|
||||||
@@ -63,8 +73,14 @@ namespace keepass2android.database.edit
|
|||||||
PwEntry entry = elementToMove as PwEntry;
|
PwEntry entry = elementToMove as PwEntry;
|
||||||
if (entry != null)
|
if (entry != null)
|
||||||
{
|
{
|
||||||
|
var dbRem = _app.FindDatabaseForElement(entry);
|
||||||
|
removeDatabases.Add(dbRem);
|
||||||
|
dbRem.EntriesById.Remove(entry.Uuid);
|
||||||
|
dbRem.Elements.Remove(entry);
|
||||||
pgParent.Entries.Remove(entry);
|
pgParent.Entries.Remove(entry);
|
||||||
_targetGroup.AddEntry(entry, true, true);
|
_targetGroup.AddEntry(entry, true, true);
|
||||||
|
addDatabase.EntriesById.Add(entry.Uuid, entry);
|
||||||
|
addDatabase.Elements.Add(entry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -74,27 +90,60 @@ namespace keepass2android.database.edit
|
|||||||
Finish(false, _app.GetResourceString(UiStringKey.CannotMoveGroupHere));
|
Finish(false, _app.GetResourceString(UiStringKey.CannotMoveGroupHere));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dbRem = _app.FindDatabaseForElement(@group);
|
||||||
|
if (dbRem == null)
|
||||||
|
{
|
||||||
|
Finish(false, "Did not find source database. Did you lock it?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbRem.GroupsById.Remove(group.Uuid);
|
||||||
|
dbRem.Elements.Remove(group);
|
||||||
|
removeDatabases.Add(dbRem);
|
||||||
pgParent.Groups.Remove(group);
|
pgParent.Groups.Remove(group);
|
||||||
_targetGroup.AddGroup(group, true, true);
|
_targetGroup.AddGroup(group, true, true);
|
||||||
|
addDatabase.GroupsById.Add(group.Uuid, group);
|
||||||
|
addDatabase.Elements.Add(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_onFinishToRun = new ActionOnFinish(ActiveActivity, (success, message, activity) =>
|
|
||||||
{
|
|
||||||
if (!success)
|
|
||||||
{ // Let's not bother recovering from a failure.
|
|
||||||
_app.LockDatabase(false);
|
|
||||||
}
|
|
||||||
}, OnFinishToRun);
|
|
||||||
|
|
||||||
// Save
|
|
||||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, false);
|
|
||||||
save.SetStatusLogger(StatusLogger);
|
|
||||||
save.Run();
|
//first save the database where we added the elements
|
||||||
|
var allDatabasesToSave = new List<Database> {addDatabase};
|
||||||
|
//then all databases where we removed elements:
|
||||||
|
removeDatabases.RemoveWhere(db => db == addDatabase);
|
||||||
|
allDatabasesToSave.AddRange(removeDatabases);
|
||||||
|
|
||||||
|
int indexToSave = 0;
|
||||||
|
bool allSavesSuccess = true;
|
||||||
|
void ContinueSave(bool success, string message, Activity activeActivity)
|
||||||
|
{
|
||||||
|
allSavesSuccess &= success;
|
||||||
|
indexToSave++;
|
||||||
|
if (indexToSave == allDatabasesToSave.Count)
|
||||||
|
{
|
||||||
|
OnFinishToRun.SetResult(allSavesSuccess);
|
||||||
|
OnFinishToRun.Run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaveDb saveDb = new SaveDb(_ctx, _app, allDatabasesToSave[indexToSave], new ActionOnFinish(activeActivity, ContinueSave), false);
|
||||||
|
saveDb.SetStatusLogger(StatusLogger);
|
||||||
|
saveDb.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
|
||||||
|
saveDb.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SaveDb save = new SaveDb(_ctx, _app, allDatabasesToSave[0], new ActionOnFinish(ActiveActivity, ContinueSave), false);
|
||||||
|
save.SetStatusLogger(StatusLogger);
|
||||||
|
save.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
|
||||||
|
save.Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Android;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
@@ -28,8 +29,14 @@ namespace keepass2android
|
|||||||
protected bool Success;
|
protected bool Success;
|
||||||
protected String Message;
|
protected String Message;
|
||||||
protected Exception Exception;
|
protected Exception Exception;
|
||||||
|
|
||||||
protected OnFinish BaseOnFinish;
|
protected bool ImportantMessage
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OnFinish BaseOnFinish;
|
||||||
protected Handler Handler;
|
protected Handler Handler;
|
||||||
private ProgressDialogStatusLogger _statusLogger = new ProgressDialogStatusLogger(); //default: no logging but not null -> can be used whenever desired
|
private ProgressDialogStatusLogger _statusLogger = new ProgressDialogStatusLogger(); //default: no logging but not null -> can be used whenever desired
|
||||||
private Activity _activeActivity;
|
private Activity _activeActivity;
|
||||||
@@ -77,20 +84,22 @@ namespace keepass2android
|
|||||||
Handler = null;
|
Handler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetResult(bool success, string message, Exception exception) {
|
public void SetResult(bool success, string message, bool importantMessage, Exception exception) {
|
||||||
Success = success;
|
Success = success;
|
||||||
Message = message;
|
Message = message;
|
||||||
|
ImportantMessage = importantMessage;
|
||||||
Exception = exception;
|
Exception = exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetResult(bool success) {
|
|
||||||
|
public void SetResult(bool success) {
|
||||||
Success = success;
|
Success = success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Run() {
|
public virtual void Run() {
|
||||||
if (BaseOnFinish == null) return;
|
if (BaseOnFinish == null) return;
|
||||||
// Pass on result on call finish
|
// Pass on result on call finish
|
||||||
BaseOnFinish.SetResult(Success, Message, Exception);
|
BaseOnFinish.SetResult(Success, Message, ImportantMessage, Exception);
|
||||||
|
|
||||||
if ( Handler != null ) {
|
if ( Handler != null ) {
|
||||||
Handler.Post(BaseOnFinish.Run);
|
Handler.Post(BaseOnFinish.Run);
|
||||||
@@ -100,14 +109,31 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void DisplayMessage(Context ctx) {
|
protected void DisplayMessage(Context ctx) {
|
||||||
DisplayMessage(ctx, Message);
|
DisplayMessage(ctx, Message, ImportantMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DisplayMessage(Context ctx, string message)
|
public static void DisplayMessage(Context ctx, string message, bool makeDialog)
|
||||||
{
|
{
|
||||||
if ( !String.IsNullOrEmpty(message) ) {
|
if ( !String.IsNullOrEmpty(message) ) {
|
||||||
Kp2aLog.Log("OnFinish message: "+message);
|
Kp2aLog.Log("OnFinish message: " + message);
|
||||||
Toast.MakeText(ctx ?? Application.Context, message, ToastLength.Long).Show();
|
if (makeDialog && ctx != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||||
|
|
||||||
|
builder.SetMessage(message)
|
||||||
|
.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => ((Dialog)sender).Dismiss())
|
||||||
|
.Show();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
Toast.MakeText(ctx, message, ToastLength.Long).Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Toast.MakeText(ctx ?? Application.Context, message, ToastLength.Long).Show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Finish(bool result, String message, Exception exception = null) {
|
protected void Finish(bool result, String message, bool importantMessage = false, Exception exception = null) {
|
||||||
if ( OnFinishToRun != null ) {
|
if ( OnFinishToRun != null ) {
|
||||||
OnFinishToRun.SetResult(result, message, exception);
|
OnFinishToRun.SetResult(result, message, importantMessage, exception);
|
||||||
OnFinishToRun.Run();
|
OnFinishToRun.Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ namespace keepass2android
|
|||||||
|
|
||||||
public class SaveDb : RunnableOnFinish {
|
public class SaveDb : RunnableOnFinish {
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private readonly bool _dontSave;
|
private readonly Database _db;
|
||||||
|
private readonly bool _dontSave;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// stream for reading the data from the original file. If this is set to a non-null value, we know we need to sync
|
/// stream for reading the data from the original file. If this is set to a non-null value, we know we need to sync
|
||||||
@@ -43,9 +44,10 @@ namespace keepass2android
|
|||||||
private readonly Context _ctx;
|
private readonly Context _ctx;
|
||||||
private Thread _workerThread;
|
private Thread _workerThread;
|
||||||
|
|
||||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, bool dontSave)
|
public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish, bool dontSave)
|
||||||
: base(ctx, finish)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
|
_db = db;
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
_app = app;
|
_app = app;
|
||||||
_dontSave = dontSave;
|
_dontSave = dontSave;
|
||||||
@@ -59,46 +61,55 @@ namespace keepass2android
|
|||||||
/// <param name="finish"></param>
|
/// <param name="finish"></param>
|
||||||
/// <param name="dontSave"></param>
|
/// <param name="dontSave"></param>
|
||||||
/// <param name="streamForOrigFile">Stream for reading the data from the (changed) original location</param>
|
/// <param name="streamForOrigFile">Stream for reading the data from the (changed) original location</param>
|
||||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, bool dontSave, Stream streamForOrigFile)
|
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, Database db, bool dontSave, Stream streamForOrigFile)
|
||||||
: base(ctx, finish)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
|
_db = db;
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
_app = app;
|
_app = app;
|
||||||
_dontSave = dontSave;
|
_dontSave = dontSave;
|
||||||
_streamForOrigFile = streamForOrigFile;
|
_streamForOrigFile = streamForOrigFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish)
|
public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish)
|
||||||
: base(ctx, finish)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
_app = app;
|
_app = app;
|
||||||
_dontSave = false;
|
_db = db;
|
||||||
|
_dontSave = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ShowDatabaseIocInStatus { get; set; }
|
||||||
public override void Run ()
|
|
||||||
|
public override void Run ()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!_dontSave)
|
if (!_dontSave)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_app.GetDb().CanWrite == false)
|
if (_db.CanWrite == false)
|
||||||
{
|
{
|
||||||
//this should only happen if there is a problem in the UI so that the user sees an edit interface.
|
//this should only happen if there is a problem in the UI so that the user sees an edit interface.
|
||||||
Finish(false,"Cannot save changes. File is read-only!");
|
Finish(false,"Cannot save changes. File is read-only!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusLogger.UpdateMessage(UiStringKey.saving_database);
|
string message = _app.GetResourceString(UiStringKey.saving_database);
|
||||||
IOConnectionInfo ioc = _app.GetDb().Ioc;
|
|
||||||
|
if (ShowDatabaseIocInStatus)
|
||||||
|
message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
|
||||||
|
|
||||||
|
StatusLogger.UpdateMessage(message);
|
||||||
|
|
||||||
|
IOConnectionInfo ioc = _db.Ioc;
|
||||||
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
||||||
|
|
||||||
if (_streamForOrigFile == null)
|
if (_streamForOrigFile == null)
|
||||||
{
|
{
|
||||||
if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave))
|
if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave))
|
||||||
|| (_app.GetDb().KpDatabase.HashOfFileOnDisk == null)) //first time saving
|
|| (_db.KpDatabase.HashOfFileOnDisk == null)) //first time saving
|
||||||
{
|
{
|
||||||
PerformSaveWithoutCheck(fileStorage, ioc);
|
PerformSaveWithoutCheck(fileStorage, ioc);
|
||||||
Finish(true);
|
Finish(true);
|
||||||
@@ -109,8 +120,8 @@ namespace keepass2android
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
(_streamForOrigFile != null)
|
(_streamForOrigFile != null)
|
||||||
|| fileStorage.CheckForFileChangeFast(ioc, _app.GetDb().LastFileVersion) //first try to use the fast change detection
|
|| fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion) //first try to use the fast change detection
|
||||||
|| (FileHashChanged(ioc, _app.GetDb().KpDatabase.HashOfFileOnDisk) == FileHashChange.Changed) //if that fails, hash the file and compare:
|
|| (FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) == FileHashChange.Changed) //if that fails, hash the file and compare:
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -128,6 +139,7 @@ namespace keepass2android
|
|||||||
//small.
|
//small.
|
||||||
MergeIn(fileStorage, ioc);
|
MergeIn(fileStorage, ioc);
|
||||||
PerformSaveWithoutCheck(fileStorage, ioc);
|
PerformSaveWithoutCheck(fileStorage, ioc);
|
||||||
|
_db.UpdateGlobals();
|
||||||
Finish(true);
|
Finish(true);
|
||||||
};
|
};
|
||||||
RunInWorkerThread(runHandler);
|
RunInWorkerThread(runHandler);
|
||||||
@@ -217,13 +229,13 @@ namespace keepass2android
|
|||||||
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.SynchronizingDatabase));
|
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.SynchronizingDatabase));
|
||||||
|
|
||||||
PwDatabase pwImp = new PwDatabase();
|
PwDatabase pwImp = new PwDatabase();
|
||||||
PwDatabase pwDatabase = _app.GetDb().KpDatabase;
|
PwDatabase pwDatabase = _db.KpDatabase;
|
||||||
pwImp.New(new IOConnectionInfo(), pwDatabase.MasterKey);
|
pwImp.New(new IOConnectionInfo(), pwDatabase.MasterKey);
|
||||||
pwImp.MemoryProtection = pwDatabase.MemoryProtection.CloneDeep();
|
pwImp.MemoryProtection = pwDatabase.MemoryProtection.CloneDeep();
|
||||||
pwImp.MasterKey = pwDatabase.MasterKey;
|
pwImp.MasterKey = pwDatabase.MasterKey;
|
||||||
var stream = GetStreamForBaseFile(fileStorage, ioc);
|
var stream = GetStreamForBaseFile(fileStorage, ioc);
|
||||||
|
|
||||||
_app.GetDb().DatabaseFormat.PopulateDatabaseFromStream(pwImp, stream, null);
|
_db.DatabaseFormat.PopulateDatabaseFromStream(pwImp, stream, null);
|
||||||
|
|
||||||
|
|
||||||
pwDatabase.MergeIn(pwImp, PwMergeMethod.Synchronize, null);
|
pwDatabase.MergeIn(pwImp, PwMergeMethod.Synchronize, null);
|
||||||
@@ -249,8 +261,8 @@ namespace keepass2android
|
|||||||
private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
|
private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
StatusLogger.UpdateSubMessage("");
|
StatusLogger.UpdateSubMessage("");
|
||||||
_app.GetDb().SaveData();
|
_db.SaveData();
|
||||||
_app.GetDb().LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
|
_db.LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] HashOriginalFile(IOConnectionInfo iocFile)
|
public byte[] HashOriginalFile(IOConnectionInfo iocFile)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace keepass2android
|
|||||||
public override void Run ()
|
public override void Run ()
|
||||||
{
|
{
|
||||||
StatusLogger.UpdateMessage(UiStringKey.SettingPassword);
|
StatusLogger.UpdateMessage(UiStringKey.SettingPassword);
|
||||||
PwDatabase pm = _app.GetDb().KpDatabase;
|
PwDatabase pm = _app.CurrentDb.KpDatabase;
|
||||||
CompositeKey newKey = new CompositeKey ();
|
CompositeKey newKey = new CompositeKey ();
|
||||||
if (String.IsNullOrEmpty (_password) == false) {
|
if (String.IsNullOrEmpty (_password) == false) {
|
||||||
newKey.AddUserKey (new KcpPassword (_password));
|
newKey.AddUserKey (new KcpPassword (_password));
|
||||||
@@ -74,7 +74,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
// Save Database
|
// Save Database
|
||||||
_onFinishToRun = new AfterSave(ActiveActivity, previousKey, previousMasterKeyChanged, pm, OnFinishToRun);
|
_onFinishToRun = new AfterSave(ActiveActivity, previousKey, previousMasterKeyChanged, pm, OnFinishToRun);
|
||||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, _dontSave);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace keepass2android
|
|||||||
|
|
||||||
public override void Run() {
|
public override void Run() {
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ namespace keepass2android
|
|||||||
if ( parent != null ) {
|
if ( parent != null ) {
|
||||||
|
|
||||||
// Mark parent group dirty
|
// Mark parent group dirty
|
||||||
_app.GetDb().Dirty.Add(parent);
|
_app.DirtyGroups.Add(parent);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,5 +67,4 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="libs\" />
|
<Folder Include="libs\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
</Project>
|
|
||||||
@@ -53,8 +53,8 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<LibraryProjectZip Include="..\java\Keepass2AndroidPluginSDK2\app\build\outputs\aar\app-release.aar">
|
<LibraryProjectZip Include="..\java\Keepass2AndroidPluginSDK2\app\build\outputs\aar\Keepass2AndroidPluginSDK2-release.aar">
|
||||||
<Link>Jars\app-release.aar</Link>
|
<Link>Jars\Keepass2AndroidPluginSDK2-release.aar</Link>
|
||||||
</LibraryProjectZip>
|
</LibraryProjectZip>
|
||||||
<None Include="Jars\AboutJars.txt" />
|
<None Include="Jars\AboutJars.txt" />
|
||||||
<None Include="Additions\AboutAdditions.txt" />
|
<None Include="Additions\AboutAdditions.txt" />
|
||||||
|
|||||||
@@ -7,4 +7,7 @@ call gradlew assemble
|
|||||||
cd ..\Keepass2AndroidPluginSDK2
|
cd ..\Keepass2AndroidPluginSDK2
|
||||||
call gradlew assemble
|
call gradlew assemble
|
||||||
|
|
||||||
cd ..\..\build-scripts
|
cd ..\PluginQR
|
||||||
|
call gradlew assemble
|
||||||
|
|
||||||
|
cd ..\..\build-scripts
|
||||||
|
|||||||
@@ -15,4 +15,8 @@ pushd Keepass2AndroidPluginSDK2
|
|||||||
./gradlew assemble
|
./gradlew assemble
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
pushd PluginQR
|
||||||
|
./gradlew assemble
|
||||||
|
popd
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ dependencies {
|
|||||||
}
|
}
|
||||||
compile 'com.pcloud.sdk:java-core:1.0.1'
|
compile 'com.pcloud.sdk:java-core:1.0.1'
|
||||||
compile 'com.pcloud.sdk:android:1.0.1'
|
compile 'com.pcloud.sdk:android:1.0.1'
|
||||||
|
compile('com.microsoft.graph:msgraph-sdk-android:1.2.+')
|
||||||
|
compile ('com.microsoft.identity.client:msal:0.1.+') {
|
||||||
|
exclude group: 'com.android.support', module: 'appcompat-v7'
|
||||||
|
}
|
||||||
compile 'com.google.code.gson:gson:2.3.1'
|
compile 'com.google.code.gson:gson:2.3.1'
|
||||||
compile 'com.microsoft.services.msa:msa-auth:0.8.6'
|
compile 'com.microsoft.services.msa:msa-auth:0.8.6'
|
||||||
compile 'com.microsoft.aad:adal:1.14.0'
|
compile 'com.microsoft.aad:adal:1.14.0'
|
||||||
|
|||||||
@@ -190,12 +190,14 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
|
|||||||
String part = parts[parts.length-1];
|
String part = parts[parts.length-1];
|
||||||
logDebug("parsing part " + part);
|
logDebug("parsing part " + part);
|
||||||
int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP);
|
int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP);
|
||||||
|
String name = "";
|
||||||
if (indexOfSeparator < 0)
|
if (indexOfSeparator < 0)
|
||||||
{
|
{
|
||||||
//seems invalid, but we're very generous here
|
//seems invalid, but we're very generous here
|
||||||
displayName += "/"+part;
|
displayName += "/"+part;
|
||||||
}
|
}
|
||||||
String name = part.substring(0, indexOfSeparator);
|
else
|
||||||
|
name = part.substring(0, indexOfSeparator);
|
||||||
try {
|
try {
|
||||||
name = decode(name);
|
name = decode(name);
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
|||||||
@@ -0,0 +1,486 @@
|
|||||||
|
package keepass2android.javafilestorage;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
||||||
|
import com.microsoft.graph.core.ClientException;
|
||||||
|
import com.microsoft.graph.core.DefaultClientConfig;
|
||||||
|
import com.microsoft.graph.core.GraphErrorCodes;
|
||||||
|
import com.microsoft.graph.extensions.DriveItem;
|
||||||
|
import com.microsoft.graph.extensions.GraphServiceClient;
|
||||||
|
import com.microsoft.graph.extensions.IDriveItemCollectionPage;
|
||||||
|
import com.microsoft.graph.extensions.IDriveItemCollectionRequestBuilder;
|
||||||
|
import com.microsoft.graph.extensions.IDriveItemRequest;
|
||||||
|
import com.microsoft.graph.extensions.IDriveItemRequestBuilder;
|
||||||
|
import com.microsoft.graph.extensions.IGraphServiceClient;
|
||||||
|
import com.microsoft.identity.client.AuthenticationCallback;
|
||||||
|
import com.microsoft.identity.client.AuthenticationResult;
|
||||||
|
import com.microsoft.identity.client.MsalException;
|
||||||
|
import com.microsoft.identity.client.PublicClientApplication;
|
||||||
|
import com.microsoft.identity.client.User;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import keepass2android.javafilestorage.onedrive2.GraphServiceClientManager;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Philipp on 20.11.2016.
|
||||||
|
*/
|
||||||
|
public class OneDriveStorage2 extends JavaFileStorageBase
|
||||||
|
{
|
||||||
|
PublicClientApplication mPublicClientApp;
|
||||||
|
|
||||||
|
final HashMap<String /*userid*/, IGraphServiceClient> mClientByUser = new HashMap<String /*userid*/, IGraphServiceClient>();
|
||||||
|
|
||||||
|
private static final String[] scopes = {"openid","offline_access", "https://graph.microsoft.com/Files.ReadWrite","https://graph.microsoft.com/User.Read"};
|
||||||
|
|
||||||
|
|
||||||
|
public OneDriveStorage2(final Activity context, final String clientId) {
|
||||||
|
|
||||||
|
mPublicClientApp = new PublicClientApplication(context, clientId);
|
||||||
|
initAuthenticator(context);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresSetup(String path)
|
||||||
|
{
|
||||||
|
|
||||||
|
return !isConnected(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, int requestCode) {
|
||||||
|
|
||||||
|
initAuthenticator((Activity)activity.getActivity());
|
||||||
|
|
||||||
|
String path = getProtocolId()+":///";
|
||||||
|
Log.d("KP2AJ", "startSelectFile "+path+", connected: "+path);
|
||||||
|
if (isConnected(null))
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(EXTRA_IS_FOR_SAVE, isForSave);
|
||||||
|
intent.putExtra(EXTRA_PATH, path);
|
||||||
|
activity.onImmediateResult(requestCode, RESULT_FILECHOOSER_PREPARED, intent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
activity.startSelectFileProcess(path, isForSave, requestCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConnected(String path) {
|
||||||
|
try {
|
||||||
|
if (tryGetMsGraphClient(path) == null)
|
||||||
|
try {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
Log.d("KP2AJ", "trying silent login");
|
||||||
|
|
||||||
|
String userId = extractUserId(path);
|
||||||
|
final MsalException[] _exception = {null};
|
||||||
|
final AuthenticationResult[] _result = {null};
|
||||||
|
User user = mPublicClientApp.getUser(userId);
|
||||||
|
mPublicClientApp.acquireTokenSilentAsync(scopes, user,
|
||||||
|
new AuthenticationCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(AuthenticationResult authenticationResult) {
|
||||||
|
_result[0] = authenticationResult;
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(MsalException exception) {
|
||||||
|
_exception[0] = exception;
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancel() {
|
||||||
|
latch.countDown();
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
latch.await();
|
||||||
|
if (_result[0] != null) {
|
||||||
|
buildClient(_result[0]);
|
||||||
|
} else if (_exception[0] != null){
|
||||||
|
_exception[0].printStackTrace();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return tryGetMsGraphClient(path) != null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private IGraphServiceClient tryGetMsGraphClient(String path) throws Exception
|
||||||
|
{
|
||||||
|
String userId = extractUserId(path);
|
||||||
|
if (mClientByUser.containsKey(userId))
|
||||||
|
return mClientByUser.get(userId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractUserId(String path) throws Exception {
|
||||||
|
String pathWithoutProtocol = removeProtocol(path);
|
||||||
|
String[] parts = pathWithoutProtocol.split("/",1);
|
||||||
|
if (parts.length != 2 || ("".equals(parts[0])))
|
||||||
|
{
|
||||||
|
throw new Exception("path does not contain user");
|
||||||
|
}
|
||||||
|
return parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAuthenticator(Activity activity) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) {
|
||||||
|
initAuthenticator((Activity)activity.getActivity());
|
||||||
|
if (isConnected(path))
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(EXTRA_PATH, path);
|
||||||
|
activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
activity.startFileUsageProcess(path, requestCode, alwaysReturnSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProtocolId() {
|
||||||
|
return "onedrive";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException {
|
||||||
|
if (!isConnected(null))
|
||||||
|
{
|
||||||
|
throw new UserInteractionRequiredException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
Log.d("KP2AJ", "OnCreate");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume(final FileStorageSetupActivity activity) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private IGraphServiceClient buildClient(AuthenticationResult authenticationResult) throws InterruptedException {
|
||||||
|
|
||||||
|
IGraphServiceClient newClient = new GraphServiceClient.Builder()
|
||||||
|
.fromConfig(DefaultClientConfig.createWithAuthenticationProvider(new GraphServiceClientManager(authenticationResult.getAccessToken())))
|
||||||
|
.buildClient();
|
||||||
|
mClientByUser.put(authenticationResult.getUser().getUserIdentifier(), newClient);
|
||||||
|
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String removeProtocol(String path) throws Exception {
|
||||||
|
if (path == null)
|
||||||
|
return null;
|
||||||
|
return path.substring(getProtocolId().length()+3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName(String path) {
|
||||||
|
|
||||||
|
if (path == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilename(String path) throws Exception {
|
||||||
|
return path.substring(path.lastIndexOf("/")+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentFileVersionFast(String path) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientAndPath
|
||||||
|
{
|
||||||
|
public IGraphServiceClient client;
|
||||||
|
public String oneDrivePath;
|
||||||
|
public IDriveItemRequestBuilder getPathItem()
|
||||||
|
{
|
||||||
|
IDriveItemRequestBuilder pathItem = client.getDrive().getRoot();
|
||||||
|
if ("".equals(oneDrivePath) == false) {
|
||||||
|
pathItem = pathItem.getItemWithPath(oneDrivePath);
|
||||||
|
}
|
||||||
|
return pathItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream openFileForRead(String path) throws Exception {
|
||||||
|
try {
|
||||||
|
ClientAndPath clientAndpath = getOneDriveClientAndPath(path);
|
||||||
|
logDebug("openFileForRead. Path="+path);
|
||||||
|
InputStream result = clientAndpath.client.getDrive()
|
||||||
|
.getRoot()
|
||||||
|
.getItemWithPath(clientAndpath.oneDrivePath)
|
||||||
|
.getContent()
|
||||||
|
.buildRequest()
|
||||||
|
.get();
|
||||||
|
logDebug("ok");
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ClientException e)
|
||||||
|
{
|
||||||
|
throw convertException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientAndPath getOneDriveClientAndPath(String path) throws Exception {
|
||||||
|
ClientAndPath result = new ClientAndPath();
|
||||||
|
|
||||||
|
String pathWithoutProtocol = removeProtocol(path);
|
||||||
|
String[] parts = pathWithoutProtocol.split("/",2);
|
||||||
|
if (parts.length != 2 || ("".equals(parts[0])))
|
||||||
|
{
|
||||||
|
throw new Exception("path does not contain user");
|
||||||
|
}
|
||||||
|
result.client = mClientByUser.get(parts[0]);
|
||||||
|
result.oneDrivePath = parts[1];
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Exception convertException(ClientException e) {
|
||||||
|
if (e.isError(GraphErrorCodes.ItemNotFound))
|
||||||
|
return new FileNotFoundException(e.getMessage());
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception {
|
||||||
|
try {
|
||||||
|
ClientAndPath clientAndPath = getOneDriveClientAndPath(path);
|
||||||
|
clientAndPath.client.getDrive()
|
||||||
|
.getRoot()
|
||||||
|
.getItemWithPath(clientAndPath.oneDrivePath)
|
||||||
|
.getContent()
|
||||||
|
.buildRequest()
|
||||||
|
.put(data);
|
||||||
|
} catch (ClientException e) {
|
||||||
|
throw convertException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createFolder(String parentPath, String newDirName) throws Exception {
|
||||||
|
throw new Exception("not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createFilePath(String parentPath, String newFileName) throws Exception {
|
||||||
|
String path = parentPath;
|
||||||
|
if (!path.endsWith("/"))
|
||||||
|
path = path + "/";
|
||||||
|
path = path + newFileName;
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FileEntry> listFiles(String parentPath) throws Exception {
|
||||||
|
try {
|
||||||
|
ArrayList<FileEntry> result = new ArrayList<FileEntry>();
|
||||||
|
ClientAndPath clientAndPath = getOneDriveClientAndPath(parentPath);
|
||||||
|
parentPath = clientAndPath.oneDrivePath;
|
||||||
|
|
||||||
|
IDriveItemCollectionPage itemsPage = clientAndPath.getPathItem()
|
||||||
|
.getChildren()
|
||||||
|
.buildRequest()
|
||||||
|
.get();
|
||||||
|
if (parentPath.endsWith("/"))
|
||||||
|
parentPath = parentPath.substring(0,parentPath.length()-1);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
List<DriveItem> items = itemsPage.getCurrentPage();
|
||||||
|
if (items.isEmpty())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
for (DriveItem i: items)
|
||||||
|
{
|
||||||
|
FileEntry e = getFileEntry(parentPath + "/" + i.name, i);
|
||||||
|
Log.d("KP2AJ", e.path);
|
||||||
|
result.add(e);
|
||||||
|
}
|
||||||
|
IDriveItemCollectionRequestBuilder nextPageReqBuilder = itemsPage.getNextPage();
|
||||||
|
if (nextPageReqBuilder == null)
|
||||||
|
return result;
|
||||||
|
itemsPage = nextPageReqBuilder.buildRequest().get();
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (ClientException e) {
|
||||||
|
throw convertException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileEntry getFileEntry(String path, DriveItem i) {
|
||||||
|
FileEntry e = new FileEntry();
|
||||||
|
if (i.size != null)
|
||||||
|
e.sizeInBytes = i.size;
|
||||||
|
else if ((i.remoteItem != null) && (i.remoteItem.size != null))
|
||||||
|
e.sizeInBytes = i.remoteItem.size;
|
||||||
|
|
||||||
|
e.displayName = i.name;
|
||||||
|
e.canRead = e.canWrite = true;
|
||||||
|
e.path = getProtocolId() +"://"+path;
|
||||||
|
if (i.lastModifiedDateTime != null)
|
||||||
|
e.lastModifiedTime = i.lastModifiedDateTime.getTimeInMillis();
|
||||||
|
else if ((i.remoteItem != null)&&(i.remoteItem.lastModifiedDateTime != null))
|
||||||
|
e.lastModifiedTime = i.remoteItem.lastModifiedDateTime.getTimeInMillis();
|
||||||
|
e.isDirectory = (i.folder != null) || ((i.remoteItem != null) && (i.remoteItem.folder != null));
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileEntry getFileEntry(String filename) throws Exception {
|
||||||
|
try {
|
||||||
|
|
||||||
|
ClientAndPath clientAndPath = getOneDriveClientAndPath(filename);
|
||||||
|
IDriveItemRequestBuilder pathItem = clientAndPath.getPathItem();
|
||||||
|
|
||||||
|
IDriveItemRequest request = pathItem.buildRequest();
|
||||||
|
DriveItem item = request.get();
|
||||||
|
return getFileEntry(filename, item);
|
||||||
|
} catch (ClientException e) {
|
||||||
|
throw convertException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String path) throws Exception {
|
||||||
|
try {
|
||||||
|
ClientAndPath clientAndPath = getOneDriveClientAndPath(path);
|
||||||
|
clientAndPath.client.getDrive()
|
||||||
|
.getRoot()
|
||||||
|
.getItemWithPath(clientAndPath.oneDrivePath)
|
||||||
|
.buildRequest()
|
||||||
|
.delete();
|
||||||
|
} catch (ClientException e) {
|
||||||
|
throw convertException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean acquireTokenRunning = false;
|
||||||
|
@Override
|
||||||
|
public void onStart(final FileStorageSetupActivity activity) {
|
||||||
|
Log.d("KP2AJ", "onStart " + activity.getPath());
|
||||||
|
if (activity.getProcessName().equals(PROCESS_NAME_SELECTFILE))
|
||||||
|
activity.getState().putString(EXTRA_PATH, activity.getPath());
|
||||||
|
|
||||||
|
String userId = activity.getState().getString("OneDriveUser");
|
||||||
|
if (mClientByUser.containsKey(userId)) {
|
||||||
|
finishActivityWithSuccess(activity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JavaFileStorage.FileStorageSetupActivity storageSetupAct = activity;
|
||||||
|
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AuthenticationResult[] _authenticationResult = {null};
|
||||||
|
MsalException _exception[] = {null};
|
||||||
|
|
||||||
|
if (!acquireTokenRunning) {
|
||||||
|
acquireTokenRunning = true;
|
||||||
|
|
||||||
|
mPublicClientApp.acquireToken((Activity) activity, scopes, new AuthenticationCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(AuthenticationResult authenticationResult) {
|
||||||
|
Log.i(TAG, "authenticating successful");
|
||||||
|
|
||||||
|
try {
|
||||||
|
buildClient(authenticationResult);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
activity.getState().putString(EXTRA_PATH, getProtocolPrefix() + authenticationResult.getUser().getUserIdentifier() + "/");
|
||||||
|
|
||||||
|
finishActivityWithSuccess(activity);
|
||||||
|
acquireTokenRunning = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(MsalException exception) {
|
||||||
|
Log.i(TAG, "authenticating not successful");
|
||||||
|
Intent data = new Intent();
|
||||||
|
data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not successful");
|
||||||
|
((Activity) activity).setResult(Activity.RESULT_CANCELED, data);
|
||||||
|
((Activity) activity).finish();
|
||||||
|
acquireTokenRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancel() {
|
||||||
|
|
||||||
|
Log.i(TAG, "authenticating cancelled");
|
||||||
|
Intent data = new Intent();
|
||||||
|
data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not cancelled");
|
||||||
|
((Activity) activity).setResult(Activity.RESULT_CANCELED, data);
|
||||||
|
((Activity) activity).finish();
|
||||||
|
acquireTokenRunning = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) {
|
||||||
|
mPublicClientApp.handleInteractiveRequestRedirect(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import com.pcloud.sdk.ApiClient;
|
import com.pcloud.sdk.ApiClient;
|
||||||
@@ -52,7 +53,7 @@ public class PCloudFileStorage extends JavaFileStorageBase
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean requiresSetup(String path) {
|
public boolean requiresSetup(String path) {
|
||||||
return true;
|
return !this.isConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -135,8 +136,10 @@ public class PCloudFileStorage extends JavaFileStorageBase
|
|||||||
String filePath = path.substring(0, path.lastIndexOf("/") + 1);
|
String filePath = path.substring(0, path.lastIndexOf("/") + 1);
|
||||||
RemoteFolder remoteFolder = this.getRemoteFolderByPath(filePath);
|
RemoteFolder remoteFolder = this.getRemoteFolderByPath(filePath);
|
||||||
|
|
||||||
|
String tempName = "." + UUID.randomUUID().toString();
|
||||||
try {
|
try {
|
||||||
this.apiClient.createFile(remoteFolder, filename, dataSource).execute();
|
RemoteFile remoteFile = this.apiClient.createFile(remoteFolder, tempName, dataSource).execute();
|
||||||
|
this.apiClient.rename(remoteFile, filename).execute();
|
||||||
} catch (ApiError e) {
|
} catch (ApiError e) {
|
||||||
throw convertApiError(e);
|
throw convertApiError(e);
|
||||||
}
|
}
|
||||||
@@ -372,7 +375,7 @@ public class PCloudFileStorage extends JavaFileStorageBase
|
|||||||
|
|
||||||
private Exception convertApiError(ApiError e) {
|
private Exception convertApiError(ApiError e) {
|
||||||
String strErrorCode = String.valueOf(e.errorCode());
|
String strErrorCode = String.valueOf(e.errorCode());
|
||||||
if (strErrorCode.startsWith("1") || "2000".equals(strErrorCode)) {
|
if (strErrorCode.startsWith("1") || "2000".equals(strErrorCode) || "2095".equals(strErrorCode)) {
|
||||||
this.clearAuthToken();
|
this.clearAuthToken();
|
||||||
return new UserInteractionRequiredException("Unlinked from PCloud! User must re-link.", e);
|
return new UserInteractionRequiredException("Unlinked from PCloud! User must re-link.", e);
|
||||||
} else if (strErrorCode.startsWith("2")) {
|
} else if (strErrorCode.startsWith("2")) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.jcraft.jsch.UserInfo;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
public class SftpStorage extends JavaFileStorageBase {
|
public class SftpStorage extends JavaFileStorageBase {
|
||||||
|
|
||||||
@@ -358,12 +357,10 @@ public class SftpStorage extends JavaFileStorageBase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private String getBaseDir() {
|
private String getBaseDir() {
|
||||||
return _appContext.getFilesDir().getAbsolutePath();
|
return _appContext.getFilesDir().getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private String getKeyFileName() {
|
private String getKeyFileName() {
|
||||||
return getBaseDir() + "/id_kp2a_rsa";
|
return getBaseDir() + "/id_kp2a_rsa";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.util.Date;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
@@ -70,9 +71,18 @@ public class WebDavStorage extends JavaFileStorageBase {
|
|||||||
|
|
||||||
String scheme = filename.substring(0, filename.indexOf("://"));
|
String scheme = filename.substring(0, filename.indexOf("://"));
|
||||||
filename = filename.substring(scheme.length() + 3);
|
filename = filename.substring(scheme.length() + 3);
|
||||||
String userPwd = filename.substring(0, filename.indexOf('@'));
|
int idxAt = filename.indexOf('@');
|
||||||
ci.username = decode(userPwd.substring(0, userPwd.indexOf(":")));
|
if (idxAt >= 0)
|
||||||
ci.password = decode(userPwd.substring(userPwd.indexOf(":") + 1));
|
{
|
||||||
|
String userPwd = filename.substring(0, idxAt);
|
||||||
|
int idxColon = userPwd.indexOf(":");
|
||||||
|
if (idxColon >= 0);
|
||||||
|
{
|
||||||
|
ci.username = decode(userPwd.substring(0, idxColon));
|
||||||
|
ci.password = decode(userPwd.substring(idxColon + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ci.URL = scheme + "://" +filename.substring(filename.indexOf('@') + 1);
|
ci.URL = scheme + "://" +filename.substring(filename.indexOf('@') + 1);
|
||||||
return ci;
|
return ci;
|
||||||
}
|
}
|
||||||
@@ -149,13 +159,19 @@ public class WebDavStorage extends JavaFileStorageBase {
|
|||||||
sslContext.init(null, new TrustManager[] { trustManager }, null);
|
sslContext.init(null, new TrustManager[] { trustManager }, null);
|
||||||
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||||
|
|
||||||
|
|
||||||
builder = builder.sslSocketFactory(sslSocketFactory, trustManager)
|
builder = builder.sslSocketFactory(sslSocketFactory, trustManager)
|
||||||
.hostnameVerifier(new DecoratedHostnameVerifier(OkHostnameVerifier.INSTANCE, mCertificateErrorHandler));
|
.hostnameVerifier(new DecoratedHostnameVerifier(OkHostnameVerifier.INSTANCE, mCertificateErrorHandler));
|
||||||
|
|
||||||
|
|
||||||
|
builder.connectTimeout(25, TimeUnit.SECONDS);
|
||||||
|
builder.readTimeout(25, TimeUnit.SECONDS);
|
||||||
|
builder.writeTimeout(25, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
OkHttpClient client = builder.build();
|
OkHttpClient client = builder.build();
|
||||||
|
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package keepass2android.javafilestorage.onedrive2;
|
||||||
|
|
||||||
|
import com.microsoft.graph.authentication.IAuthenticationProvider;
|
||||||
|
import com.microsoft.graph.core.DefaultClientConfig;
|
||||||
|
import com.microsoft.graph.core.IClientConfig;
|
||||||
|
import com.microsoft.graph.extensions.GraphServiceClient;
|
||||||
|
import com.microsoft.graph.extensions.IGraphServiceClient;
|
||||||
|
import com.microsoft.graph.http.IHttpRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton class that manages a GraphServiceClient object.
|
||||||
|
* It implements IAuthentication provider to authenticate requests using an access token.
|
||||||
|
*/
|
||||||
|
public class GraphServiceClientManager implements IAuthenticationProvider {
|
||||||
|
private IGraphServiceClient mGraphServiceClient;
|
||||||
|
private String mAccessToken;
|
||||||
|
|
||||||
|
public GraphServiceClientManager(String accessToken) {
|
||||||
|
mAccessToken = accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void authenticateRequest(IHttpRequest request) {
|
||||||
|
try {
|
||||||
|
request.addHeader("Authorization", "Bearer " + mAccessToken);
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized IGraphServiceClient getGraphServiceClient() {
|
||||||
|
return getGraphServiceClient(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized IGraphServiceClient getGraphServiceClient(IAuthenticationProvider authenticationProvider) {
|
||||||
|
if (mGraphServiceClient == null) {
|
||||||
|
IClientConfig clientConfig = DefaultClientConfig.createWithAuthenticationProvider(
|
||||||
|
authenticationProvider
|
||||||
|
);
|
||||||
|
mGraphServiceClient = new GraphServiceClient.Builder().fromConfig(clientConfig).buildClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mGraphServiceClient;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@ android {
|
|||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
multiDexEnabled true
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -59,6 +59,18 @@
|
|||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="com.microsoft.identity.client.BrowserTabActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="@string/msalPrefix"
|
||||||
|
android:host="auth" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
||||||
@@ -68,6 +80,8 @@
|
|||||||
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
|
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -89,6 +89,8 @@ extends Activity implements JavaFileStorage.FileStorageSetupActivity {
|
|||||||
@Override
|
@Override
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
if (getState().containsKey(JavaFileStorage.EXTRA_PATH))
|
||||||
|
return getState().getString(JavaFileStorage.EXTRA_PATH);
|
||||||
return getIntent().getStringExtra(JavaFileStorage.EXTRA_PATH);
|
return getIntent().getStringExtra(JavaFileStorage.EXTRA_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,6 @@ package com.crocoapps.javafilestoragetest;
|
|||||||
import group.pals.android.lib.ui.filechooser.FileChooserActivity;
|
import group.pals.android.lib.ui.filechooser.FileChooserActivity;
|
||||||
import group.pals.android.lib.ui.filechooser.providers.BaseFileProviderUtils;
|
import group.pals.android.lib.ui.filechooser.providers.BaseFileProviderUtils;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -146,11 +145,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
//import keepass2android.javafilestorage.DropboxCloudRailStorage;
|
//import keepass2android.javafilestorage.DropboxCloudRailStorage;
|
||||||
import keepass2android.javafilestorage.DropboxV2Storage;
|
|
||||||
import keepass2android.javafilestorage.ICertificateErrorHandler;
|
|
||||||
import keepass2android.javafilestorage.JavaFileStorage;
|
import keepass2android.javafilestorage.JavaFileStorage;
|
||||||
import keepass2android.javafilestorage.JavaFileStorage.FileEntry;
|
import keepass2android.javafilestorage.JavaFileStorage.FileEntry;
|
||||||
import keepass2android.javafilestorage.OneDriveStorage;
|
import keepass2android.javafilestorage.OneDriveStorage2;
|
||||||
import keepass2android.javafilestorage.SftpStorage;
|
import keepass2android.javafilestorage.SftpStorage;
|
||||||
import keepass2android.javafilestorage.UserInteractionRequiredException;
|
import keepass2android.javafilestorage.UserInteractionRequiredException;
|
||||||
import keepass2android.javafilestorage.WebDavStorage;
|
import keepass2android.javafilestorage.WebDavStorage;
|
||||||
@@ -206,10 +203,17 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
Log.d("KP2AJ",e.toString());
|
||||||
//if exception because folder exists
|
//if exception because folder exists
|
||||||
path = fs.createFilePath(parentPath, testPath);
|
path = fs.createFilePath(parentPath, testPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String textToUpload2 = "abcdefg";
|
||||||
|
String filename2 = fs.createFilePath(parentPath, "file.txt");
|
||||||
|
/*if (!path.endsWith("/"))
|
||||||
|
path += "/";
|
||||||
|
String filename = path+"file.text";*/
|
||||||
|
fs.uploadFile(filename2,textToUpload2.getBytes(),true);
|
||||||
|
|
||||||
FileEntry e1 = fs.getFileEntry(parentPath);
|
FileEntry e1 = fs.getFileEntry(parentPath);
|
||||||
FileEntry e2 = fs.getFileEntry(path);
|
FileEntry e2 = fs.getFileEntry(path);
|
||||||
@@ -531,9 +535,11 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
|||||||
}
|
}
|
||||||
|
|
||||||
static JavaFileStorage createStorageToTest(Context ctx, Context appContext, boolean simulateRestart) {
|
static JavaFileStorage createStorageToTest(Context ctx, Context appContext, boolean simulateRestart) {
|
||||||
storageToTest = new SftpStorage(ctx.getApplicationContext());
|
//storageToTest = new SftpStorage(ctx.getApplicationContext());
|
||||||
//storageToTest = new SkyDriveFileStorage("000000004010C234", appContext);
|
//storageToTest = new SkyDriveFileStorage("000000004010C234", appContext);
|
||||||
//storageToTest = new OneDriveStorage(appContext, "000000004010C234");
|
storageToTest = new OneDriveStorage2((Activity) ctx, "8374f801-0f55-407d-80cc-9a04fe86d9b2");
|
||||||
|
|
||||||
|
|
||||||
//storageToTest = new GoogleDriveFileStorage();
|
//storageToTest = new GoogleDriveFileStorage();
|
||||||
/*storageToTest = new WebDavStorage(new ICertificateErrorHandler() {
|
/*storageToTest = new WebDavStorage(new ICertificateErrorHandler() {
|
||||||
@Override
|
@Override
|
||||||
@@ -620,7 +626,7 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
|||||||
Toast.makeText(this, "requestCode: "+requestCode, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "requestCode: "+requestCode, Toast.LENGTH_LONG).show();
|
||||||
if (requestCode == 1)
|
if (requestCode == 1)
|
||||||
//new PerformTestTask().execute(path,"TestFileStorage<67>", storageToTest); //use an umlaut to see how that works
|
//new PerformTestTask().execute(path,"TestFileStorage<67>", storageToTest); //use an umlaut to see how that works
|
||||||
new PerformTestTask().execute(path,"TestFileStorage", storageToTest);
|
new PerformTestTask().execute(path,"TestFileStorage", storageToTest);
|
||||||
else
|
else
|
||||||
if (requestCode == 2)
|
if (requestCode == 2)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="msalPrefix">msal8374f801-0f55-407d-80cc-9a04fe86d9b2</string>
|
||||||
<string name="action_settings">Settings</string>
|
<string name="action_settings">Settings</string>
|
||||||
<string name="hello_world">Hello world!</string>
|
<string name="hello_world">Hello world!</string>
|
||||||
<string name="title_activity_file_storage_setup">FileStorageSetupActivity</string>
|
<string name="title_activity_file_storage_setup">FileStorageSetupActivity</string>
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ package keepass2android.softkeyboard;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
@@ -17,9 +21,11 @@ import android.content.pm.PackageManager;
|
|||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
import android.content.res.XmlResourceParser;
|
import android.content.res.XmlResourceParser;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
//based on https://github.com/klausw/hackerskeyboard/blob/master/java/src/org/pocketworkstation/pckeyboard/PluginManager.java
|
||||||
public class PluginManager extends BroadcastReceiver {
|
public class PluginManager extends BroadcastReceiver {
|
||||||
private static String TAG = "PCKeyboard";
|
private static String TAG = "PCKeyboard";
|
||||||
private static String HK_INTENT_DICT = "org.pocketworkstation.DICT";
|
private static String HK_INTENT_DICT = "org.pocketworkstation.DICT";
|
||||||
@@ -120,6 +126,46 @@ public class PluginManager extends BroadcastReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static private class DictPluginSpecResourceSoftKeyboard
|
||||||
|
extends DictPluginSpecBase {
|
||||||
|
|
||||||
|
|
||||||
|
int[] resId;
|
||||||
|
Resources pluginRes;
|
||||||
|
|
||||||
|
public DictPluginSpecResourceSoftKeyboard(String pkg, int[] resId, Resources pluginRes) {
|
||||||
|
mPackageName = pkg;
|
||||||
|
this.resId = resId;
|
||||||
|
this.pluginRes = pluginRes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
InputStream[] getStreams(Resources res) {
|
||||||
|
final InputStream[] is = new InputStream[resId.length];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// merging separated dictionary into one if dictionary is separated
|
||||||
|
int total = 0;
|
||||||
|
for (int i = 0; i < resId.length; i++) {
|
||||||
|
|
||||||
|
// http://ponystyle.com/blog/2010/03/26/dealing-with-asset-compression-in-android-apps/
|
||||||
|
// NOTE: the resource file can not be larger than 1MB
|
||||||
|
is[i] = pluginRes.openRawResource(resId[i]);
|
||||||
|
final int dictSize = is[i].available();
|
||||||
|
Log.d(TAG, "Will load a resource dictionary id " + resId[i] + " whose size is " + dictSize + " bytes.");
|
||||||
|
total += dictSize;
|
||||||
|
}
|
||||||
|
return is;
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, "No available memory for binary dictionary: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
@@ -129,21 +175,59 @@ public class PluginManager extends BroadcastReceiver {
|
|||||||
mIME.toggleLanguage(true, true);
|
mIME.toggleLanguage(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface MemRelatedOperation {
|
||||||
|
void operation();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final int GC_TRY_LOOP_MAX = 5;
|
||||||
|
|
||||||
|
static void doGarbageCollection(final String tag) {
|
||||||
|
System.gc();
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000 /*ms*/);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.e(tag, "Sleep was interrupted.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void performOperationWithMemRetry(final String tag, MemRelatedOperation operation) {
|
||||||
|
int retryCount = GC_TRY_LOOP_MAX;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
operation.operation();
|
||||||
|
return;
|
||||||
|
} catch (OutOfMemoryError e) {
|
||||||
|
if (retryCount == 0) throw e;
|
||||||
|
|
||||||
|
retryCount--;
|
||||||
|
Log.w(tag, "WOW! No memory for operation... I'll try to release some.");
|
||||||
|
doGarbageCollection(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void getSoftKeyboardDictionaries(PackageManager packageManager) {
|
static void getSoftKeyboardDictionaries(PackageManager packageManager) {
|
||||||
Intent dictIntent = new Intent(SOFTKEYBOARD_INTENT_DICT);
|
Intent dictIntent = new Intent(SOFTKEYBOARD_INTENT_DICT);
|
||||||
List<ResolveInfo> dictPacks = packageManager.queryBroadcastReceivers(
|
List<ResolveInfo> dictPacks = packageManager.queryBroadcastReceivers(
|
||||||
dictIntent, PackageManager.GET_RECEIVERS);
|
dictIntent, PackageManager.GET_RECEIVERS);
|
||||||
for (ResolveInfo ri : dictPacks) {
|
for (ResolveInfo ri : dictPacks) {
|
||||||
ApplicationInfo appInfo = ri.activityInfo.applicationInfo;
|
ApplicationInfo appInfo = ri.activityInfo.applicationInfo;
|
||||||
String pkgName = appInfo.packageName;
|
final String pkgName = appInfo.packageName;
|
||||||
boolean success = false;
|
final boolean[] success = {false};
|
||||||
try {
|
try {
|
||||||
Resources res = packageManager.getResourcesForApplication(appInfo);
|
final Resources res = packageManager.getResourcesForApplication(appInfo);
|
||||||
Log.i("KP2AK", "Found dictionary plugin package: " + pkgName);
|
Log.i("KP2AK", "Found dictionary plugin package: " + pkgName);
|
||||||
int dictId = res.getIdentifier("dictionaries", "xml", pkgName);
|
int dictId = res.getIdentifier("dictionaries", "xml", pkgName);
|
||||||
if (dictId == 0) continue;
|
|
||||||
|
if (dictId == 0)
|
||||||
|
{
|
||||||
|
Log.i("KP2AK", "dictId == 0");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
XmlResourceParser xrp = res.getXml(dictId);
|
XmlResourceParser xrp = res.getXml(dictId);
|
||||||
|
|
||||||
|
int dictResourceId = 0;
|
||||||
String assetName = null;
|
String assetName = null;
|
||||||
String lang = null;
|
String lang = null;
|
||||||
try {
|
try {
|
||||||
@@ -157,12 +241,21 @@ public class PluginManager extends BroadcastReceiver {
|
|||||||
String convLang = SOFTKEYBOARD_LANG_MAP.get(lang);
|
String convLang = SOFTKEYBOARD_LANG_MAP.get(lang);
|
||||||
if (convLang != null) lang = convLang;
|
if (convLang != null) lang = convLang;
|
||||||
String type = xrp.getAttributeValue(null, "type");
|
String type = xrp.getAttributeValue(null, "type");
|
||||||
if (type == null || type.equals("raw") || type.equals("binary")) {
|
|
||||||
|
if (type == null || type.equals("raw") || type.equals("binary"))
|
||||||
|
{
|
||||||
assetName = xrp.getAttributeValue(null, "dictionaryAssertName"); // sic
|
assetName = xrp.getAttributeValue(null, "dictionaryAssertName"); // sic
|
||||||
} else {
|
if (assetName != null) {
|
||||||
|
Log.i(TAG, "asset=" + assetName + " lang=" + lang);
|
||||||
|
}
|
||||||
|
} else if (type.equals("binary_resource"))
|
||||||
|
{
|
||||||
|
dictResourceId = xrp.getAttributeResourceValue(null, "dictionaryResourceId",0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
Log.w(TAG, "Unsupported AnySoftKeyboard dict type " + type);
|
Log.w(TAG, "Unsupported AnySoftKeyboard dict type " + type);
|
||||||
}
|
}
|
||||||
//Log.i(TAG, "asset=" + assetName + " lang=" + lang);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,15 +268,55 @@ public class PluginManager extends BroadcastReceiver {
|
|||||||
Log.e(TAG, "Dictionary XML IOException");
|
Log.e(TAG, "Dictionary XML IOException");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (assetName == null || lang == null) continue;
|
if (lang == null)
|
||||||
DictPluginSpec spec = new DictPluginSpecSoftKeyboard(pkgName, assetName);
|
continue;
|
||||||
mPluginDicts.put(lang, spec);
|
if (assetName != null) {
|
||||||
Log.i("KP2AK", "Found plugin dictionary: lang=" + lang + ", pkg=" + pkgName);
|
DictPluginSpec spec = new DictPluginSpecSoftKeyboard(pkgName, assetName);
|
||||||
success = true;
|
mPluginDicts.put(lang, spec);
|
||||||
|
Log.i("KP2AK", "Found plugin dictionary: lang=" + lang + ", pkg=" + pkgName);
|
||||||
|
success[0] = true;
|
||||||
|
}
|
||||||
|
else if (dictResourceId != 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
Resources pkgRes = packageManager.getResourcesForApplication(appInfo);
|
||||||
|
final int[] resId;
|
||||||
|
// is it an array of dictionaries? Or a ref to raw?
|
||||||
|
final String dictResType = pkgRes.getResourceTypeName(dictResourceId);
|
||||||
|
if (dictResType.equalsIgnoreCase("raw")) {
|
||||||
|
resId = new int[]{dictResourceId};
|
||||||
|
} else {
|
||||||
|
TypedArray a = pkgRes.obtainTypedArray(dictResourceId);
|
||||||
|
resId = new int[a.length()];
|
||||||
|
for (int index = 0; index < a.length(); index++)
|
||||||
|
resId[index] = a.getResourceId(index, 0);
|
||||||
|
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
final String finalLang = lang;
|
||||||
|
performOperationWithMemRetry(TAG, new MemRelatedOperation() {
|
||||||
|
@Override
|
||||||
|
public void operation() {
|
||||||
|
// The try-catch is for issue 878:
|
||||||
|
// http://code.google.com/p/softkeyboard/issues/detail?id=878
|
||||||
|
try {
|
||||||
|
DictPluginSpec spec = new DictPluginSpecResourceSoftKeyboard(pkgName, resId, res);
|
||||||
|
mPluginDicts.put(finalLang, spec);
|
||||||
|
Log.i("KP2AK", "Found plugin dictionary: lang=" + finalLang + ", pkg=" + pkgName);
|
||||||
|
success[0] = true;
|
||||||
|
} catch (UnsatisfiedLinkError ex) {
|
||||||
|
Log.w(TAG, "Failed to load binary JNI connection! Error: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (NameNotFoundException e) {
|
} catch (NameNotFoundException e) {
|
||||||
Log.i("KP2AK", "bad");
|
Log.i("KP2AK", "bad");
|
||||||
} finally {
|
} finally {
|
||||||
if (!success) {
|
if (!success[0]) {
|
||||||
Log.i("KP2AK", "failed to load plugin dictionary spec from " + pkgName);
|
Log.i("KP2AK", "failed to load plugin dictionary spec from " + pkgName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,6 +326,7 @@ public class PluginManager extends BroadcastReceiver {
|
|||||||
static void getHKDictionaries(PackageManager packageManager) {
|
static void getHKDictionaries(PackageManager packageManager) {
|
||||||
Intent dictIntent = new Intent(HK_INTENT_DICT);
|
Intent dictIntent = new Intent(HK_INTENT_DICT);
|
||||||
List<ResolveInfo> dictPacks = packageManager.queryIntentActivities(dictIntent, 0);
|
List<ResolveInfo> dictPacks = packageManager.queryIntentActivities(dictIntent, 0);
|
||||||
|
Log.i("KP2AK", "Searching for HK dictionaries. Found " + dictPacks.size() + " packages");
|
||||||
for (ResolveInfo ri : dictPacks) {
|
for (ResolveInfo ri : dictPacks) {
|
||||||
ApplicationInfo appInfo = ri.activityInfo.applicationInfo;
|
ApplicationInfo appInfo = ri.activityInfo.applicationInfo;
|
||||||
String pkgName = appInfo.packageName;
|
String pkgName = appInfo.packageName;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.util.Iterator;
|
|||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -65,7 +66,16 @@ public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver {
|
|||||||
|
|
||||||
protected String[] getProtectedFieldsListFromIntent()
|
protected String[] getProtectedFieldsListFromIntent()
|
||||||
{
|
{
|
||||||
return _intent.getStringArrayExtra(Strings.EXTRA_PROTECTED_FIELDS_LIST);
|
try {
|
||||||
|
JSONArray json = new JSONArray(_intent.getStringExtra(Strings.EXTRA_PROTECTED_FIELDS_LIST));
|
||||||
|
String[] res = new String[json.length()];
|
||||||
|
for(int i = 0; i < json.length(); i++)
|
||||||
|
res[i] = json.getString(i);
|
||||||
|
return res;
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
src/java/PluginQR/.gitignore
vendored
Normal file
1
src/java/PluginQR/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
1
src/java/PluginQR/app/.gitignore
vendored
Normal file
1
src/java/PluginQR/app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
37
src/java/PluginQR/app/build.gradle
Normal file
37
src/java/PluginQR/app/build.gradle
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 26
|
||||||
|
buildToolsVersion '26.0.2'
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "keepass2android.plugin.qr"
|
||||||
|
minSdkVersion 14
|
||||||
|
targetSdkVersion 19
|
||||||
|
versionCode 3
|
||||||
|
versionName "1.0.2"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility 1.8
|
||||||
|
targetCompatibility 1.8
|
||||||
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
disable 'ExtraTranslation'
|
||||||
|
disable 'MissingTranslation'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':Keepass2AndroidPluginSDK2')
|
||||||
|
compile 'com.google.zxing:core:2.3.0'
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="keepass2android.plugin.qr"
|
package="keepass2android.plugin.qr" >
|
||||||
android:versionCode="2"
|
|
||||||
android:versionName="1.0.1" >
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
<uses-permission
|
<uses-permission
|
||||||
@@ -15,10 +13,6 @@
|
|||||||
<uses-feature android:name="android.hardware.camera.flash" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera.flash" android:required="false"/>
|
||||||
<uses-feature android:name="android.hardware.screen.portrait" android:required="false"/>
|
<uses-feature android:name="android.hardware.screen.portrait" android:required="false"/>
|
||||||
|
|
||||||
<uses-sdk
|
|
||||||
android:minSdkVersion="14"
|
|
||||||
android:targetSdkVersion="19" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user