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%
 | 
			
		||||
    translate_attributes: '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:
 | 
			
		||||
 | 
			
		||||
{{
 | 
			
		||||
```
 | 
			
		||||
Mono for Android		
 | 
			
		||||
			.net dlls			5.0 MB
 | 
			
		||||
			Runtime				2.5 MB				
 | 
			
		||||
@@ -22,4 +22,4 @@ Java/Mono bindings					0.5 MB
 | 
			
		||||
rest							0.3 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?
 | 
			
		||||
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.
 | 
			
		||||
 
 | 
			
		||||
@@ -81,10 +81,6 @@
 | 
			
		||||
    </XamarinComponentReference>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\AdalBindings\AdalBindings.csproj">
 | 
			
		||||
      <Project>{0b109c0e-0514-4340-8779-5bd6a0dde84e}</Project>
 | 
			
		||||
      <Name>AdalBindings</Name>
 | 
			
		||||
    </ProjectReference>
 | 
			
		||||
    <ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCloudBindings", "PCloudBindings\PCloudBindings.csproj", "{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdalBindings", "AdalBindings\AdalBindings.csproj", "{0B109C0E-0514-4340-8779-5BD6A0DDE84E}"
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		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|x64.ActiveCfg = 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
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using keepass2android;
 | 
			
		||||
#if KeePassUAP
 | 
			
		||||
using Org.BouncyCastle.Crypto;
 | 
			
		||||
using Org.BouncyCastle.Crypto.Engines;
 | 
			
		||||
@@ -144,6 +144,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
 | 
			
		||||
		public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
 | 
			
		||||
			ulong uNumRounds)
 | 
			
		||||
		{
 | 
			
		||||
		    Kp2aLog.Log("Warning: transforming key managed. Expect this to be slow!");
 | 
			
		||||
#if KeePassUAP
 | 
			
		||||
			KeyParameter kp = new KeyParameter(pbKeySeed32);
 | 
			
		||||
			AesEngine aes = new AesEngine();
 | 
			
		||||
 
 | 
			
		||||
@@ -38,9 +38,11 @@ namespace KeePassLib.Keys
 | 
			
		||||
			get;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// Clear the key and securely erase all security-critical information.
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// void Clear();
 | 
			
		||||
        // /// <summary>
 | 
			
		||||
        // /// Clear the key and securely erase all security-critical information.
 | 
			
		||||
        // /// </summary>
 | 
			
		||||
        // void Clear();
 | 
			
		||||
 | 
			
		||||
        uint GetMinKdbxVersion();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,12 @@ namespace KeePassLib.Keys
 | 
			
		||||
			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(pbKeyData != null); if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,12 @@ namespace KeePassLib.Keys
 | 
			
		||||
			get { return m_pbKeyData; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IOConnectionInfo Ioc
 | 
			
		||||
	    public uint GetMinKdbxVersion()
 | 
			
		||||
	    {
 | 
			
		||||
	        return 0;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    public IOConnectionInfo Ioc
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_ioc; }
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,12 @@ namespace KeePassLib.Keys
 | 
			
		||||
			get { return m_pbKeyData; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public KcpPassword(byte[] pbPasswordUtf8)
 | 
			
		||||
	    public uint GetMinKdbxVersion()
 | 
			
		||||
	    {
 | 
			
		||||
	        return 0;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    public KcpPassword(byte[] pbPasswordUtf8)
 | 
			
		||||
		{
 | 
			
		||||
			SetKey(pbPasswordUtf8);
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,12 @@ namespace KeePassLib.Keys
 | 
			
		||||
			get { return m_pbKeyData; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
	    public uint GetMinKdbxVersion()
 | 
			
		||||
	    {
 | 
			
		||||
	        return 0;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    /// <summary>
 | 
			
		||||
		/// Construct a user account key.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public KcpUserAccount()
 | 
			
		||||
 
 | 
			
		||||
@@ -85,8 +85,8 @@ namespace keepass2android
 | 
			
		||||
		{
 | 
			
		||||
			if (!File.Exists(LogFilename))
 | 
			
		||||
			{
 | 
			
		||||
				File.Create(LogFilename);
 | 
			
		||||
				_logToFile = true;
 | 
			
		||||
				File.Create(LogFilename).Dispose();
 | 
			
		||||
                _logToFile = true;
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
@@ -100,8 +100,7 @@ namespace keepass2android
 | 
			
		||||
				int count = 0;
 | 
			
		||||
				while (File.Exists(LogFilename + "." + 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)
 | 
			
		||||
			{
 | 
			
		||||
				Kp2aLog.Log(e.Message);
 | 
			
		||||
				Kp2aLog.Log(e.ToString());
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -485,12 +485,6 @@ namespace KeePassLib
 | 
			
		||||
			set { m_pbHashOfLastIO = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public bool UseFileTransactions
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_bUseFileTransactions; }
 | 
			
		||||
			set { m_bUseFileTransactions = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public bool UseFileLocks
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_bUseFileLocks; }
 | 
			
		||||
 
 | 
			
		||||
@@ -360,5 +360,12 @@ namespace KeePassLib.Serialization
 | 
			
		||||
				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.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Security;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
@@ -126,8 +127,8 @@ namespace KeePassLib.Serialization
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private const uint FileVersion32 = 0x00040000;
 | 
			
		||||
 | 
			
		||||
		internal const uint FileVersion32_4 = 0x00040000; // First of 4.x series
 | 
			
		||||
		internal const uint FileVersion32_3 = 0x00030001; // Old format 3.1
 | 
			
		||||
		public const uint FileVersion32_4 = 0x00040000; // First of 4.x series
 | 
			
		||||
		public const uint FileVersion32_3 = 0x00030001; // Old format 3.1
 | 
			
		||||
 | 
			
		||||
		private const uint FileVersionCriticalMask = 0xFFFF0000;
 | 
			
		||||
 | 
			
		||||
@@ -372,16 +373,19 @@ namespace KeePassLib.Serialization
 | 
			
		||||
		{
 | 
			
		||||
			if(m_uForceVersion != 0) return m_uForceVersion;
 | 
			
		||||
 | 
			
		||||
			// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
 | 
			
		||||
 | 
			
		||||
			AesKdf kdfAes = new AesKdf();
 | 
			
		||||
            // See also KeePassKdb2x3.Export (KDBX 3.1 export module)
 | 
			
		||||
		    uint minVersionForKeys = m_pwDatabase.MasterKey.UserKeys.Select(key => key.GetMinKdbxVersion()).Max();
 | 
			
		||||
		    
 | 
			
		||||
            AesKdf kdfAes = new AesKdf();
 | 
			
		||||
			if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
 | 
			
		||||
				return FileVersion32;
 | 
			
		||||
				return Math.Max(FileVersion32, minVersionForKeys);
 | 
			
		||||
 | 
			
		||||
			if(m_pwDatabase.PublicCustomData.Count > 0)
 | 
			
		||||
				return FileVersion32;
 | 
			
		||||
			    return Math.Max(FileVersion32, minVersionForKeys);
 | 
			
		||||
 | 
			
		||||
			bool bCustomData = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            bool bCustomData = false;
 | 
			
		||||
			GroupHandler gh = delegate(PwGroup pg)
 | 
			
		||||
			{
 | 
			
		||||
				if(pg == null) { Debug.Assert(false); return true; }
 | 
			
		||||
@@ -396,9 +400,10 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			};
 | 
			
		||||
			gh(m_pwDatabase.RootGroup);
 | 
			
		||||
			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,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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.Collections.Generic;
 | 
			
		||||
using System.Net.Security;
 | 
			
		||||
using System.Security.Cryptography.X509Certificates;
 | 
			
		||||
using Android.App;
 | 
			
		||||
@@ -9,6 +10,7 @@ using KeePassLib;
 | 
			
		||||
using KeePassLib.Keys;
 | 
			
		||||
using KeePassLib.Serialization;
 | 
			
		||||
using keepass2android.Io;
 | 
			
		||||
using KeePassLib.Interfaces;
 | 
			
		||||
#if !NoNet
 | 
			
		||||
using Keepass2android.Javafilestorage;
 | 
			
		||||
#endif 
 | 
			
		||||
@@ -33,31 +35,41 @@ namespace keepass2android
 | 
			
		||||
	/// This also contains methods which are UI specific and should be replacable for testing.
 | 
			
		||||
	public interface IKp2aApp : ICertificateValidationHandler
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Locks the currently open database, quicklocking if available (unless false is passed for allowQuickUnlock)
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		void LockDatabase(bool allowQuickUnlock = true);
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Locks all currently open databases, quicklocking if available (unless false is passed for allowQuickUnlock)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        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>
 | 
			
		||||
		/// Returns the current database
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		Database GetDb();
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Loads the specified data as the currently open database, as unlocked.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        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.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, string displayName = "");
 | 
			
		||||
 | 
			
		||||
	    HashSet<PwGroup> DirtyGroups { get; }
 | 
			
		||||
 | 
			
		||||
	    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>
 | 
			
		||||
		/// Creates a new database and returns it
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		Database CreateNewDatabase();
 | 
			
		||||
		Database CreateNewDatabase(bool makeCurrent);
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Returns the user-displayable string identified by stringKey
 | 
			
		||||
@@ -76,7 +88,8 @@ namespace keepass2android
 | 
			
		||||
		                    EventHandler<DialogClickEventArgs> yesHandler,
 | 
			
		||||
		                    EventHandler<DialogClickEventArgs> noHandler,
 | 
			
		||||
		                    EventHandler<DialogClickEventArgs> cancelHandler,
 | 
			
		||||
		                    Context ctx);
 | 
			
		||||
		                    Context ctx,
 | 
			
		||||
                            string messageSuffix = "");
 | 
			
		||||
 | 
			
		||||
		/// <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.
 | 
			
		||||
@@ -86,7 +99,8 @@ namespace keepass2android
 | 
			
		||||
		                    EventHandler<DialogClickEventArgs> yesHandler,
 | 
			
		||||
		                    EventHandler<DialogClickEventArgs> noHandler,
 | 
			
		||||
		                    EventHandler<DialogClickEventArgs> cancelHandler,
 | 
			
		||||
		                    Context ctx);
 | 
			
		||||
		                    Context ctx,
 | 
			
		||||
		                    string messageSuffix = "");
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Returns a Handler object which can run tasks on the UI thread
 | 
			
		||||
@@ -111,6 +125,10 @@ namespace keepass2android
 | 
			
		||||
		bool CheckForDuplicateUuids { get; }
 | 
			
		||||
#if !NoNet
 | 
			
		||||
		ICertificateErrorHandler CertificateErrorHandler { get; }
 | 
			
		||||
	    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
	    
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -207,8 +207,7 @@ namespace keepass2android.Io
 | 
			
		||||
 | 
			
		||||
		public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
 | 
			
		||||
		{
 | 
			
		||||
			//TODO: required for OTP Aux file retrieval
 | 
			
		||||
			throw new NotImplementedException();
 | 
			
		||||
		    return IoUtil.GetParentPath(ioc);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
 | 
			
		||||
 
 | 
			
		||||
@@ -255,7 +255,8 @@ namespace keepass2android.Io
 | 
			
		||||
			if (ioc.IsLocalFile())
 | 
			
		||||
			{
 | 
			
		||||
				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);
 | 
			
		||||
				if ((extDirectory != null) && (ioc.Path.StartsWith(extDirectory.CanonicalPath)))
 | 
			
		||||
 
 | 
			
		||||
@@ -65,22 +65,28 @@ namespace keepass2android.Io
 | 
			
		||||
		
 | 
			
		||||
		protected readonly OfflineSwitchableFileStorage _cachedStorage;
 | 
			
		||||
		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);
 | 
			
		||||
			_cacheSupervisor = cacheSupervisor;
 | 
			
		||||
			_streamCacheDir = cacheDir + Java.IO.File.Separator + "OfflineCache" + Java.IO.File.Separator;
 | 
			
		||||
			if (!Directory.Exists(_streamCacheDir))
 | 
			
		||||
				Directory.CreateDirectory(_streamCacheDir);
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
			_legacyCacheDir = cacheDirContext.CacheDir.Path + Java.IO.File.Separator + "OfflineCache" + Java.IO.File.Separator;
 | 
			
		||||
			if (!Directory.Exists(_legacyCacheDir))
 | 
			
		||||
				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()
 | 
			
		||||
		{
 | 
			
		||||
			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; } }
 | 
			
		||||
 | 
			
		||||
@@ -105,7 +111,11 @@ namespace keepass2android.Io
 | 
			
		||||
		{
 | 
			
		||||
			SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
			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)
 | 
			
		||||
@@ -168,7 +178,9 @@ namespace keepass2android.Io
 | 
			
		||||
				if (!IsCached(ioc))
 | 
			
		||||
					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());
 | 
			
		||||
 | 
			
		||||
				_cacheSupervisor.CouldntOpenFromRemote(ioc, ex);
 | 
			
		||||
 
 | 
			
		||||
@@ -233,7 +233,6 @@ namespace keepass2android.Io
 | 
			
		||||
 | 
			
		||||
		public FileDescription GetFileDescription(IOConnectionInfo ioc)
 | 
			
		||||
		{
 | 
			
		||||
			Kp2aLog.Log("GetFileDescription "+ioc.Path);
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				return ConvertToFileDescription(Jfs.GetFileEntry(IocToPath(ioc)));
 | 
			
		||||
@@ -302,7 +301,9 @@ namespace keepass2android.Io
 | 
			
		||||
 | 
			
		||||
		public void OnResume(IFileStorageSetupActivity activity)
 | 
			
		||||
		{
 | 
			
		||||
#if DEBUG
 | 
			
		||||
			Kp2aLog.Log("JFS/OnResume Ioc.Path=" +activity.Ioc.Path+". Path="+((IJavaFileStorageFileStorageSetupActivity)activity).Path);
 | 
			
		||||
#endif
 | 
			
		||||
			_jfs.OnResume(((IJavaFileStorageFileStorageSetupActivity) activity));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -366,4 +367,4 @@ namespace keepass2android.Io
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
        }
 | 
			
		||||
@@ -91,7 +91,7 @@
 | 
			
		||||
    <Compile Include="database\CheckDatabaseForChanges.cs" />
 | 
			
		||||
    <Compile Include="database\edit\AddTemplateEntries.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\MoveElements.cs" />
 | 
			
		||||
    <Compile Include="database\KdbDatabaseFormat.cs" />
 | 
			
		||||
@@ -103,6 +103,7 @@
 | 
			
		||||
    <Compile Include="DataExchange\Formats\KeePassKdb2x.cs" />
 | 
			
		||||
    <Compile Include="DataExchange\Formats\KeePassXml2x.cs" />
 | 
			
		||||
    <Compile Include="DataExchange\PwExportInfo.cs" />
 | 
			
		||||
    <Compile Include="ElementAndDatabaseId.cs" />
 | 
			
		||||
    <Compile Include="Io\AndroidContentStorage.cs" />
 | 
			
		||||
    <Compile Include="Io\BuiltInFileStorage.cs" />
 | 
			
		||||
    <Compile Include="Io\CachingFileStorage.cs" />
 | 
			
		||||
@@ -156,10 +157,6 @@
 | 
			
		||||
    <Compile Include="Utils\Spr\SprEngine.PickChars.cs" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\AdalBindings\AdalBindings.csproj">
 | 
			
		||||
      <Project>{0b109c0e-0514-4340-8779-5bd6a0dde84e}</Project>
 | 
			
		||||
      <Name>AdalBindings</Name>
 | 
			
		||||
    </ProjectReference>
 | 
			
		||||
    <ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
 | 
			
		||||
      <Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
 | 
			
		||||
      <Name>JavaFileStorageBindings</Name>
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,8 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
		public void UpdateMessage (String message)
 | 
			
		||||
		{
 | 
			
		||||
			_message = message;
 | 
			
		||||
		    Kp2aLog.Log("status message: " + message);
 | 
			
		||||
            _message = message;
 | 
			
		||||
			if ( _app!= null && _progressDialog != null && _handler != null ) {
 | 
			
		||||
				_handler.Post(() => {_progressDialog.SetMessage(message); } );
 | 
			
		||||
			}
 | 
			
		||||
@@ -60,6 +61,7 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
		public void UpdateSubMessage(String submessage)
 | 
			
		||||
		{
 | 
			
		||||
		    Kp2aLog.Log("status submessage: " + submessage);
 | 
			
		||||
		    _submessage = submessage;
 | 
			
		||||
			if (_app != null && _progressDialog != null && _handler != null)
 | 
			
		||||
			{
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ namespace keepass2android
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// EqualityComparer implementation to compare PwGroups based on their Id
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	class PwGroupEqualityFromIdComparer: IEqualityComparer<PwGroup>
 | 
			
		||||
	public class PwGroupEqualityFromIdComparer: IEqualityComparer<PwGroup>
 | 
			
		||||
	{
 | 
			
		||||
		#region IEqualityComparer implementation			
 | 
			
		||||
		public bool Equals (PwGroup x, PwGroup y)
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,7 @@ namespace keepass2android
 | 
			
		||||
			PwGroup pgResults = new PwGroup(true, true, strGroupName, PwIcon.EMailSearch) {IsVirtual = true};
 | 
			
		||||
			if (String.IsNullOrWhiteSpace(host))
 | 
			
		||||
				return pgResults;
 | 
			
		||||
			foreach (PwEntry entry in database.Entries.Values)
 | 
			
		||||
			foreach (PwEntry entry in database.EntriesById.Values)
 | 
			
		||||
			{
 | 
			
		||||
				string otherUrl = entry.Strings.ReadSafe(PwDefs.UrlField);
 | 
			
		||||
				otherUrl = SprEngine.Compile(otherUrl, new SprContext(entry, database.KpDatabase, SprCompileFlags.References));
 | 
			
		||||
 
 | 
			
		||||
@@ -86,6 +86,8 @@ namespace keepass2android
 | 
			
		||||
		ReadOnlyReason_PreKitKat,
 | 
			
		||||
		ReadOnlyReason_ReadOnlyFlag,
 | 
			
		||||
		ReadOnlyReason_ReadOnlyKitKat,
 | 
			
		||||
        ReadOnlyReason_LocalBackup
 | 
			
		||||
        ReadOnlyReason_LocalBackup,
 | 
			
		||||
        Ok,
 | 
			
		||||
        cancel
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ namespace keepass2android
 | 
			
		||||
		{
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				IOConnectionInfo ioc = _app.GetDb().Ioc;
 | 
			
		||||
				IOConnectionInfo ioc = _app.CurrentDb.Ioc;
 | 
			
		||||
				IFileStorage fileStorage = _app.GetFileStorage(ioc);
 | 
			
		||||
				if (fileStorage is CachingFileStorage)
 | 
			
		||||
				{
 | 
			
		||||
@@ -49,7 +49,7 @@ namespace keepass2android
 | 
			
		||||
					hashingRemoteStream.CopyTo(remoteData);
 | 
			
		||||
					hashingRemoteStream.Close();
 | 
			
		||||
					
 | 
			
		||||
					if (!MemUtil.ArraysEqual(_app.GetDb().KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash))
 | 
			
		||||
					if (!MemUtil.ArraysEqual(_app.CurrentDb.KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash))
 | 
			
		||||
					{
 | 
			
		||||
						_app.TriggerReload(_context);
 | 
			
		||||
						Finish(true);
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ using KeePassLib;
 | 
			
		||||
using KeePassLib.Keys;
 | 
			
		||||
using KeePassLib.Serialization;
 | 
			
		||||
using keepass2android.Io;
 | 
			
		||||
using KeePassLib.Interfaces;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
using Exception = System.Exception;
 | 
			
		||||
using String = System.String;
 | 
			
		||||
@@ -33,27 +34,23 @@ using String = System.String;
 | 
			
		||||
namespace keepass2android
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	public class Database {
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		public Dictionary<PwUuid, PwGroup> Groups = new Dictionary<PwUuid, PwGroup>(new PwUuidEqualityComparer());
 | 
			
		||||
		public Dictionary<PwUuid, PwEntry> Entries = new Dictionary<PwUuid, PwEntry>(new PwUuidEqualityComparer());
 | 
			
		||||
		public HashSet<PwGroup> Dirty = new HashSet<PwGroup>(new PwGroupEqualityFromIdComparer());
 | 
			
		||||
	public class Database
 | 
			
		||||
	{
 | 
			
		||||
	    public HashSet<IStructureItem> Elements = new HashSet<IStructureItem>();
 | 
			
		||||
		public Dictionary<PwUuid, PwGroup> GroupsById = new Dictionary<PwUuid, PwGroup>(new PwUuidEqualityComparer());
 | 
			
		||||
		public Dictionary<PwUuid, PwEntry> EntriesById = new Dictionary<PwUuid, PwEntry>(new PwUuidEqualityComparer());
 | 
			
		||||
		public PwGroup Root;
 | 
			
		||||
		public PwDatabase KpDatabase;
 | 
			
		||||
		public IOConnectionInfo Ioc 
 | 
			
		||||
		{
 | 
			
		||||
			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>
 | 
			
		||||
		/// if an OTP key was used, this property tells the location of the OTP auxiliary file.
 | 
			
		||||
		/// Must be set after loading.
 | 
			
		||||
@@ -73,31 +70,14 @@ namespace keepass2android
 | 
			
		||||
            _app = app;
 | 
			
		||||
			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
 | 
			
		||||
        {
 | 
			
		||||
            get { return _reloadRequested; }
 | 
			
		||||
            set { _reloadRequested = value; }
 | 
			
		||||
        }
 | 
			
		||||
		public bool ReloadRequested { get; set; }
 | 
			
		||||
 | 
			
		||||
		public bool Loaded {
 | 
			
		||||
			get { return _loaded;}
 | 
			
		||||
			set { _loaded = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public bool DidOpenFileChange()
 | 
			
		||||
	    public bool DidOpenFileChange()
 | 
			
		||||
		{
 | 
			
		||||
			if (Loaded == false)
 | 
			
		||||
			{
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			return _app.GetFileStorage(Ioc).CheckForFileChangeFast(Ioc, LastFileVersion);
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -112,32 +92,20 @@ namespace keepass2android
 | 
			
		||||
			Stream s = databaseData ?? fileStorage.OpenFileForRead(iocInfo);
 | 
			
		||||
			var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo);
 | 
			
		||||
			PopulateDatabaseFromStream(pwDatabase, s, iocInfo, compositeKey, status, databaseFormat);
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				LastFileVersion = fileVersion;
 | 
			
		||||
		    LastFileVersion = fileVersion;
 | 
			
		||||
 | 
			
		||||
				status.UpdateSubMessage("");
 | 
			
		||||
		    status.UpdateSubMessage("");
 | 
			
		||||
 | 
			
		||||
				Root = pwDatabase.RootGroup;
 | 
			
		||||
				PopulateGlobals(Root);
 | 
			
		||||
		    Root = pwDatabase.RootGroup;
 | 
			
		||||
		    PopulateGlobals(Root);
 | 
			
		||||
 | 
			
		||||
				
 | 
			
		||||
				KpDatabase = pwDatabase;
 | 
			
		||||
				SearchHelper = new SearchDbHelper(app);
 | 
			
		||||
		    KpDatabase = pwDatabase;
 | 
			
		||||
		    SearchHelper = new SearchDbHelper(app);
 | 
			
		||||
 | 
			
		||||
				_databaseFormat = databaseFormat;
 | 
			
		||||
		    _databaseFormat = databaseFormat;
 | 
			
		||||
 | 
			
		||||
				CanWrite = databaseFormat.CanWrite && !fileStorage.IsReadOnly(iocInfo);
 | 
			
		||||
				Loaded = true;
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception)
 | 
			
		||||
			{
 | 
			
		||||
				Clear();				
 | 
			
		||||
				throw;
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
			
 | 
			
		||||
		    CanWrite = databaseFormat.CanWrite && !fileStorage.IsReadOnly(iocInfo);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@@ -219,8 +187,7 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
		public void SaveData()  {
 | 
			
		||||
            
 | 
			
		||||
			KpDatabase.UseFileTransactions = _app.GetBooleanPreference(PreferenceKey.UseFileTransactions);
 | 
			
		||||
			using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, KpDatabase.UseFileTransactions))
 | 
			
		||||
			using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
 | 
			
		||||
			{
 | 
			
		||||
				DatabaseFormat.Save(KpDatabase, trans.OpenFile());
 | 
			
		||||
				
 | 
			
		||||
@@ -239,14 +206,18 @@ namespace keepass2android
 | 
			
		||||
			{
 | 
			
		||||
				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) 
 | 
			
		||||
			{
 | 
			
		||||
				if (checkForDuplicateUuids)
 | 
			
		||||
@@ -258,7 +229,6 @@ namespace keepass2android
 | 
			
		||||
					}
 | 
			
		||||
					 * */
 | 
			
		||||
				}
 | 
			
		||||
				Groups[g.Uuid] = g;
 | 
			
		||||
				PopulateGlobals(g);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -266,33 +236,15 @@ namespace keepass2android
 | 
			
		||||
		{
 | 
			
		||||
			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]
 | 
			
		||||
 
 | 
			
		||||
@@ -216,9 +216,16 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
		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
 | 
			
		||||
 
 | 
			
		||||
@@ -14,13 +14,13 @@ namespace keepass2android
 | 
			
		||||
	public class PwEntryOutput
 | 
			
		||||
	{
 | 
			
		||||
		private readonly PwEntry _entry;
 | 
			
		||||
		private readonly PwDatabase _db;
 | 
			
		||||
		private readonly Database _db;
 | 
			
		||||
		private readonly ProtectedStringDictionary _outputStrings = new ProtectedStringDictionary();
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Constructs the PwEntryOutput by replacing the placeholders
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public PwEntryOutput(PwEntry entry, PwDatabase db)
 | 
			
		||||
		public PwEntryOutput(PwEntry entry, Database db)
 | 
			
		||||
		{
 | 
			
		||||
			_entry = entry;
 | 
			
		||||
			_db = db;
 | 
			
		||||
@@ -34,7 +34,7 @@ namespace keepass2android
 | 
			
		||||
		string GetStringAndReplacePlaceholders(string 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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ namespace keepass2android
 | 
			
		||||
		{
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				IOConnectionInfo ioc = _app.GetDb().Ioc;
 | 
			
		||||
				IOConnectionInfo ioc = _app.CurrentDb.Ioc;
 | 
			
		||||
				IFileStorage fileStorage = _app.GetFileStorage(ioc);
 | 
			
		||||
				if (!(fileStorage is CachingFileStorage))
 | 
			
		||||
				{
 | 
			
		||||
@@ -70,10 +70,12 @@ namespace keepass2android
 | 
			
		||||
									Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
 | 
			
		||||
								}
 | 
			
		||||
								_saveDb = null;
 | 
			
		||||
							}), false, remoteData);
 | 
			
		||||
							}), _app.CurrentDb, false, remoteData);
 | 
			
		||||
						_saveDb.Run();
 | 
			
		||||
 | 
			
		||||
						_app.GetDb().MarkAllGroupsAsDirty();
 | 
			
		||||
                        _app.CurrentDb.UpdateGlobals();
 | 
			
		||||
 | 
			
		||||
						_app.MarkAllGroupsAsDirty();
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
@@ -103,6 +105,7 @@ namespace keepass2android
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception e)
 | 
			
		||||
			{
 | 
			
		||||
                Kp2aLog.LogUnexpectedError(e);
 | 
			
		||||
				Finish(false, e.Message);
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ namespace keepass2android
 | 
			
		||||
	public class AddEntry : RunnableOnFinish {
 | 
			
		||||
		protected Database Db
 | 
			
		||||
		{
 | 
			
		||||
			get { return _app.GetDb(); }
 | 
			
		||||
			get { return _app.CurrentDb; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private readonly IKp2aApp _app;
 | 
			
		||||
@@ -37,13 +37,13 @@ namespace keepass2android
 | 
			
		||||
			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;
 | 
			
		||||
			_parentGroup = parentGroup;
 | 
			
		||||
			_app = app;
 | 
			
		||||
			_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
 | 
			
		||||
			SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
 | 
			
		||||
			SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
 | 
			
		||||
			save.SetStatusLogger(StatusLogger);
 | 
			
		||||
			save.Run();
 | 
			
		||||
		}
 | 
			
		||||
@@ -68,12 +68,13 @@ namespace keepass2android
 | 
			
		||||
		private class AfterAdd : OnFinish {
 | 
			
		||||
			private readonly Database _db;
 | 
			
		||||
			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;
 | 
			
		||||
				_entry = entry;
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		        _app = app;
 | 
			
		||||
		    }
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -83,11 +84,12 @@ namespace keepass2android
 | 
			
		||||
					PwGroup parent = _entry.ParentGroup; 
 | 
			
		||||
					
 | 
			
		||||
					// Mark parent group dirty
 | 
			
		||||
					_db.Dirty.Add(parent);
 | 
			
		||||
					_app.DirtyGroups.Add(parent);
 | 
			
		||||
					
 | 
			
		||||
					// Add entry to global
 | 
			
		||||
					_db.Entries[_entry.Uuid] = _entry;
 | 
			
		||||
					
 | 
			
		||||
					_db.EntriesById[_entry.Uuid] = _entry;
 | 
			
		||||
				    _db.Elements.Add(_entry);
 | 
			
		||||
 | 
			
		||||
				} else
 | 
			
		||||
				{
 | 
			
		||||
					StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
 | 
			
		||||
 
 | 
			
		||||
@@ -26,13 +26,16 @@ namespace keepass2android
 | 
			
		||||
	public class AddGroup : RunnableOnFinish {
 | 
			
		||||
		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 int _iconId;
 | 
			
		||||
		private readonly PwUuid _groupCustomIconId;
 | 
			
		||||
		internal PwGroup Group;
 | 
			
		||||
	    public PwGroup Group;
 | 
			
		||||
		internal PwGroup Parent;
 | 
			
		||||
		protected bool DontSave;
 | 
			
		||||
		readonly Activity _ctx;
 | 
			
		||||
@@ -67,9 +70,11 @@ namespace keepass2android
 | 
			
		||||
				Group.CustomIconUuid = _groupCustomIconId;
 | 
			
		||||
			}
 | 
			
		||||
			Parent.AddGroup(Group, true);
 | 
			
		||||
		    _app.CurrentDb.GroupsById[Group.Uuid] = Group;
 | 
			
		||||
		    _app.CurrentDb.Elements.Add(Group);
 | 
			
		||||
 | 
			
		||||
			// Commit to disk
 | 
			
		||||
			SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, DontSave);
 | 
			
		||||
            // Commit to disk
 | 
			
		||||
            SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, DontSave);
 | 
			
		||||
			save.SetStatusLogger(StatusLogger);
 | 
			
		||||
			save.Run();
 | 
			
		||||
		}
 | 
			
		||||
@@ -86,13 +91,15 @@ namespace keepass2android
 | 
			
		||||
				
 | 
			
		||||
				if ( Success ) {
 | 
			
		||||
					// Mark parent group dirty
 | 
			
		||||
					_addGroup.Db.Dirty.Add(_addGroup.Parent);
 | 
			
		||||
					_addGroup.App.DirtyGroups.Add(_addGroup.Parent);
 | 
			
		||||
					
 | 
			
		||||
					// 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 {
 | 
			
		||||
					StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
 | 
			
		||||
					_addGroup.Parent.Groups.Remove(_addGroup.Group);
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				base.Run();
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ namespace keepass2android
 | 
			
		||||
{
 | 
			
		||||
	public class AddTemplateEntries : RunnableOnFinish {
 | 
			
		||||
 | 
			
		||||
		class TemplateEntry
 | 
			
		||||
		public class TemplateEntry
 | 
			
		||||
		{
 | 
			
		||||
			public UiStringKey Title { get; set; }
 | 
			
		||||
			public PwIcon Icon { get; set; }
 | 
			
		||||
@@ -47,11 +47,12 @@ namespace keepass2android
 | 
			
		||||
				void AddToEntry(IKp2aApp app, PwEntry entry, int position);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			internal enum FieldType
 | 
			
		||||
		    public enum FieldType
 | 
			
		||||
			{
 | 
			
		||||
				Inline, ProtectedInline
 | 
			
		||||
			}
 | 
			
		||||
			internal enum SpecialFieldKey
 | 
			
		||||
 | 
			
		||||
		    public enum SpecialFieldKey
 | 
			
		||||
			{
 | 
			
		||||
				ExpDate, OverrideUrl, Tags
 | 
			
		||||
			}
 | 
			
		||||
@@ -125,7 +126,7 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
		protected Database Db
 | 
			
		||||
		{
 | 
			
		||||
			get { return _app.GetDb(); }
 | 
			
		||||
			get { return _app.CurrentDb; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private readonly IKp2aApp _app;
 | 
			
		||||
@@ -140,7 +141,7 @@ namespace keepass2android
 | 
			
		||||
			//_onFinishToRun = new AfterAdd(this, OnFinishToRun);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
 | 
			
		||||
		public static readonly List<TemplateEntry> TemplateEntries = new List<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);
 | 
			
		||||
 | 
			
		||||
			List<PwEntry> addedEntries;
 | 
			
		||||
@@ -298,10 +310,10 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
			if (addedEntries.Any())
 | 
			
		||||
			{
 | 
			
		||||
				_app.GetDb().Dirty.Add(templateGroup);
 | 
			
		||||
				_app.DirtyGroups.Add(templateGroup);
 | 
			
		||||
 | 
			
		||||
				// Commit to disk
 | 
			
		||||
				SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
 | 
			
		||||
				SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
 | 
			
		||||
				save.SetStatusLogger(StatusLogger);
 | 
			
		||||
				save.Run();
 | 
			
		||||
			}
 | 
			
		||||
@@ -315,26 +327,28 @@ namespace keepass2android
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			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
 | 
			
		||||
				templateGroup = new PwGroup(true, true, _app.GetResourceString(UiStringKey.TemplateGroupName), PwIcon.Folder);
 | 
			
		||||
				_app.GetDb().KpDatabase.RootGroup.AddGroup(templateGroup, true);
 | 
			
		||||
				_app.GetDb().KpDatabase.EntryTemplatesGroup = templateGroup.Uuid;
 | 
			
		||||
				_app.GetDb().KpDatabase.EntryTemplatesGroupChanged = DateTime.Now;
 | 
			
		||||
				_app.GetDb().Dirty.Add(_app.GetDb().KpDatabase.RootGroup);
 | 
			
		||||
				_app.GetDb().Groups[templateGroup.Uuid] = templateGroup;
 | 
			
		||||
				_app.CurrentDb.KpDatabase.RootGroup.AddGroup(templateGroup, true);
 | 
			
		||||
				_app.CurrentDb.KpDatabase.EntryTemplatesGroup = templateGroup.Uuid;
 | 
			
		||||
				_app.CurrentDb.KpDatabase.EntryTemplatesGroupChanged = DateTime.Now;
 | 
			
		||||
				_app.DirtyGroups.Add(_app.CurrentDb.KpDatabase.RootGroup);
 | 
			
		||||
				_app.CurrentDb.GroupsById[templateGroup.Uuid] = templateGroup;
 | 
			
		||||
			    _app.CurrentDb.Elements.Add(templateGroup);
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
			addedEntries = new List<PwEntry>();
 | 
			
		||||
 | 
			
		||||
			foreach (var template in TemplateEntries)
 | 
			
		||||
			{
 | 
			
		||||
				if (_app.GetDb().Entries.ContainsKey(template.Uuid))
 | 
			
		||||
				if (_app.CurrentDb.EntriesById.ContainsKey(template.Uuid))
 | 
			
		||||
					continue;
 | 
			
		||||
				PwEntry entry = CreateEntry(template);
 | 
			
		||||
				templateGroup.AddEntry(entry, true);
 | 
			
		||||
				addedEntries.Add(entry);
 | 
			
		||||
				_app.GetDb().Entries[entry.Uuid] = entry;
 | 
			
		||||
				_app.CurrentDb.EntriesById[entry.Uuid] = entry;
 | 
			
		||||
			}
 | 
			
		||||
			return templateGroup;
 | 
			
		||||
		}
 | 
			
		||||
@@ -373,8 +387,12 @@ namespace keepass2android
 | 
			
		||||
				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 {
 | 
			
		||||
		
 | 
			
		||||
		private const ulong DefaultEncryptionRounds = PwDefs.DefaultKeyEncryptionRounds;
 | 
			
		||||
		
 | 
			
		||||
		private readonly IOConnectionInfo _ioc;
 | 
			
		||||
	    private readonly IOConnectionInfo _ioc;
 | 
			
		||||
		private readonly bool _dontSave;
 | 
			
		||||
		private readonly Activity _ctx;
 | 
			
		||||
        private readonly IKp2aApp _app;
 | 
			
		||||
		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;
 | 
			
		||||
			_ioc = ioc;
 | 
			
		||||
			_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)
 | 
			
		||||
		{
 | 
			
		||||
			_ctx = ctx;
 | 
			
		||||
@@ -51,12 +50,13 @@ namespace keepass2android
 | 
			
		||||
			_dontSave = dontSave;
 | 
			
		||||
			_app = app;
 | 
			
		||||
			_key = key;
 | 
			
		||||
		    _makeCurrent = makeCurrent;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		public override void Run() {
 | 
			
		||||
			StatusLogger.UpdateMessage(UiStringKey.progress_create);
 | 
			
		||||
			Database db = _app.CreateNewDatabase();
 | 
			
		||||
			Database db = _app.CreateNewDatabase(_makeCurrent);
 | 
			
		||||
 | 
			
		||||
			db.KpDatabase = new KeePassLib.PwDatabase();
 | 
			
		||||
			
 | 
			
		||||
@@ -74,7 +74,6 @@ namespace keepass2android
 | 
			
		||||
			
 | 
			
		||||
			// Set Database state
 | 
			
		||||
			db.Root = db.KpDatabase.RootGroup;
 | 
			
		||||
			db.Loaded = true;
 | 
			
		||||
			db.SearchHelper = new SearchDbHelper(_app);
 | 
			
		||||
 | 
			
		||||
			// Add a couple default groups
 | 
			
		||||
@@ -88,12 +87,14 @@ namespace keepass2android
 | 
			
		||||
			addTemplates.AddTemplates(out addedEntries);
 | 
			
		||||
			
 | 
			
		||||
			// Commit changes
 | 
			
		||||
			SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave);
 | 
			
		||||
			SaveDb save = new SaveDb(_ctx, _app, db, OnFinishToRun, _dontSave);
 | 
			
		||||
			save.SetStatusLogger(StatusLogger);
 | 
			
		||||
			_onFinishToRun = null;
 | 
			
		||||
			save.Run();
 | 
			
		||||
			
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
		    db.UpdateGlobals();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
	    public DeleteEntry(Activity activiy, IKp2aApp app, PwEntry entry, OnFinish finish):base(activiy, finish, app) {
 | 
			
		||||
			Ctx = activiy;
 | 
			
		||||
			Db = app.GetDb();
 | 
			
		||||
			Db = app.FindDatabaseForElement(entry);
 | 
			
		||||
			_entry = entry;
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
@@ -40,7 +40,7 @@ namespace keepass2android
 | 
			
		||||
		{
 | 
			
		||||
			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)
 | 
			
		||||
        {
 | 
			
		||||
			base.SetMembers(activity, app.GetDb());
 | 
			
		||||
			base.SetMembers(activity, app.FindDatabaseForElement(group));
 | 
			
		||||
 | 
			
		||||
			_group = group;
 | 
			
		||||
	        DontSave = dontSave;
 | 
			
		||||
@@ -58,7 +58,7 @@ namespace keepass2android
 | 
			
		||||
		{
 | 
			
		||||
			get
 | 
			
		||||
			{
 | 
			
		||||
				return App.GetDb().DatabaseFormat.CanRecycle && CanRecycleGroup(_group);
 | 
			
		||||
				return Db.DatabaseFormat.CanRecycle && CanRecycleGroup(_group);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,12 @@ using KeePassLib.Interfaces;
 | 
			
		||||
 | 
			
		||||
namespace keepass2android
 | 
			
		||||
{
 | 
			
		||||
	public class DeleteMultipleItems : DeleteRunnable
 | 
			
		||||
	public class DeleteMultipleItemsFromOneDatabase : DeleteRunnable
 | 
			
		||||
	{
 | 
			
		||||
		private readonly List<IStructureItem> _elementsToDelete;
 | 
			
		||||
		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)
 | 
			
		||||
		{
 | 
			
		||||
			_elementsToDelete = elementsToDelete;
 | 
			
		||||
@@ -21,12 +21,13 @@ namespace keepass2android
 | 
			
		||||
			//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)
 | 
			
		||||
			_canRecycle = DetermineCanRecycle();
 | 
			
		||||
		    ShowDatabaseIocInStatus = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool DetermineCanRecycle()
 | 
			
		||||
		{
 | 
			
		||||
			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.");
 | 
			
		||||
				return false;
 | 
			
		||||
@@ -100,7 +100,8 @@ namespace keepass2android
 | 
			
		||||
						};
 | 
			
		||||
 | 
			
		||||
				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;
 | 
			
		||||
 | 
			
		||||
				bGroupListUpdateRequired = true;
 | 
			
		||||
@@ -121,24 +122,27 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
		public void Start()
 | 
			
		||||
		{
 | 
			
		||||
			if (CanRecycle)
 | 
			
		||||
            string messageSuffix = ShowDatabaseIocInStatus ? "(" + App.GetFileStorage(Db.Ioc).GetDisplayName(Db.Ioc) + ")" : "";
 | 
			
		||||
 | 
			
		||||
            if (CanRecycle)
 | 
			
		||||
			{
 | 
			
		||||
				App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
 | 
			
		||||
					QuestionRecycleResourceId,
 | 
			
		||||
					(dlgSender, dlgEvt) =>
 | 
			
		||||
					{
 | 
			
		||||
						DeletePermanently = true;
 | 
			
		||||
						ProgressTask pt = new ProgressTask(App, Ctx, this);
 | 
			
		||||
						pt.Run();
 | 
			
		||||
					    DeletePermanently = true;
 | 
			
		||||
					    ProgressTask pt = new ProgressTask(App, Ctx, this);
 | 
			
		||||
					    pt.Run();
 | 
			
		||||
 | 
			
		||||
					},
 | 
			
		||||
				(dlgSender, dlgEvt) =>
 | 
			
		||||
				{
 | 
			
		||||
					DeletePermanently = false;
 | 
			
		||||
					ProgressTask pt = new ProgressTask(App, Ctx, this);
 | 
			
		||||
					pt.Run();
 | 
			
		||||
				    DeletePermanently = false;
 | 
			
		||||
				    ProgressTask pt = new ProgressTask(App, Ctx, this);
 | 
			
		||||
				    pt.Run();
 | 
			
		||||
				},
 | 
			
		||||
				(dlgSender, dlgEvt) => { },
 | 
			
		||||
				Ctx);
 | 
			
		||||
				Ctx, messageSuffix);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -149,12 +153,12 @@ namespace keepass2android
 | 
			
		||||
					QuestionNoRecycleResourceId,
 | 
			
		||||
					(dlgSender, dlgEvt) =>
 | 
			
		||||
					{
 | 
			
		||||
						ProgressTask pt = new ProgressTask(App, Ctx, this);
 | 
			
		||||
						pt.Run();
 | 
			
		||||
					    ProgressTask pt = new ProgressTask(App, Ctx, this);
 | 
			
		||||
					    pt.Run();
 | 
			
		||||
					},
 | 
			
		||||
				null,
 | 
			
		||||
				(dlgSender, dlgEvt) => { },
 | 
			
		||||
				Ctx);
 | 
			
		||||
				Ctx, messageSuffix);
 | 
			
		||||
 | 
			
		||||
				
 | 
			
		||||
			}
 | 
			
		||||
@@ -182,7 +186,8 @@ namespace keepass2android
 | 
			
		||||
					PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
 | 
			
		||||
					pd.DeletedObjects.Add(pdo);
 | 
			
		||||
					touchedGroups.Add(pgParent);
 | 
			
		||||
				    Db.Entries.Remove(pe.Uuid);
 | 
			
		||||
				    Db.EntriesById.Remove(pe.Uuid);
 | 
			
		||||
				    Db.Elements.Remove(pe);
 | 
			
		||||
				}
 | 
			
		||||
				else // Recycle
 | 
			
		||||
				{
 | 
			
		||||
@@ -215,31 +220,41 @@ namespace keepass2android
 | 
			
		||||
				if (success)
 | 
			
		||||
				{
 | 
			
		||||
					foreach (var g in touchedGroups)
 | 
			
		||||
						Db.Dirty.Add(g);
 | 
			
		||||
						App.DirtyGroups.Add(g);
 | 
			
		||||
					foreach (var g in permanentlyDeletedGroups)
 | 
			
		||||
					{
 | 
			
		||||
						//remove groups from global lists if present there
 | 
			
		||||
						Db.Dirty.Remove(g);
 | 
			
		||||
						Db.Groups.Remove(g.Uuid);
 | 
			
		||||
                        //remove groups from global lists if present there
 | 
			
		||||
					    App.DirtyGroups.Remove(g);
 | 
			
		||||
						Db.GroupsById.Remove(g.Uuid);
 | 
			
		||||
					    Db.Elements.Remove(g);
 | 
			
		||||
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					// Let's not bother recovering from a failure to save.  It is too much work.
 | 
			
		||||
					App.LockDatabase(false);
 | 
			
		||||
					App.Lock(false);
 | 
			
		||||
				}
 | 
			
		||||
			}, OnFinishToRun);
 | 
			
		||||
 | 
			
		||||
			// Commit database
 | 
			
		||||
			SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, false);
 | 
			
		||||
			save.SetStatusLogger(StatusLogger);
 | 
			
		||||
			SaveDb save = new SaveDb(Ctx, App, Db, OnFinishToRun, false);
 | 
			
		||||
		    save.ShowDatabaseIocInStatus = ShowDatabaseIocInStatus;
 | 
			
		||||
 | 
			
		||||
            save.SetStatusLogger(StatusLogger);
 | 
			
		||||
			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; }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,9 +26,12 @@ namespace keepass2android
 | 
			
		||||
	public class EditGroup : RunnableOnFinish {
 | 
			
		||||
		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 PwIcon _iconId;
 | 
			
		||||
		private readonly PwUuid _customIconId;
 | 
			
		||||
@@ -57,7 +60,7 @@ namespace keepass2android
 | 
			
		||||
			Group.Touch(true);
 | 
			
		||||
 | 
			
		||||
			// Commit to disk
 | 
			
		||||
			SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
 | 
			
		||||
			SaveDb save = new SaveDb(_ctx, _app, Db, OnFinishToRun);
 | 
			
		||||
			save.SetStatusLogger(StatusLogger);
 | 
			
		||||
			save.Run();
 | 
			
		||||
		}
 | 
			
		||||
@@ -76,10 +79,10 @@ namespace keepass2android
 | 
			
		||||
				
 | 
			
		||||
				if ( Success ) {
 | 
			
		||||
					// Mark parent group dirty
 | 
			
		||||
					_editGroup.Db.Dirty.Add(_editGroup.Group.ParentGroup);
 | 
			
		||||
					_editGroup.App.DirtyGroups.Add(_editGroup.Group.ParentGroup);
 | 
			
		||||
				} else
 | 
			
		||||
				{
 | 
			
		||||
					_editGroup._app.LockDatabase(false);
 | 
			
		||||
					_editGroup._app.Lock(false);
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				base.Run();
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ using System.Linq;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Android.App;
 | 
			
		||||
using keepass2android.database.edit;
 | 
			
		||||
using KeePassLib;
 | 
			
		||||
using KeePassLib.Keys;
 | 
			
		||||
using KeePassLib.Serialization;
 | 
			
		||||
@@ -36,20 +37,25 @@ namespace keepass2android
 | 
			
		||||
		private readonly bool _rememberKeyfile;
 | 
			
		||||
		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;
 | 
			
		||||
			_ioc = ioc;
 | 
			
		||||
			_databaseData = databaseData;
 | 
			
		||||
			_compositeKey = compositeKey;
 | 
			
		||||
			_keyfileOrProvider = keyfileOrProvider;
 | 
			
		||||
		    _updateLastUsageTimestamp = updateLastUsageTimestamp;
 | 
			
		||||
		    _makeCurrent = makeCurrent;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile); 
 | 
			
		||||
		    _rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile); 
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		public override void Run()
 | 
			
		||||
 | 
			
		||||
	    protected bool success = false;
 | 
			
		||||
	    private bool _updateLastUsageTimestamp;
 | 
			
		||||
	    private readonly bool _makeCurrent;
 | 
			
		||||
 | 
			
		||||
	    public override void Run()
 | 
			
		||||
		{
 | 
			
		||||
			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:
 | 
			
		||||
					_format = new KdbxDatabaseFormat(KdbpFile.GetFormatToUse(_ioc));
 | 
			
		||||
					TryLoad(databaseStream);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
				    success = true;
 | 
			
		||||
				}
 | 
			
		||||
				catch (Exception e)
 | 
			
		||||
				{
 | 
			
		||||
@@ -89,7 +99,7 @@ namespace keepass2android
 | 
			
		||||
			{
 | 
			
		||||
				Kp2aLog.Log("KeyFileException");
 | 
			
		||||
				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)
 | 
			
		||||
			{
 | 
			
		||||
@@ -100,20 +110,20 @@ namespace keepass2android
 | 
			
		||||
					// Override the message shown with the last (hopefully most recent) inner exception
 | 
			
		||||
					Kp2aLog.LogUnexpectedError(innerException);
 | 
			
		||||
				}
 | 
			
		||||
				Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + message, Exception);
 | 
			
		||||
				Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + message, false, Exception);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			catch (DuplicateUuidsException e)
 | 
			
		||||
			{
 | 
			
		||||
				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;
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception e)
 | 
			
		||||
			{
 | 
			
		||||
				if (!(e is InvalidCompositeKeyException))
 | 
			
		||||
					Kp2aLog.LogUnexpectedError(e);
 | 
			
		||||
				Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message, Exception);
 | 
			
		||||
				Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message, false, Exception);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
@@ -125,7 +135,7 @@ namespace keepass2android
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		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
 | 
			
		||||
			//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:
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
                _app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format);
 | 
			
		||||
                Database newDb = _app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format, _makeCurrent);
 | 
			
		||||
				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);
 | 
			
		||||
			    return newDb;
 | 
			
		||||
			}
 | 
			
		||||
			catch (OldFormatException)
 | 
			
		||||
			{
 | 
			
		||||
				_format = new KdbDatabaseFormat(_app);
 | 
			
		||||
				TryLoad(databaseStream);
 | 
			
		||||
				return TryLoad(databaseStream);
 | 
			
		||||
			}
 | 
			
		||||
			catch (InvalidCompositeKeyException)
 | 
			
		||||
			{
 | 
			
		||||
@@ -162,7 +169,7 @@ namespace keepass2android
 | 
			
		||||
					//retry without password:
 | 
			
		||||
					_compositeKey.RemoveUserKey(passwordKey);
 | 
			
		||||
					//retry:
 | 
			
		||||
					TryLoad(databaseStream);
 | 
			
		||||
					return TryLoad(databaseStream);
 | 
			
		||||
				}
 | 
			
		||||
				else throw;
 | 
			
		||||
			}
 | 
			
		||||
@@ -175,7 +182,7 @@ namespace keepass2android
 | 
			
		||||
            {
 | 
			
		||||
                keyfileOrProvider = "";
 | 
			
		||||
            }
 | 
			
		||||
            _app.StoreOpenedFileAsRecent(ioc, keyfileOrProvider);
 | 
			
		||||
            _app.StoreOpenedFileAsRecent(ioc, keyfileOrProvider, _updateLastUsageTimestamp);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
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)
 | 
			
		||||
		    {
 | 
			
		||||
 | 
			
		||||
                _app.GetDb().Dirty.Add(elementToMove.ParentGroup);
 | 
			
		||||
                _app.DirtyGroups.Add(elementToMove.ParentGroup);
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                PwGroup pgParent = elementToMove.ParentGroup;
 | 
			
		||||
                if (pgParent != _targetGroup)
 | 
			
		||||
@@ -63,8 +73,14 @@ namespace keepass2android.database.edit
 | 
			
		||||
                        PwEntry entry = elementToMove as PwEntry;
 | 
			
		||||
                        if (entry != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            var dbRem = _app.FindDatabaseForElement(entry);
 | 
			
		||||
                            removeDatabases.Add(dbRem);
 | 
			
		||||
                            dbRem.EntriesById.Remove(entry.Uuid);
 | 
			
		||||
                            dbRem.Elements.Remove(entry);
 | 
			
		||||
                            pgParent.Entries.Remove(entry);
 | 
			
		||||
                            _targetGroup.AddEntry(entry, true, true);
 | 
			
		||||
                            addDatabase.EntriesById.Add(entry.Uuid, entry);
 | 
			
		||||
                            addDatabase.Elements.Add(entry);
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
@@ -74,27 +90,60 @@ namespace keepass2android.database.edit
 | 
			
		||||
                                Finish(false, _app.GetResourceString(UiStringKey.CannotMoveGroupHere));
 | 
			
		||||
                                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);
 | 
			
		||||
                            _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 Android;
 | 
			
		||||
using Android.App;
 | 
			
		||||
using Android.Content;
 | 
			
		||||
using Android.OS;
 | 
			
		||||
@@ -28,8 +29,14 @@ namespace keepass2android
 | 
			
		||||
		protected bool Success;
 | 
			
		||||
		protected String Message;
 | 
			
		||||
		protected Exception Exception;
 | 
			
		||||
		
 | 
			
		||||
		protected OnFinish BaseOnFinish;
 | 
			
		||||
 | 
			
		||||
	    protected bool ImportantMessage
 | 
			
		||||
	    {
 | 
			
		||||
	        get;
 | 
			
		||||
	        set;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
        protected OnFinish BaseOnFinish;
 | 
			
		||||
		protected Handler Handler;
 | 
			
		||||
		private ProgressDialogStatusLogger _statusLogger = new ProgressDialogStatusLogger(); //default: no logging but not null -> can be used whenever desired
 | 
			
		||||
	    private Activity _activeActivity;
 | 
			
		||||
@@ -77,20 +84,22 @@ namespace keepass2android
 | 
			
		||||
			Handler = null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void SetResult(bool success, string message, Exception exception) {
 | 
			
		||||
		public void SetResult(bool success, string message, bool importantMessage, Exception exception) {
 | 
			
		||||
			Success = success;
 | 
			
		||||
			Message = message;
 | 
			
		||||
		    ImportantMessage = importantMessage;
 | 
			
		||||
			Exception = exception;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public void SetResult(bool success) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	    public void SetResult(bool success) {
 | 
			
		||||
			Success = success;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		public virtual void Run() {
 | 
			
		||||
			if (BaseOnFinish == null) return;
 | 
			
		||||
			// Pass on result on call finish
 | 
			
		||||
			BaseOnFinish.SetResult(Success, Message, Exception);
 | 
			
		||||
			BaseOnFinish.SetResult(Success, Message, ImportantMessage, Exception);
 | 
			
		||||
				
 | 
			
		||||
			if ( Handler != null ) {
 | 
			
		||||
				Handler.Post(BaseOnFinish.Run); 
 | 
			
		||||
@@ -100,14 +109,31 @@ namespace keepass2android
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		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) ) {
 | 
			
		||||
				Kp2aLog.Log("OnFinish message: "+message);
 | 
			
		||||
				Toast.MakeText(ctx ?? Application.Context, message, ToastLength.Long).Show();
 | 
			
		||||
			    Kp2aLog.Log("OnFinish message: " + message);
 | 
			
		||||
                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 ) {
 | 
			
		||||
				OnFinishToRun.SetResult(result, message, exception);
 | 
			
		||||
				OnFinishToRun.SetResult(result, message, importantMessage, exception);
 | 
			
		||||
				OnFinishToRun.Run();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,8 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
	public class SaveDb : RunnableOnFinish {
 | 
			
		||||
		private readonly IKp2aApp _app;
 | 
			
		||||
		private readonly bool _dontSave;
 | 
			
		||||
	    private readonly Database _db;
 | 
			
		||||
	    private readonly bool _dontSave;
 | 
			
		||||
 | 
			
		||||
		/// <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
 | 
			
		||||
@@ -43,9 +44,10 @@ namespace keepass2android
 | 
			
		||||
		private readonly Context _ctx;
 | 
			
		||||
		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)
 | 
			
		||||
		{
 | 
			
		||||
		    _db = db;
 | 
			
		||||
			_ctx = ctx;
 | 
			
		||||
			_app = app;
 | 
			
		||||
			_dontSave = dontSave;
 | 
			
		||||
@@ -59,46 +61,55 @@ namespace keepass2android
 | 
			
		||||
		/// <param name="finish"></param>
 | 
			
		||||
		/// <param name="dontSave"></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)
 | 
			
		||||
		{
 | 
			
		||||
		    _db = db;
 | 
			
		||||
			_ctx = ctx;
 | 
			
		||||
			_app = app;
 | 
			
		||||
			_dontSave = dontSave;
 | 
			
		||||
			_streamForOrigFile = streamForOrigFile;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish)
 | 
			
		||||
		public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish)
 | 
			
		||||
			: base(ctx, finish)
 | 
			
		||||
		{
 | 
			
		||||
			_ctx = ctx;
 | 
			
		||||
			_app = app;
 | 
			
		||||
			_dontSave = false;
 | 
			
		||||
		    _db = db;
 | 
			
		||||
		    _dontSave = false;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		public override void Run ()
 | 
			
		||||
 | 
			
		||||
	    public bool ShowDatabaseIocInStatus { get; set; }
 | 
			
		||||
	    
 | 
			
		||||
	    public override void Run ()
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
			if (!_dontSave)
 | 
			
		||||
			{
 | 
			
		||||
				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.
 | 
			
		||||
						Finish(false,"Cannot save changes. File is read-only!");
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					StatusLogger.UpdateMessage(UiStringKey.saving_database);
 | 
			
		||||
					IOConnectionInfo ioc = _app.GetDb().Ioc;
 | 
			
		||||
				    string message = _app.GetResourceString(UiStringKey.saving_database);
 | 
			
		||||
 | 
			
		||||
				    if (ShowDatabaseIocInStatus)
 | 
			
		||||
				        message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
 | 
			
		||||
 | 
			
		||||
                    StatusLogger.UpdateMessage(message);
 | 
			
		||||
                    
 | 
			
		||||
					IOConnectionInfo ioc = _db.Ioc;
 | 
			
		||||
					IFileStorage fileStorage = _app.GetFileStorage(ioc);
 | 
			
		||||
 | 
			
		||||
					if (_streamForOrigFile == null)
 | 
			
		||||
					{
 | 
			
		||||
						if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave))
 | 
			
		||||
							|| (_app.GetDb().KpDatabase.HashOfFileOnDisk == null)) //first time saving
 | 
			
		||||
							|| (_db.KpDatabase.HashOfFileOnDisk == null)) //first time saving
 | 
			
		||||
						{
 | 
			
		||||
							PerformSaveWithoutCheck(fileStorage, ioc);
 | 
			
		||||
							Finish(true);
 | 
			
		||||
@@ -109,8 +120,8 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
					if (
 | 
			
		||||
						(_streamForOrigFile != null)
 | 
			
		||||
						|| fileStorage.CheckForFileChangeFast(ioc, _app.GetDb().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:
 | 
			
		||||
						|| fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion)  //first try to use the fast change detection
 | 
			
		||||
						|| (FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) == FileHashChange.Changed) //if that fails, hash the file and compare:
 | 
			
		||||
						)
 | 
			
		||||
					{
 | 
			
		||||
 | 
			
		||||
@@ -128,6 +139,7 @@ namespace keepass2android
 | 
			
		||||
										//small.
 | 
			
		||||
										MergeIn(fileStorage, ioc);
 | 
			
		||||
										PerformSaveWithoutCheck(fileStorage, ioc);
 | 
			
		||||
                                        _db.UpdateGlobals();
 | 
			
		||||
										Finish(true);
 | 
			
		||||
									};
 | 
			
		||||
								RunInWorkerThread(runHandler);
 | 
			
		||||
@@ -217,13 +229,13 @@ namespace keepass2android
 | 
			
		||||
			StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.SynchronizingDatabase));
 | 
			
		||||
 | 
			
		||||
			PwDatabase pwImp = new PwDatabase();
 | 
			
		||||
			PwDatabase pwDatabase = _app.GetDb().KpDatabase;
 | 
			
		||||
			PwDatabase pwDatabase = _db.KpDatabase;
 | 
			
		||||
			pwImp.New(new IOConnectionInfo(), pwDatabase.MasterKey);
 | 
			
		||||
			pwImp.MemoryProtection = pwDatabase.MemoryProtection.CloneDeep();
 | 
			
		||||
			pwImp.MasterKey = pwDatabase.MasterKey;
 | 
			
		||||
			var stream = GetStreamForBaseFile(fileStorage, ioc);
 | 
			
		||||
 | 
			
		||||
			_app.GetDb().DatabaseFormat.PopulateDatabaseFromStream(pwImp, stream, null);
 | 
			
		||||
			_db.DatabaseFormat.PopulateDatabaseFromStream(pwImp, stream, null);
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
			pwDatabase.MergeIn(pwImp, PwMergeMethod.Synchronize, null); 
 | 
			
		||||
@@ -249,8 +261,8 @@ namespace keepass2android
 | 
			
		||||
		private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
 | 
			
		||||
		{
 | 
			
		||||
			StatusLogger.UpdateSubMessage("");
 | 
			
		||||
			_app.GetDb().SaveData();
 | 
			
		||||
			_app.GetDb().LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
 | 
			
		||||
			_db.SaveData();
 | 
			
		||||
			_db.LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public byte[] HashOriginalFile(IOConnectionInfo iocFile)
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@ namespace keepass2android
 | 
			
		||||
		public override void Run ()
 | 
			
		||||
		{
 | 
			
		||||
			StatusLogger.UpdateMessage(UiStringKey.SettingPassword);
 | 
			
		||||
			PwDatabase pm = _app.GetDb().KpDatabase;
 | 
			
		||||
			PwDatabase pm = _app.CurrentDb.KpDatabase;
 | 
			
		||||
			CompositeKey newKey = new CompositeKey ();
 | 
			
		||||
			if (String.IsNullOrEmpty (_password) == false) {
 | 
			
		||||
				newKey.AddUserKey (new KcpPassword (_password)); 
 | 
			
		||||
@@ -74,7 +74,7 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
			// Save Database
 | 
			
		||||
			_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.Run();
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ namespace keepass2android
 | 
			
		||||
		
 | 
			
		||||
		public override void Run() {
 | 
			
		||||
			// Commit to disk
 | 
			
		||||
			SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
 | 
			
		||||
			SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
 | 
			
		||||
			save.SetStatusLogger(StatusLogger);
 | 
			
		||||
			save.Run();
 | 
			
		||||
		}
 | 
			
		||||
@@ -59,7 +59,7 @@ namespace keepass2android
 | 
			
		||||
					if ( parent != null ) {
 | 
			
		||||
 | 
			
		||||
						// Mark parent group dirty
 | 
			
		||||
						_app.GetDb().Dirty.Add(parent);
 | 
			
		||||
						_app.DirtyGroups.Add(parent);
 | 
			
		||||
							
 | 
			
		||||
					}
 | 
			
		||||
					
 | 
			
		||||
 
 | 
			
		||||
@@ -67,5 +67,4 @@
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <Folder Include="libs\" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  
 | 
			
		||||
</Project>
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -53,8 +53,8 @@
 | 
			
		||||
    <Compile Include="Properties\AssemblyInfo.cs" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <LibraryProjectZip Include="..\java\Keepass2AndroidPluginSDK2\app\build\outputs\aar\app-release.aar">
 | 
			
		||||
      <Link>Jars\app-release.aar</Link>
 | 
			
		||||
    <LibraryProjectZip Include="..\java\Keepass2AndroidPluginSDK2\app\build\outputs\aar\Keepass2AndroidPluginSDK2-release.aar">
 | 
			
		||||
      <Link>Jars\Keepass2AndroidPluginSDK2-release.aar</Link>
 | 
			
		||||
    </LibraryProjectZip>
 | 
			
		||||
    <None Include="Jars\AboutJars.txt" />
 | 
			
		||||
    <None Include="Additions\AboutAdditions.txt" />
 | 
			
		||||
 
 | 
			
		||||
@@ -7,4 +7,7 @@ call gradlew assemble
 | 
			
		||||
cd ..\Keepass2AndroidPluginSDK2
 | 
			
		||||
call gradlew assemble
 | 
			
		||||
 | 
			
		||||
cd ..\..\build-scripts
 | 
			
		||||
cd ..\PluginQR
 | 
			
		||||
call gradlew assemble
 | 
			
		||||
 | 
			
		||||
cd ..\..\build-scripts
 | 
			
		||||
 
 | 
			
		||||
@@ -15,4 +15,8 @@ pushd Keepass2AndroidPluginSDK2
 | 
			
		||||
./gradlew assemble
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
pushd PluginQR
 | 
			
		||||
./gradlew assemble
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
popd
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,10 @@ dependencies {
 | 
			
		||||
    }
 | 
			
		||||
    compile 'com.pcloud.sdk:java-core: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.microsoft.services.msa:msa-auth:0.8.6'
 | 
			
		||||
    compile 'com.microsoft.aad:adal:1.14.0'
 | 
			
		||||
 
 | 
			
		||||
@@ -190,12 +190,14 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
 | 
			
		||||
			String part = parts[parts.length-1];
 | 
			
		||||
			logDebug("parsing part " + part);
 | 
			
		||||
			int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP);
 | 
			
		||||
			String name = "";
 | 
			
		||||
			if (indexOfSeparator < 0)
 | 
			
		||||
			{
 | 
			
		||||
				//seems invalid, but we're very generous here
 | 
			
		||||
				displayName += "/"+part;
 | 
			
		||||
			}
 | 
			
		||||
			String name = part.substring(0, indexOfSeparator);
 | 
			
		||||
			else
 | 
			
		||||
				name = part.substring(0, indexOfSeparator);
 | 
			
		||||
			try {
 | 
			
		||||
				name = decode(name);
 | 
			
		||||
			} 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.List;
 | 
			
		||||
import java.util.NoSuchElementException;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import com.pcloud.sdk.ApiClient;
 | 
			
		||||
@@ -52,7 +53,7 @@ public class PCloudFileStorage extends JavaFileStorageBase
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean requiresSetup(String path) {
 | 
			
		||||
        return true;
 | 
			
		||||
        return !this.isConnected();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -135,8 +136,10 @@ public class PCloudFileStorage extends JavaFileStorageBase
 | 
			
		||||
        String filePath = path.substring(0, path.lastIndexOf("/") + 1);
 | 
			
		||||
        RemoteFolder remoteFolder = this.getRemoteFolderByPath(filePath);
 | 
			
		||||
 | 
			
		||||
        String tempName = "." + UUID.randomUUID().toString();
 | 
			
		||||
        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) {
 | 
			
		||||
            throw convertApiError(e);
 | 
			
		||||
        }
 | 
			
		||||
@@ -372,7 +375,7 @@ public class PCloudFileStorage extends JavaFileStorageBase
 | 
			
		||||
 | 
			
		||||
    private Exception convertApiError(ApiError e) {
 | 
			
		||||
        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();
 | 
			
		||||
            return new UserInteractionRequiredException("Unlinked from PCloud! User must re-link.", e);
 | 
			
		||||
        } else if (strErrorCode.startsWith("2")) {
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,6 @@ import com.jcraft.jsch.UserInfo;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
public class SftpStorage extends JavaFileStorageBase {
 | 
			
		||||
 | 
			
		||||
@@ -358,12 +357,10 @@ public class SftpStorage extends JavaFileStorageBase {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@NonNull
 | 
			
		||||
	private String getBaseDir() {
 | 
			
		||||
		return _appContext.getFilesDir().getAbsolutePath();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@NonNull
 | 
			
		||||
	private String getKeyFileName() {
 | 
			
		||||
		return getBaseDir() + "/id_kp2a_rsa";
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ import java.util.Date;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
import javax.net.ssl.SSLContext;
 | 
			
		||||
import javax.net.ssl.SSLSocketFactory;
 | 
			
		||||
@@ -70,9 +71,18 @@ public class WebDavStorage extends JavaFileStorageBase {
 | 
			
		||||
 | 
			
		||||
        String scheme = filename.substring(0, filename.indexOf("://"));
 | 
			
		||||
        filename = filename.substring(scheme.length() + 3);
 | 
			
		||||
        String userPwd = filename.substring(0, filename.indexOf('@'));
 | 
			
		||||
        ci.username = decode(userPwd.substring(0, userPwd.indexOf(":")));
 | 
			
		||||
        ci.password = decode(userPwd.substring(userPwd.indexOf(":") + 1));
 | 
			
		||||
        int idxAt = filename.indexOf('@');
 | 
			
		||||
        if (idxAt >= 0)
 | 
			
		||||
        {
 | 
			
		||||
            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);
 | 
			
		||||
        return ci;
 | 
			
		||||
    }
 | 
			
		||||
@@ -149,13 +159,19 @@ public class WebDavStorage extends JavaFileStorageBase {
 | 
			
		||||
            sslContext.init(null, new TrustManager[] { trustManager }, null);
 | 
			
		||||
            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            builder = builder.sslSocketFactory(sslSocketFactory, trustManager)
 | 
			
		||||
                             .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();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        versionCode 1
 | 
			
		||||
        versionName "1.0"
 | 
			
		||||
        multiDexEnabled true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    buildTypes {
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,18 @@
 | 
			
		||||
                <category android:name="android.intent.category.DEFAULT" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </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>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -68,6 +80,8 @@
 | 
			
		||||
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
 | 
			
		||||
    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
 | 
			
		||||
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
@@ -89,6 +89,8 @@ extends Activity implements JavaFileStorage.FileStorageSetupActivity {
 | 
			
		||||
	@Override
 | 
			
		||||
	public String getPath() {
 | 
			
		||||
		// TODO Auto-generated method stub
 | 
			
		||||
		if (getState().containsKey(JavaFileStorage.EXTRA_PATH))
 | 
			
		||||
			return getState().getString(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.providers.BaseFileProviderUtils;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
@@ -146,11 +145,9 @@ import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
//import keepass2android.javafilestorage.DropboxCloudRailStorage;
 | 
			
		||||
import keepass2android.javafilestorage.DropboxV2Storage;
 | 
			
		||||
import keepass2android.javafilestorage.ICertificateErrorHandler;
 | 
			
		||||
import keepass2android.javafilestorage.JavaFileStorage;
 | 
			
		||||
import keepass2android.javafilestorage.JavaFileStorage.FileEntry;
 | 
			
		||||
import keepass2android.javafilestorage.OneDriveStorage;
 | 
			
		||||
import keepass2android.javafilestorage.OneDriveStorage2;
 | 
			
		||||
import keepass2android.javafilestorage.SftpStorage;
 | 
			
		||||
import keepass2android.javafilestorage.UserInteractionRequiredException;
 | 
			
		||||
import keepass2android.javafilestorage.WebDavStorage;
 | 
			
		||||
@@ -206,10 +203,17 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
 | 
			
		||||
				}
 | 
			
		||||
				catch (Exception e)
 | 
			
		||||
				{
 | 
			
		||||
					Log.d("KP2AJ",e.toString());
 | 
			
		||||
					//if exception because folder exists
 | 
			
		||||
					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 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) {
 | 
			
		||||
		storageToTest = new SftpStorage(ctx.getApplicationContext());
 | 
			
		||||
		//storageToTest = new SftpStorage(ctx.getApplicationContext());
 | 
			
		||||
		//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 WebDavStorage(new ICertificateErrorHandler() {
 | 
			
		||||
			@Override
 | 
			
		||||
@@ -620,7 +626,7 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
 | 
			
		||||
		Toast.makeText(this, "requestCode: "+requestCode, Toast.LENGTH_LONG).show();
 | 
			
		||||
		if (requestCode == 1)
 | 
			
		||||
			//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
 | 
			
		||||
		if (requestCode == 2)
 | 
			
		||||
		{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
 | 
			
		||||
    <string name="msalPrefix">msal8374f801-0f55-407d-80cc-9a04fe86d9b2</string>
 | 
			
		||||
    <string name="action_settings">Settings</string>
 | 
			
		||||
    <string name="hello_world">Hello world!</string>
 | 
			
		||||
    <string name="title_activity_file_storage_setup">FileStorageSetupActivity</string>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,14 @@ package keepass2android.softkeyboard;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.nio.ByteBuffer;
 | 
			
		||||
import java.nio.ByteOrder;
 | 
			
		||||
import java.nio.channels.Channels;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import org.xmlpull.v1.XmlPullParserException;
 | 
			
		||||
 | 
			
		||||
@@ -17,9 +21,11 @@ import android.content.pm.PackageManager;
 | 
			
		||||
import android.content.pm.ResolveInfo;
 | 
			
		||||
import android.content.pm.PackageManager.NameNotFoundException;
 | 
			
		||||
import android.content.res.Resources;
 | 
			
		||||
import android.content.res.TypedArray;
 | 
			
		||||
import android.content.res.XmlResourceParser;
 | 
			
		||||
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 {
 | 
			
		||||
    private static String TAG = "PCKeyboard";
 | 
			
		||||
    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
 | 
			
		||||
    public void onReceive(Context context, Intent intent) {
 | 
			
		||||
@@ -129,21 +175,59 @@ public class PluginManager extends BroadcastReceiver {
 | 
			
		||||
        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) {
 | 
			
		||||
        Intent dictIntent = new Intent(SOFTKEYBOARD_INTENT_DICT);
 | 
			
		||||
        List<ResolveInfo> dictPacks = packageManager.queryBroadcastReceivers(
 | 
			
		||||
        		dictIntent, PackageManager.GET_RECEIVERS);
 | 
			
		||||
        for (ResolveInfo ri : dictPacks) {
 | 
			
		||||
            ApplicationInfo appInfo = ri.activityInfo.applicationInfo;
 | 
			
		||||
            String pkgName = appInfo.packageName;
 | 
			
		||||
            boolean success = false;
 | 
			
		||||
            final String pkgName = appInfo.packageName;
 | 
			
		||||
            final boolean[] success = {false};
 | 
			
		||||
            try {
 | 
			
		||||
                Resources res = packageManager.getResourcesForApplication(appInfo);
 | 
			
		||||
                final Resources res = packageManager.getResourcesForApplication(appInfo);
 | 
			
		||||
                Log.i("KP2AK", "Found dictionary plugin package: " + 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);
 | 
			
		||||
 | 
			
		||||
                int dictResourceId = 0;
 | 
			
		||||
                String assetName = null;
 | 
			
		||||
                String lang = null;
 | 
			
		||||
                try {
 | 
			
		||||
@@ -157,12 +241,21 @@ public class PluginManager extends BroadcastReceiver {
 | 
			
		||||
                                    String convLang = SOFTKEYBOARD_LANG_MAP.get(lang);
 | 
			
		||||
                                    if (convLang != null) lang = convLang;
 | 
			
		||||
                                    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
 | 
			
		||||
                                    } 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.i(TAG, "asset=" + assetName + " lang=" + lang);
 | 
			
		||||
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
@@ -175,15 +268,55 @@ public class PluginManager extends BroadcastReceiver {
 | 
			
		||||
                    Log.e(TAG, "Dictionary XML IOException");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (assetName == null || lang == null) continue;
 | 
			
		||||
                DictPluginSpec spec = new DictPluginSpecSoftKeyboard(pkgName, assetName);
 | 
			
		||||
                mPluginDicts.put(lang, spec);
 | 
			
		||||
                Log.i("KP2AK", "Found plugin dictionary: lang=" + lang + ", pkg=" + pkgName);
 | 
			
		||||
                success = true;
 | 
			
		||||
                if (lang == null)
 | 
			
		||||
                    continue;
 | 
			
		||||
                if (assetName != null) {
 | 
			
		||||
                    DictPluginSpec spec = new DictPluginSpecSoftKeyboard(pkgName, assetName);
 | 
			
		||||
                    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) {
 | 
			
		||||
                Log.i("KP2AK", "bad");
 | 
			
		||||
            } finally {
 | 
			
		||||
                if (!success) {
 | 
			
		||||
                if (!success[0]) {
 | 
			
		||||
                    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) {
 | 
			
		||||
        Intent dictIntent = new Intent(HK_INTENT_DICT);
 | 
			
		||||
        List<ResolveInfo> dictPacks = packageManager.queryIntentActivities(dictIntent, 0);
 | 
			
		||||
        Log.i("KP2AK", "Searching for HK dictionaries. Found " + dictPacks.size() + " packages");
 | 
			
		||||
        for (ResolveInfo ri : dictPacks) {
 | 
			
		||||
            ApplicationInfo appInfo = ri.activityInfo.applicationInfo;
 | 
			
		||||
            String pkgName = appInfo.packageName;
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import java.util.Iterator;
 | 
			
		||||
 | 
			
		||||
import org.json.JSONException;
 | 
			
		||||
import org.json.JSONObject;
 | 
			
		||||
import org.json.JSONArray;
 | 
			
		||||
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
@@ -65,7 +66,16 @@ public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver {
 | 
			
		||||
		
 | 
			
		||||
		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"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="keepass2android.plugin.qr"
 | 
			
		||||
    android:versionCode="2"
 | 
			
		||||
    android:versionName="1.0.1" >
 | 
			
		||||
    package="keepass2android.plugin.qr" >
 | 
			
		||||
    
 | 
			
		||||
    <uses-permission android:name="android.permission.CAMERA"/>
 | 
			
		||||
     <uses-permission
 | 
			
		||||
@@ -15,10 +13,6 @@
 | 
			
		||||
	<uses-feature android:name="android.hardware.camera.flash" android:required="false"/>
 | 
			
		||||
	<uses-feature android:name="android.hardware.screen.portrait" android:required="false"/>
 | 
			
		||||
 | 
			
		||||
    <uses-sdk
 | 
			
		||||
        android:minSdkVersion="14"
 | 
			
		||||
        android:targetSdkVersion="19" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:allowBackup="true"
 | 
			
		||||
        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