mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Compare commits
236 Commits
2.5.0.0bet
...
2.5.0.0bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
161923c808 | ||
|
|
d5a03424d0 | ||
|
|
3e87c1ab9d | ||
|
|
a6046bc49a | ||
|
|
db98f4b30c | ||
|
|
60eff63569 | ||
|
|
b8e97ec53b | ||
|
|
c2b60b2268 | ||
|
|
cac1f923cc | ||
|
|
b4ac0ef21f | ||
|
|
8b85c22f25 | ||
|
|
582faab29d | ||
|
|
a9f6fa3441 | ||
|
|
54740c1ba2 | ||
|
|
94a8ecbdf1 | ||
|
|
46855e0b70 | ||
|
|
2d3eabe07e | ||
|
|
48bda000e9 | ||
|
|
8aea6fa2da | ||
|
|
c46aa0e5c8 | ||
|
|
825d88014d | ||
|
|
b3ff38fb35 | ||
|
|
1e4deb2182 | ||
|
|
d5acd04a19 | ||
|
|
fe73a951e3 | ||
|
|
415094154d | ||
|
|
3b88b0fea6 | ||
|
|
d8e3d52f21 | ||
|
|
8105f89088 | ||
|
|
90084ed2f7 | ||
|
|
496725a8fd | ||
|
|
72b0cdf0d7 | ||
|
|
a266d307a6 | ||
|
|
f7fe414597 | ||
|
|
5c1e9bd94f | ||
|
|
747135afae | ||
|
|
675efe3ab8 | ||
|
|
02b6191e66 | ||
|
|
1e07d6a28d | ||
|
|
ab98f03ebf | ||
|
|
4aff5e3a06 | ||
|
|
3ad4e8efe7 | ||
|
|
d04d7737c3 | ||
|
|
8de52cb51e | ||
|
|
c8cb79726b | ||
|
|
d51a227c1d | ||
|
|
94ed03ce35 | ||
|
|
304a5e946d | ||
|
|
ff64b53580 | ||
|
|
9ece794f9a | ||
|
|
b67dc22f10 | ||
|
|
8a1a29caa8 | ||
|
|
a8d07c3e82 | ||
|
|
937135bb36 | ||
|
|
10c32266c2 | ||
|
|
ad45369442 | ||
|
|
1ca770fa72 | ||
|
|
4acd11d2b2 | ||
|
|
38c264e38d | ||
|
|
8da6b882fc | ||
|
|
6e03c2ebfc | ||
|
|
a5cb5ed896 | ||
|
|
32bef7b099 | ||
|
|
af0359132e | ||
|
|
85990879de | ||
|
|
9436a4c3a5 | ||
|
|
efc786e318 | ||
|
|
ed299b35e9 | ||
|
|
0defe9401f | ||
|
|
b66678f028 | ||
|
|
b0cfd4292f | ||
|
|
8e390685cd | ||
|
|
9bcacdca4a | ||
|
|
c1969402f1 | ||
|
|
50bf22a4c7 | ||
|
|
cf93044d3f | ||
|
|
33404add38 | ||
|
|
49ba74c38f | ||
|
|
3ed5db9819 | ||
|
|
4535204d1e | ||
|
|
18c656a555 | ||
|
|
42c28b5b95 | ||
|
|
9fc47fdf05 | ||
|
|
9ef34599ce | ||
|
|
bd3e5c6917 | ||
|
|
7e9bed1e53 | ||
|
|
ff2215929c | ||
|
|
36d53e9788 | ||
|
|
6b9db7d40d | ||
|
|
30f805f5a9 | ||
|
|
64448ef218 | ||
|
|
6e312e0420 | ||
|
|
1f7b86d34f | ||
|
|
ae00fd5782 | ||
|
|
62f6f02467 | ||
|
|
55ee89314f | ||
|
|
b2f985aa03 | ||
|
|
b3a34c0138 | ||
|
|
1f06b09d38 | ||
|
|
758914a80a | ||
|
|
f2566abdcd | ||
|
|
cb6d479350 | ||
|
|
846930a4f9 | ||
|
|
e788d89b18 | ||
|
|
07f68cfe2f | ||
|
|
1c6ef51925 | ||
|
|
9995bc4d9f | ||
|
|
accb931831 | ||
|
|
42ac83c814 | ||
|
|
9ee9063c4d | ||
|
|
8c38b361ea | ||
|
|
cb919b2de5 | ||
|
|
679ea7c58f | ||
|
|
21ebbd25f8 | ||
|
|
3c232ac5b6 | ||
|
|
d36d2408d7 | ||
|
|
9fd59f850c | ||
|
|
4cc7d1e74d | ||
|
|
66f4353c3e | ||
|
|
0acc83b066 | ||
|
|
943d7ca6b9 | ||
|
|
65d1c7376b | ||
|
|
871a624313 | ||
|
|
8cf515120f | ||
|
|
30c5db92e6 | ||
|
|
19ebc40bdb | ||
|
|
4db2e6baf9 | ||
|
|
a9c1369cbf | ||
|
|
dd478d7cd4 | ||
|
|
53e7cc7f72 | ||
|
|
e5d3a0a931 | ||
|
|
23e6b12326 | ||
|
|
2cc530cbec | ||
|
|
b540cde623 | ||
|
|
5a54955941 | ||
|
|
9ffffdceca | ||
|
|
257d0ff315 | ||
|
|
a935b0a089 | ||
|
|
0236bddffd | ||
|
|
384966641a | ||
|
|
3be9c9161c | ||
|
|
a445533ffc | ||
|
|
e073b21544 | ||
|
|
7e746bb01c | ||
|
|
2d8e902a2a | ||
|
|
bb10892b76 | ||
|
|
bdfa5ae707 | ||
|
|
2091b04a11 | ||
|
|
f367c098ec | ||
|
|
98631d37ae | ||
|
|
d504ab7924 | ||
|
|
4fc8c74530 | ||
|
|
4876623f86 | ||
|
|
d5b752e4c0 | ||
|
|
a1ecc5b399 | ||
|
|
306e8b17b2 | ||
|
|
f66500f535 | ||
|
|
b92ef7cb1a | ||
|
|
b30c2656e8 | ||
|
|
0899c7fc5a | ||
|
|
800d0eb04d | ||
|
|
b09bc52b51 | ||
|
|
657c810c1f | ||
|
|
c501d6bdc6 | ||
|
|
f13c595bf9 | ||
|
|
60e2209281 | ||
|
|
24eeff1d61 | ||
|
|
c94291f6e1 | ||
|
|
6faee3cef9 | ||
|
|
bb4e067394 | ||
|
|
81503c6934 | ||
|
|
8858a5cdca | ||
|
|
d994fbafcf | ||
|
|
c3954faa3e | ||
|
|
0650a6f7db | ||
|
|
0ddd08bf6d | ||
|
|
a1720c1c79 | ||
|
|
cd9f82696a | ||
|
|
242427d348 | ||
|
|
3f1ab3623d | ||
|
|
7068b4b4b3 | ||
|
|
7c81685aa6 | ||
|
|
f7fc7984e2 | ||
|
|
b2ef29d131 | ||
|
|
d5dcf697f6 | ||
|
|
9731247f2e | ||
|
|
c2654cd65c | ||
|
|
e032540c0b | ||
|
|
6dda7d1e64 | ||
|
|
f67df77dff | ||
|
|
ecdadaf0bc | ||
|
|
25fb85a5ef | ||
|
|
31426268ea | ||
|
|
f0b5b1bcb5 | ||
|
|
1f2ebf0825 | ||
|
|
fa0afbe947 | ||
|
|
70967a4234 | ||
|
|
87c9aeeb12 | ||
|
|
0a386c3985 | ||
|
|
bfcd4b9f00 | ||
|
|
3d6082a5d9 | ||
|
|
aceeb581d4 | ||
|
|
d46a6a2ea8 | ||
|
|
c1bf96ac5f | ||
|
|
a1bf5f1e70 | ||
|
|
7155e25c1e | ||
|
|
5c4d2e607a | ||
|
|
79750c5320 | ||
|
|
bb353bc9d6 | ||
|
|
5818762aaf | ||
|
|
e5467bc54b | ||
|
|
e574fba0a5 | ||
|
|
82b59662f3 | ||
|
|
260ce95509 | ||
|
|
23df28cb25 | ||
|
|
a68960a30f | ||
|
|
f295aac206 | ||
|
|
d568604117 | ||
|
|
795d6fa334 | ||
|
|
d788d16020 | ||
|
|
c8db1f7c5c | ||
|
|
ac67ff9f21 | ||
|
|
3a71f635f0 | ||
|
|
a9e12cb518 | ||
|
|
b0aac43d6c | ||
|
|
f0d7249679 | ||
|
|
32fb536a92 | ||
|
|
8f8361a176 | ||
|
|
06056128e5 | ||
|
|
259c31b94c | ||
|
|
ffffeb9e85 | ||
|
|
ebe8c90238 | ||
|
|
25f657e665 | ||
|
|
ffd40a5419 | ||
|
|
8f8b265d97 | ||
|
|
d20eef0922 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -75,3 +75,4 @@ art/logo_512.png
|
||||
# Dir linux
|
||||
.directory
|
||||
*/.directory
|
||||
.idea
|
||||
|
||||
22
.idea/compiler.xml
generated
22
.idea/compiler.xml
generated
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.form" />
|
||||
<entry name="!?*.class" />
|
||||
<entry name="!?*.groovy" />
|
||||
<entry name="!?*.scala" />
|
||||
<entry name="!?*.flex" />
|
||||
<entry name="!?*.kt" />
|
||||
<entry name="!?*.clj" />
|
||||
<entry name="!?*.aj" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
3
.idea/copyright/profiles_settings.xml
generated
3
.idea/copyright/profiles_settings.xml
generated
@@ -1,3 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings default="" />
|
||||
</component>
|
||||
6
.idea/encodings.xml
generated
6
.idea/encodings.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
20
.idea/gradle.xml
generated
20
.idea/gradle.xml
generated
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="LOCAL" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.14.1" />
|
||||
<option name="gradleJvm" value="1.8" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
11
.idea/inspectionProfiles/Project_Default.xml
generated
11
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,11 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="AndroidLintAppCompatResource" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="AndroidLintMissingTranslation" enabled="true" level="INFO" enabled_by_default="true" />
|
||||
<inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
|
||||
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
7
.idea/inspectionProfiles/profiles_settings.xml
generated
7
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -1,7 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" value="Project Default" />
|
||||
<option name="USE_PROJECT_PROFILE" value="true" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
49
.idea/misc.xml
generated
49
.idea/misc.xml
generated
@@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
<component name="masterDetails">
|
||||
<states>
|
||||
<state key="ProjectJDKs.UI">
|
||||
<settings>
|
||||
<last-edited>1.7</last-edited>
|
||||
<splitter-proportions>
|
||||
<option name="proportions">
|
||||
<list>
|
||||
<option value="0.2" />
|
||||
</list>
|
||||
</option>
|
||||
</splitter-proportions>
|
||||
</settings>
|
||||
</state>
|
||||
</states>
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/modules.xml
generated
9
.idea/modules.xml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/KeePassDX.iml" filepath="$PROJECT_DIR$/KeePassDX.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/runConfigurations.xml
generated
12
.idea/runConfigurations.xml
generated
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
43
CHANGELOG
43
CHANGELOG
@@ -1,3 +1,46 @@
|
||||
KeepassDX (2.5.0.0beta8)
|
||||
* Hide custom entries protected
|
||||
* Best management of field references (https://keepass.info/help/base/fieldrefs.html)
|
||||
* Change database / default settings
|
||||
* Add Autofill for search
|
||||
* Add sorting by last access and by creation time
|
||||
* Rebuild custom entries
|
||||
* Refactor old code
|
||||
* Fix bugs
|
||||
|
||||
KeepassDX (2.5.0.0beta7)
|
||||
* Rebuild Notifications
|
||||
* Change links to https
|
||||
* Add extended Ascii (ñæËÌÂÝÜ...)
|
||||
* Upgrade custom visibility font
|
||||
* Best fingerprint error management
|
||||
* Add setting to prevent the password copy
|
||||
* Fix bugs
|
||||
|
||||
KeepassDX (2.5.0.0beta6)
|
||||
* Fix crash
|
||||
|
||||
KeepassDX (2.5.0.0beta5)
|
||||
* Autofill (Android O)
|
||||
* Deletion for group
|
||||
* New sorts with (Asc/Dsc, Groups before or after)
|
||||
* Better permission management with dialog at runtime
|
||||
* Setting to change font of field (monospace for a better visibility)
|
||||
* Open kdbx and kdb files from file browser
|
||||
* Change sort of fields
|
||||
* Hide empty fields
|
||||
* Add copy button for Username / Password and extra field
|
||||
* Add 5, 10, 20 seconds and 15 minutes of clipboard timeout
|
||||
* Hide "show password" icon when password not present
|
||||
* New animations for add button
|
||||
* New list to add and delete node with animation
|
||||
* Change view for better cohesion
|
||||
* Upgrade translations
|
||||
* Fix crash for API < Kitkat
|
||||
* Fix fingerprint bugs
|
||||
* Fix many small bugs
|
||||
* Add recycle bin setting (not yet accessible)
|
||||
|
||||
KeepassDX (2.5.0.0beta4)
|
||||
* Show only file name
|
||||
* Setting for full path
|
||||
|
||||
339
COPYING.gpl-2.0
339
COPYING.gpl-2.0
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
16
LICENSE
16
LICENSE
@@ -1,7 +1,19 @@
|
||||
---
|
||||
|
||||
The KeePass icon was created by Jeremy JAMET and is licensed under the terms of GPLv3.
|
||||
KeePass DX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The KeePass DX icon was created by Jeremy JAMET and is licensed under the terms of GPLv3.
|
||||
|
||||
---
|
||||
|
||||
@@ -54,7 +66,7 @@ Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft <contact@kunzisoft.com>.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
|
||||
37
ReadMe.md
37
ReadMe.md
@@ -1,18 +1,19 @@
|
||||
# Android Keepass DX
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/logo.png"> Keepass DX is a material design Keepass Client for manage keys and passwords in crypt database for your android device.
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/fastlane/metadata/android/en-US/images/icon.png"> Keepass DX is a material design Keepass Client for manage keys and passwords in crypt database for your android device.
|
||||
|
||||
### Features
|
||||
|
||||
- Simplified creation of the database file
|
||||
- Create entries and groups
|
||||
- Open database, copy username / password, open URI / URL
|
||||
- Fingerprint for fast unlocking
|
||||
- Material design with themes
|
||||
- Device integration and AutoFill (In progress)
|
||||
* Create database files / entries and groups
|
||||
* Support for .kdb and .kdbx files (version 1 to 4)
|
||||
* Open database, copy username / password, open URI / URL
|
||||
* Fingerprint for fast unlocking
|
||||
* Material design with themes
|
||||
* AutoFill and Integration (Development in progress)
|
||||
* Precise management of settings
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen1.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen2.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screen1.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screen2.jpg" width="220">
|
||||
|
||||
## What is KeePass?
|
||||
|
||||
@@ -32,8 +33,8 @@ Even if the application is free, to maintain the application, you can make donat
|
||||
|
||||
[](https://liberapay.com/Kunzisoft/donate "Kunzisoft Liberapay Donation")
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen4.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen5.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screen4.jpg" width="220">
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screen5.jpg" width="220">
|
||||
|
||||
## Download
|
||||
|
||||
@@ -45,16 +46,6 @@ Even if the application is free, to maintain the application, you can make donat
|
||||
alt="Get it on Google Play"
|
||||
height="80">](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.free)
|
||||
|
||||
### JNI
|
||||
|
||||
Native library build instructions:
|
||||
1. Make sure you have the latest MIPS Android NDK installed:
|
||||
https://developer.android.com/tools/sdk/ndk/index.html
|
||||
2. From KeePassDroid/app/src/main/jni, call prep_build.sh to download and unpack the crypto sources.
|
||||
3. The standard gradle files build everything now.
|
||||
|
||||
This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid) by bpellin.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2017 Jeremy Jamet / Kunzisoft.
|
||||
@@ -63,7 +54,7 @@ This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid
|
||||
|
||||
KeePass DX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -73,3 +64,5 @@ This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
This project is a fork of [KeepassDroid](https://github.com/bpellin/keepassdroid) by bpellin.
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion = 25
|
||||
buildToolsVersion = "27.0.1"
|
||||
compileSdkVersion = 27
|
||||
buildToolsVersion = '27.0.3'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.kunzisoft.keepass"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 25
|
||||
versionCode = 4
|
||||
versionName = "2.5.0.0beta4"
|
||||
targetSdkVersion 27
|
||||
versionCode = 8
|
||||
versionName = "2.5.0.0beta8"
|
||||
multiDexEnabled true
|
||||
|
||||
testApplicationId = "com.keepassdroid.tests"
|
||||
testApplicationId = "com.kunzisoft.keepass.tests"
|
||||
testInstrumentationRunner = "android.test.InstrumentationTestRunner"
|
||||
|
||||
ndk {
|
||||
@@ -58,21 +59,38 @@ android {
|
||||
buildConfigField "boolean", "GOOGLE_PLAY_VERSION", "true"
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
targetCompatibility 1.8
|
||||
sourceCompatibility 1.8
|
||||
}
|
||||
}
|
||||
|
||||
def supportVersion = "25.4.0"
|
||||
def supportVersion = "27.1.0"
|
||||
def spongycastleVersion = "1.58.0.0"
|
||||
def permissionDispatcherVersion = "3.1.0"
|
||||
|
||||
dependencies {
|
||||
androidTestCompile "junit:junit:4.12"
|
||||
compile "com.android.support:appcompat-v7:$supportVersion"
|
||||
compile "com.android.support:design:$supportVersion"
|
||||
compile "com.android.support:preference-v7:$supportVersion"
|
||||
compile "com.android.support:preference-v14:$supportVersion"
|
||||
compile "com.android.support:cardview-v7:$supportVersion"
|
||||
compile "com.madgag.spongycastle:core:$spongycastleVersion"
|
||||
compile "com.madgag.spongycastle:prov:$spongycastleVersion"
|
||||
compile "joda-time:joda-time:2.9.9"
|
||||
compile "org.sufficientlysecure:html-textview:3.5"
|
||||
compile "com.nononsenseapps:filepicker:4.1.0"
|
||||
implementation "com.android.support:appcompat-v7:$supportVersion"
|
||||
implementation "com.android.support:design:$supportVersion"
|
||||
implementation "com.android.support:preference-v7:$supportVersion"
|
||||
implementation "com.android.support:preference-v14:$supportVersion"
|
||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||
implementation "com.madgag.spongycastle:core:$spongycastleVersion"
|
||||
implementation "com.madgag.spongycastle:prov:$spongycastleVersion"
|
||||
// Time
|
||||
implementation "joda-time:joda-time:2.9.9"
|
||||
implementation "org.sufficientlysecure:html-textview:3.5"
|
||||
implementation "com.nononsenseapps:filepicker:4.1.0"
|
||||
// Permissions
|
||||
implementation ("com.github.hotchemi:permissionsdispatcher:$permissionDispatcherVersion") {
|
||||
// if you don't use android.app.Fragment you can exclude support for them
|
||||
exclude module: "support-v13"
|
||||
}
|
||||
// Apache Commons Collections
|
||||
implementation group: 'commons-collections', name: 'commons-collections', version: '3.2.1'
|
||||
// Base64
|
||||
compile group: 'biz.source_code', name: 'base64coder', version: '2010-12-19'
|
||||
annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$permissionDispatcherVersion"
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.1'
|
||||
implementation group: 'com.google.guava', name: 'guava', version: '23.0-android'
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.PwGroupV4;
|
||||
import com.keepassdroid.database.PwIconCustom;
|
||||
import com.keepassdroid.database.PwIconStandard;
|
||||
import com.keepassdroid.database.security.ProtectedBinary;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
|
||||
public class PwEntryTestV4 extends TestCase {
|
||||
public void testAssign() {
|
||||
PwEntryV4 entry = new PwEntryV4();
|
||||
|
||||
entry.additional = "test223";
|
||||
|
||||
entry.autoType = entry.new AutoType();
|
||||
entry.autoType.defaultSequence = "1324";
|
||||
entry.autoType.enabled = true;
|
||||
entry.autoType.obfuscationOptions = 123412432109L;
|
||||
entry.autoType.put("key", "value");
|
||||
|
||||
entry.backgroupColor = "blue";
|
||||
entry.binaries.put("key1", new ProtectedBinary(false, new byte[] {0,1}));
|
||||
entry.customIcon = new PwIconCustom(UUID.randomUUID(), new byte[0]);
|
||||
entry.foregroundColor = "red";
|
||||
entry.history.add(new PwEntryV4());
|
||||
entry.icon = new PwIconStandard(5);
|
||||
entry.overrideURL = "override";
|
||||
entry.parent = new PwGroupV4();
|
||||
entry.strings.put("key2", new ProtectedString(false, "value2"));
|
||||
entry.url = "http://localhost";
|
||||
entry.uuid = UUID.randomUUID();
|
||||
|
||||
PwEntryV4 target = new PwEntryV4();
|
||||
target.assign(entry);
|
||||
|
||||
/* This test is not so useful now that I am not implementing value equality for Entries
|
||||
assertTrue("Entries do not match.", entry.equals(target));
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,11 +17,11 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class AccentTest extends AndroidTestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
@@ -29,7 +29,7 @@ public class OutputTests extends TestSuite {
|
||||
public static Test suite() {
|
||||
|
||||
return new TestSuiteBuilder(AllTests.class)
|
||||
.includePackages("com.keepassdroid.tests.output")
|
||||
.includePackages("com.kunzisoft.keepass.tests.output")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,11 +17,11 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDate;
|
||||
import com.kunzisoft.keepass.database.PwDate;
|
||||
|
||||
public class PwDateTest extends TestCase {
|
||||
public void testDate() {
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -27,8 +27,8 @@ import java.util.Calendar;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwEntryV3;
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.database.PwEntryV3;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class PwEntryTestV3 extends AndroidTestCase {
|
||||
PwEntryV3 mPE;
|
||||
@@ -37,12 +37,12 @@ public class PwEntryTestV3 extends AndroidTestCase {
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mPE = (PwEntryV3) TestData.GetTest1(getContext()).entries.get(0);
|
||||
mPE = (PwEntryV3) TestData.GetTest1(getContext()).getEntryAt(0);
|
||||
|
||||
}
|
||||
|
||||
public void testName() {
|
||||
assertTrue("Name was " + mPE.title, mPE.title.equals("Amazon"));
|
||||
assertTrue("Name was " + mPE.getTitle(), mPE.getTitle().equals("Amazon"));
|
||||
}
|
||||
|
||||
public void testPassword() throws UnsupportedEncodingException {
|
||||
@@ -54,7 +54,7 @@ public class PwEntryTestV3 extends AndroidTestCase {
|
||||
|
||||
public void testCreation() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(mPE.tCreation.getJDate());
|
||||
cal.setTime(mPE.getCreationTime().getDate());
|
||||
|
||||
assertEquals("Incorrect year.", cal.get(Calendar.YEAR), 2009);
|
||||
assertEquals("Incorrect month.", cal.get(Calendar.MONTH), 3);
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import com.kunzisoft.keepass.database.AutoType;
|
||||
import com.kunzisoft.keepass.database.PwEntryV4;
|
||||
import com.kunzisoft.keepass.database.PwGroupV4;
|
||||
import com.kunzisoft.keepass.database.PwIconCustom;
|
||||
import com.kunzisoft.keepass.database.PwIconStandard;
|
||||
import com.kunzisoft.keepass.database.security.ProtectedBinary;
|
||||
import com.kunzisoft.keepass.database.security.ProtectedString;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PwEntryTestV4 extends TestCase {
|
||||
public void testAssign() {
|
||||
PwEntryV4 entry = new PwEntryV4();
|
||||
|
||||
entry.setAdditional("test223");
|
||||
|
||||
entry.setAutoType(new AutoType());
|
||||
entry.getAutoType().defaultSequence = "1324";
|
||||
entry.getAutoType().enabled = true;
|
||||
entry.getAutoType().obfuscationOptions = 123412432109L;
|
||||
entry.getAutoType().put("key", "value");
|
||||
|
||||
entry.setBackgroupColor("blue");
|
||||
entry.putProtectedBinary("key1", new ProtectedBinary(false, new byte[] {0,1}));
|
||||
entry.setCustomIcon(new PwIconCustom(UUID.randomUUID(), new byte[0]));
|
||||
entry.setForegroundColor("red");
|
||||
entry.addToHistory(new PwEntryV4());
|
||||
entry.setIcon(new PwIconStandard(5));
|
||||
entry.setOverrideURL("override");
|
||||
entry.setParent(new PwGroupV4());
|
||||
entry.addExtraField("key2", new ProtectedString(false, "value2"));
|
||||
entry.setUrl("http://localhost");
|
||||
entry.setUUID(UUID.randomUUID());
|
||||
|
||||
PwEntryV4 target = new PwEntryV4();
|
||||
target.updateWith(entry);
|
||||
|
||||
/* This test is not so useful now that I am not implementing value equality for Entries
|
||||
assertTrue("Entries do not match.", entry.equals(target));
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,13 +17,13 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.database.PwGroupV3;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class PwGroupTest extends AndroidTestCase {
|
||||
|
||||
@@ -38,7 +38,7 @@ public class PwGroupTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
public void testGroupName() {
|
||||
assertTrue("Name was " + mPG.name, mPG.name.equals("Internet"));
|
||||
assertTrue("Name was " + mPG.getName(), mPG.getName().equals("Internet"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -29,8 +29,8 @@ import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.kunzisoft.keepass.utils.EmptyUtils;
|
||||
import com.kunzisoft.keepass.utils.UriUtil;
|
||||
|
||||
public class TestUtil {
|
||||
private static final File sdcard = Environment.getExternalStorageDirectory();
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests;
|
||||
package com.kunzisoft.keepass.tests;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -28,10 +28,10 @@ import java.util.UUID;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDate;
|
||||
import com.keepassdroid.stream.LEDataInputStream;
|
||||
import com.keepassdroid.stream.LEDataOutputStream;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.kunzisoft.keepass.database.PwDate;
|
||||
import com.kunzisoft.keepass.stream.LEDataInputStream;
|
||||
import com.kunzisoft.keepass.stream.LEDataOutputStream;
|
||||
import com.kunzisoft.keepass.utils.Types;
|
||||
|
||||
public class TypesTest extends TestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,9 +17,11 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.crypto;
|
||||
package com.kunzisoft.keepass.tests.crypto;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import com.kunzisoft.keepass.crypto.CipherFactory;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
@@ -33,9 +35,7 @@ import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.crypto.CipherFactory;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class AESTest extends TestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.crypto;
|
||||
package com.kunzisoft.keepass.tests.crypto;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -37,11 +37,11 @@ import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.crypto.CipherFactory;
|
||||
import com.keepassdroid.crypto.engine.AesEngine;
|
||||
import com.keepassdroid.crypto.engine.CipherEngine;
|
||||
import com.keepassdroid.stream.BetterCipherInputStream;
|
||||
import com.keepassdroid.stream.LEDataInputStream;
|
||||
import com.kunzisoft.keepass.crypto.CipherFactory;
|
||||
import com.kunzisoft.keepass.crypto.engine.AesEngine;
|
||||
import com.kunzisoft.keepass.crypto.engine.CipherEngine;
|
||||
import com.kunzisoft.keepass.stream.BetterCipherInputStream;
|
||||
import com.kunzisoft.keepass.stream.LEDataInputStream;
|
||||
|
||||
public class CipherTest extends TestCase {
|
||||
private Random rand = new Random();
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.crypto;
|
||||
package com.kunzisoft.keepass.tests.crypto;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -26,8 +26,8 @@ import java.util.Random;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.crypto.finalkey.AndroidFinalKey;
|
||||
import com.keepassdroid.crypto.finalkey.NativeFinalKey;
|
||||
import com.kunzisoft.keepass.crypto.finalkey.AndroidFinalKey;
|
||||
import com.kunzisoft.keepass.crypto.finalkey.NativeFinalKey;
|
||||
|
||||
public class FinalKeyTest extends TestCase {
|
||||
private Random mRand;
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,20 +17,21 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.edit.DeleteGroup;
|
||||
import com.keepassdroid.search.SearchDbHelper;
|
||||
import com.kunzisoft.keepass.database.Database;
|
||||
import com.kunzisoft.keepass.database.PwDatabase;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV3;
|
||||
import com.kunzisoft.keepass.database.PwEntry;
|
||||
import com.kunzisoft.keepass.database.PwEntryV3;
|
||||
import com.kunzisoft.keepass.database.PwGroup;
|
||||
import com.kunzisoft.keepass.database.edit.DeleteGroup;
|
||||
import com.kunzisoft.keepass.search.SearchDbHelper;
|
||||
|
||||
public class DeleteEntry extends AndroidTestCase {
|
||||
private static final String GROUP1_NAME = "Group1";
|
||||
@@ -54,7 +55,7 @@ public class DeleteEntry extends AndroidTestCase {
|
||||
return;
|
||||
}
|
||||
|
||||
PwDatabaseV3 pm = (PwDatabaseV3) db.pm;
|
||||
PwDatabaseV3 pm = (PwDatabaseV3) db.getPwDatabase();
|
||||
PwGroup group1 = getGroup(pm, GROUP1_NAME);
|
||||
assertNotNull("Could not find group1", group1);
|
||||
|
||||
@@ -71,24 +72,22 @@ public class DeleteEntry extends AndroidTestCase {
|
||||
|
||||
// Verify the entries were removed from the search index
|
||||
SearchDbHelper dbHelp = new SearchDbHelper(ctx);
|
||||
PwGroup results1 = dbHelp.search(db, ENTRY1_NAME);
|
||||
PwGroup results2 = dbHelp.search(db, ENTRY2_NAME);
|
||||
PwGroup results1 = dbHelp.search(db.getPwDatabase(), ENTRY1_NAME);
|
||||
PwGroup results2 = dbHelp.search(db.getPwDatabase(), ENTRY2_NAME);
|
||||
|
||||
assertEquals("Entry1 was not removed from the search results", 0, results1.childEntries.size());
|
||||
assertEquals("Entry2 was not removed from the search results", 0, results2.childEntries.size());
|
||||
assertEquals("Entry1 was not removed from the search results", 0, results1.numbersOfChildEntries());
|
||||
assertEquals("Entry2 was not removed from the search results", 0, results2.numbersOfChildEntries());
|
||||
|
||||
// Verify the group was deleted
|
||||
group1 = getGroup(pm, GROUP1_NAME);
|
||||
assertNull("Group 1 was not removed.", group1);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private PwEntry getEntry(PwDatabaseV3 pm, String name) {
|
||||
List<PwEntry> entries = pm.entries;
|
||||
private PwEntryV3 getEntry(PwDatabaseV3 pm, String name) {
|
||||
List<PwEntryV3> entries = pm.getEntries();
|
||||
for ( int i = 0; i < entries.size(); i++ ) {
|
||||
PwEntry entry = entries.get(i);
|
||||
PwEntryV3 entry = entries.get(i);
|
||||
if ( entry.getTitle().equals(name) ) {
|
||||
return entry;
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,34 +17,36 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV4;
|
||||
import com.kunzisoft.keepass.database.PwEntryV4;
|
||||
|
||||
public class EntryV4 extends TestCase {
|
||||
|
||||
public void testBackup() {
|
||||
PwDatabaseV4 db = new PwDatabaseV4();
|
||||
|
||||
db.historyMaxItems = 2;
|
||||
db.setHistoryMaxItems(2);
|
||||
|
||||
PwEntryV4 entry = new PwEntryV4();
|
||||
entry.setTitle("Title1", db);
|
||||
entry.setUsername("User1", db);
|
||||
entry.startToManageFieldReferences(db);
|
||||
entry.setTitle("Title1");
|
||||
entry.setUsername("User1");
|
||||
entry.createBackup(db);
|
||||
|
||||
entry.setTitle("Title2", db);
|
||||
entry.setUsername("User2", db);
|
||||
entry.setTitle("Title2");
|
||||
entry.setUsername("User2");
|
||||
entry.createBackup(db);
|
||||
|
||||
entry.setTitle("Title3", db);
|
||||
entry.setUsername("User3", db);
|
||||
entry.setTitle("Title3");
|
||||
entry.setUsername("User3");
|
||||
entry.createBackup(db);
|
||||
|
||||
PwEntryV4 backup = entry.history.get(0);
|
||||
PwEntryV4 backup = entry.getHistory().get(0);
|
||||
entry.endToManageFieldReferences();
|
||||
assertEquals("Title2", backup.getTitle());
|
||||
assertEquals("User2", backup.getUsername());
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
@@ -25,9 +25,9 @@ import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.load.ImporterV3;
|
||||
import com.keepassdroid.tests.TestUtil;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV3;
|
||||
import com.kunzisoft.keepass.tests.TestUtil;
|
||||
import com.kunzisoft.keepass.utils.UriUtil;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.File;
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -25,9 +25,9 @@ import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwEncryptionAlgorithm;
|
||||
import com.keepassdroid.database.load.ImporterV3;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV3;
|
||||
import com.kunzisoft.keepass.database.PwEncryptionAlgorithm;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV3;
|
||||
|
||||
public class Kdb3Twofish extends AndroidTestCase {
|
||||
public void testReadTwofish() throws Exception {
|
||||
@@ -40,10 +40,9 @@ public class Kdb3Twofish extends AndroidTestCase {
|
||||
|
||||
PwDatabaseV3 db = importer.openDatabase(is, "12345", null);
|
||||
|
||||
assertTrue(db.algorithm == PwEncryptionAlgorithm.Twofish);
|
||||
assertTrue(db.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish);
|
||||
|
||||
is.close();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -29,16 +29,16 @@ import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.exception.InvalidDBException;
|
||||
import com.keepassdroid.database.exception.PwDbOutputException;
|
||||
import com.keepassdroid.database.load.Importer;
|
||||
import com.keepassdroid.database.load.ImporterFactory;
|
||||
import com.keepassdroid.database.load.ImporterV4;
|
||||
import com.keepassdroid.database.save.PwDbOutput;
|
||||
import com.keepassdroid.database.save.PwDbV4Output;
|
||||
import com.keepassdroid.stream.CopyInputStream;
|
||||
import com.keepassdroid.tests.TestUtil;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV4;
|
||||
import com.kunzisoft.keepass.database.exception.InvalidDBException;
|
||||
import com.kunzisoft.keepass.database.exception.PwDbOutputException;
|
||||
import com.kunzisoft.keepass.database.load.Importer;
|
||||
import com.kunzisoft.keepass.database.load.ImporterFactory;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV4;
|
||||
import com.kunzisoft.keepass.database.save.PwDbOutput;
|
||||
import com.kunzisoft.keepass.database.save.PwDbV4Output;
|
||||
import com.kunzisoft.keepass.stream.CopyInputStream;
|
||||
import com.kunzisoft.keepass.tests.TestUtil;
|
||||
|
||||
public class Kdb4 extends AndroidTestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,18 +17,17 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
|
||||
import java.io.InputStream;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.crypto.CipherFactory;
|
||||
import com.keepassdroid.crypto.engine.AesEngine;
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.load.ImporterV4;
|
||||
import com.kunzisoft.keepass.crypto.engine.AesEngine;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV4;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV4;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public class Kdb4Header extends AndroidTestCase {
|
||||
public void testReadHeader() throws Exception {
|
||||
@@ -41,9 +40,9 @@ public class Kdb4Header extends AndroidTestCase {
|
||||
|
||||
PwDatabaseV4 db = importer.openDatabase(is, "12345", null);
|
||||
|
||||
assertEquals(6000, db.numKeyEncRounds);
|
||||
assertEquals(6000, db.getNumberKeyEncryptionRounds());
|
||||
|
||||
assertTrue(db.dataCipher.equals(AesEngine.CIPHER_UUID));
|
||||
assertTrue(db.getDataCipher().equals(AesEngine.CIPHER_UUID));
|
||||
|
||||
is.close();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.UUID;
|
||||
@@ -27,15 +27,16 @@ import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
import biz.source_code.base64Coder.Base64Coder;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.load.ImporterV4;
|
||||
import com.keepassdroid.utils.SprEngine;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.kunzisoft.keepass.database.PwDatabase;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV4;
|
||||
import com.kunzisoft.keepass.database.PwEntryV4;
|
||||
import com.kunzisoft.keepass.database.load.ImporterV4;
|
||||
import com.kunzisoft.keepass.utils.SprEngineV4;
|
||||
import com.kunzisoft.keepass.utils.Types;
|
||||
|
||||
public class SprEngineTest extends AndroidTestCase {
|
||||
private PwDatabaseV4 db;
|
||||
private SprEngine spr;
|
||||
private SprEngineV4 spr;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
@@ -51,7 +52,7 @@ public class SprEngineTest extends AndroidTestCase {
|
||||
|
||||
is.close();
|
||||
|
||||
spr = SprEngine.getInstance(db);
|
||||
spr = new SprEngineV4();
|
||||
}
|
||||
|
||||
private final String REF = "{REF:P@I:2B1D56590D961F48A8CE8C392CE6CD35}";
|
||||
@@ -60,7 +61,7 @@ public class SprEngineTest extends AndroidTestCase {
|
||||
public void testRefReplace() {
|
||||
UUID entryUUID = decodeUUID(ENCODE_UUID);
|
||||
|
||||
PwEntryV4 entry = (PwEntryV4) db.entries.get(entryUUID);
|
||||
PwEntryV4 entry = (PwEntryV4) db.getEntryByUUIDId(entryUUID);
|
||||
|
||||
|
||||
assertEquals(RESULT, spr.compile(REF, entry, db));
|
||||
@@ -69,7 +70,7 @@ public class SprEngineTest extends AndroidTestCase {
|
||||
|
||||
private UUID decodeUUID(String encoded) {
|
||||
if (encoded == null || encoded.length() == 0 ) {
|
||||
return PwDatabaseV4.UUID_ZERO;
|
||||
return PwDatabase.UUID_ZERO;
|
||||
}
|
||||
|
||||
byte[] buf = Base64Coder.decode(encoded);
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.database;
|
||||
package com.kunzisoft.keepass.tests.database;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -25,12 +25,10 @@ import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.database.PwDatabaseV3Debug;
|
||||
import com.keepassdroid.database.load.Importer;
|
||||
import com.keepassdroid.tests.TestUtil;
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.kunzisoft.keepass.database.Database;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV3Debug;
|
||||
import com.kunzisoft.keepass.database.load.Importer;
|
||||
import com.kunzisoft.keepass.tests.TestUtil;
|
||||
|
||||
public class TestData {
|
||||
private static final String TEST1_KEYFILE = "";
|
||||
@@ -60,10 +58,10 @@ public class TestData {
|
||||
|
||||
InputStream keyIs = TestUtil.getKeyFileInputStream(ctx, keyfile);
|
||||
|
||||
Db.LoadData(ctx, is, password, keyIs, Importer.DEBUG);
|
||||
Db.loadData(ctx, is, password, keyIs, Importer.DEBUG);
|
||||
Uri.Builder b = new Uri.Builder();
|
||||
|
||||
Db.mUri = b.scheme("file").path(filename).build();
|
||||
Db.setUri(b.scheme("file").path(filename).build());
|
||||
|
||||
return Db;
|
||||
|
||||
@@ -74,6 +72,6 @@ public class TestData {
|
||||
GetDb1(ctx);
|
||||
}
|
||||
|
||||
return (PwDatabaseV3Debug) mDb1.pm;
|
||||
return (PwDatabaseV3Debug) mDb1.getPwDatabase();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.output;
|
||||
package com.kunzisoft.keepass.tests.output;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -32,16 +32,16 @@ import java.security.NoSuchAlgorithmException;
|
||||
import android.content.res.AssetManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.keepassdroid.database.PwDatabaseV3Debug;
|
||||
import com.keepassdroid.database.PwDbHeader;
|
||||
import com.keepassdroid.database.PwDbHeaderV3;
|
||||
import com.keepassdroid.database.exception.PwDbOutputException;
|
||||
import com.keepassdroid.database.save.PwDbHeaderOutputV3;
|
||||
import com.keepassdroid.database.save.PwDbV3Output;
|
||||
import com.keepassdroid.database.save.PwDbV3OutputDebug;
|
||||
import com.keepassdroid.stream.NullOutputStream;
|
||||
import com.keepassdroid.tests.TestUtil;
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.database.PwDatabaseV3Debug;
|
||||
import com.kunzisoft.keepass.database.PwDbHeader;
|
||||
import com.kunzisoft.keepass.database.PwDbHeaderV3;
|
||||
import com.kunzisoft.keepass.database.exception.PwDbOutputException;
|
||||
import com.kunzisoft.keepass.database.save.PwDbHeaderOutputV3;
|
||||
import com.kunzisoft.keepass.database.save.PwDbV3Output;
|
||||
import com.kunzisoft.keepass.database.save.PwDbV3OutputDebug;
|
||||
import com.kunzisoft.keepass.stream.NullOutputStream;
|
||||
import com.kunzisoft.keepass.tests.TestUtil;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class PwManagerOutputTest extends AndroidTestCase {
|
||||
PwDatabaseV3Debug mPM;
|
||||
@@ -60,7 +60,7 @@ public class PwManagerOutputTest extends AndroidTestCase {
|
||||
pos.outputPlanGroupAndEntries(bos);
|
||||
|
||||
assertTrue("No output", bos.toByteArray().length > 0);
|
||||
assertArrayEquals("Group and entry output doesn't match.", mPM.postHeader, bos.toByteArray());
|
||||
assertArrayEquals("Group and entry output doesn't match.", mPM.getPostHeader(), bos.toByteArray());
|
||||
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class PwManagerOutputTest extends AndroidTestCase {
|
||||
|
||||
byte[] digest = md.digest();
|
||||
assertTrue("No output", digest.length > 0);
|
||||
assertArrayEquals("Hash of groups and entries failed.", mPM.dbHeader.contentsHash, digest);
|
||||
assertArrayEquals("Hash of groups and entries failed.", mPM.getDbHeader().contentsHash, digest);
|
||||
}
|
||||
|
||||
private void assertHeadersEquals(PwDbHeaderV3 expected, PwDbHeaderV3 actual) {
|
||||
@@ -100,10 +100,10 @@ public class PwManagerOutputTest extends AndroidTestCase {
|
||||
PwDbHeaderV3 header = pActual.outputHeader(bActual);
|
||||
|
||||
ByteArrayOutputStream bExpected = new ByteArrayOutputStream();
|
||||
PwDbHeaderOutputV3 outExpected = new PwDbHeaderOutputV3(mPM.dbHeader, bExpected);
|
||||
PwDbHeaderOutputV3 outExpected = new PwDbHeaderOutputV3(mPM.getDbHeader(), bExpected);
|
||||
outExpected.output();
|
||||
|
||||
assertHeadersEquals(mPM.dbHeader, header);
|
||||
assertHeadersEquals(mPM.getDbHeader(), header);
|
||||
assertTrue("No output", bActual.toByteArray().length > 0);
|
||||
assertArrayEquals("Header does not match.", bExpected.toByteArray(), bActual.toByteArray());
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class PwManagerOutputTest extends AndroidTestCase {
|
||||
PwDbHeader hActual = pActual.outputHeader(bActual);
|
||||
byte[] finalKey = pActual.getFinalKey(hActual);
|
||||
|
||||
assertArrayEquals("Keys mismatched", mPM.finalKey, finalKey);
|
||||
assertArrayEquals("Keys mismatched", mPM.getFinalKey(), finalKey);
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.search;
|
||||
package com.kunzisoft.keepass.tests.search;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
@@ -25,10 +25,9 @@ import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.tests.database.TestData;
|
||||
import com.kunzisoft.keepass.database.Database;
|
||||
import com.kunzisoft.keepass.database.PwGroup;
|
||||
import com.kunzisoft.keepass.tests.database.TestData;
|
||||
|
||||
public class SearchTest extends AndroidTestCase {
|
||||
|
||||
@@ -42,23 +41,23 @@ public class SearchTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
public void testSearch() {
|
||||
PwGroup results = mDb.Search("Amazon");
|
||||
assertTrue("Search result not found.", results.childEntries.size() > 0);
|
||||
PwGroup results = mDb.search("Amazon");
|
||||
assertTrue("Search result not found.", results.numbersOfChildEntries() > 0);
|
||||
|
||||
}
|
||||
|
||||
public void testBackupIncluded() {
|
||||
updateOmitSetting(false);
|
||||
PwGroup results = mDb.Search("BackupOnly");
|
||||
PwGroup results = mDb.search("BackupOnly");
|
||||
|
||||
assertTrue("Search result not found.", results.childEntries.size() > 0);
|
||||
assertTrue("Search result not found.", results.numbersOfChildEntries() > 0);
|
||||
}
|
||||
|
||||
public void testBackupExcluded() {
|
||||
updateOmitSetting(true);
|
||||
PwGroup results = mDb.Search("BackupOnly");
|
||||
PwGroup results = mDb.search("BackupOnly");
|
||||
|
||||
assertFalse("Search result found, but should not have been.", results.childEntries.size() > 0);
|
||||
assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0);
|
||||
}
|
||||
|
||||
private void updateOmitSetting(boolean setting) {
|
||||
@@ -66,7 +65,7 @@ public class SearchTest extends AndroidTestCase {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
editor.putBoolean(ctx.getString(R.string.omitbackup_key), setting);
|
||||
editor.putBoolean("settings_omitbackup_key", setting);
|
||||
editor.commit();
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.stream;
|
||||
package com.kunzisoft.keepass.tests.stream;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
@@ -30,8 +30,8 @@ import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.keepassdroid.stream.HashedBlockInputStream;
|
||||
import com.keepassdroid.stream.HashedBlockOutputStream;
|
||||
import com.kunzisoft.keepass.stream.HashedBlockInputStream;
|
||||
import com.kunzisoft.keepass.stream.HashedBlockOutputStream;
|
||||
|
||||
public class HashedBlock extends TestCase {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -17,11 +17,11 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.tests.utils;
|
||||
package com.kunzisoft.keepass.tests.utils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import com.keepassdroid.utils.StrUtil;
|
||||
import com.kunzisoft.keepass.utils.StrUtil;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@@ -16,15 +16,16 @@
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:name="com.keepassdroid.app.App"
|
||||
android:name="com.kunzisoft.keepass.app.App"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backup"
|
||||
android:backupAgent="com.keepassdroid.backup.SettingsBackupAgent"
|
||||
android:backupAgent="com.kunzisoft.keepass.backup.SettingsBackupAgent"
|
||||
android:theme="@style/KeepassDXStyle.Light"
|
||||
tools:replace="android:theme">
|
||||
<!-- TODO backup API Key -->
|
||||
<meta-data android:name="com.google.android.backup.api_key"
|
||||
android:value="" />
|
||||
<meta-data
|
||||
android:name="com.google.android.backup.api_key"
|
||||
android:value="" />
|
||||
|
||||
<!-- Folder picker -->
|
||||
<provider
|
||||
@@ -37,7 +38,7 @@
|
||||
android:resource="@xml/nnf_provider_paths" />
|
||||
</provider>
|
||||
<activity
|
||||
android:name="com.keepassdroid.FilePickerStylishActivity"
|
||||
android:name="com.kunzisoft.keepass.fileselect.FilePickerStylishActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
@@ -52,15 +53,19 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.keepassdroid.fileselect.FileSelectActivity"
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.fileselect.FileSelectActivity"
|
||||
android:launchMode="singleInstance"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity android:name="com.keepassdroid.AboutActivity"
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.activities.AboutActivity"
|
||||
android:launchMode="singleInstance"
|
||||
android:label="@string/menu_about" />
|
||||
<activity android:name="com.keepassdroid.PasswordActivity"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.password.PasswordActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
@@ -90,42 +95,63 @@
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.keepassdroid.GroupActivityV3" android:configChanges="orientation|keyboardHidden">
|
||||
<meta-data android:name="android.app.default_searchable"
|
||||
android:value="com.keepassdroid.search.SearchResults" />
|
||||
</activity>
|
||||
<activity android:name="com.keepassdroid.GroupActivityV4" android:configChanges="orientation|keyboardHidden">
|
||||
<meta-data android:name="android.app.default_searchable"
|
||||
android:value="com.keepassdroid.search.SearchResults"
|
||||
android:exported="false" />
|
||||
<intent-filter tools:ignore="AppLinkUrlError">
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:mimeType="application/octet-stream"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.keepassdroid.EntryActivity"
|
||||
android:name="com.kunzisoft.keepass.activities.GroupActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="adjustPan">
|
||||
<meta-data
|
||||
android:name="android.app.default_searchable"
|
||||
android:value="com.kunzisoft.keepass.search.SearchResults"
|
||||
android:exported="false"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.activities.EntryActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name="com.keepassdroid.EntryActivityV4"
|
||||
android:name="com.kunzisoft.keepass.activities.EntryEditActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name="com.keepassdroid.EntryEditActivityV3"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name="com.keepassdroid.EntryEditActivityV4"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity android:name="com.keepassdroid.search.SearchResultsActivity" android:launchMode="standard">
|
||||
android:name="com.kunzisoft.keepass.search.SearchResultsActivity"
|
||||
android:launchMode="standard">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable" />
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
<activity android:name="com.keepassdroid.settings.SettingsActivity" />
|
||||
<activity android:name="com.kunzisoft.keepass.settings.SettingsActivity" />
|
||||
<activity android:name="com.kunzisoft.keepass.autofill.AutoFillAuthActivity"
|
||||
android:configChanges="orientation|keyboardHidden" />
|
||||
<activity android:name="com.kunzisoft.keepass.settings.SettingsAutofillActivity" />
|
||||
|
||||
<service android:name="com.keepassdroid.services.TimeoutService" />
|
||||
<service android:name="com.kunzisoft.keepass.timeout.TimeoutService" />
|
||||
<service
|
||||
android:name="com.kunzisoft.keepass.notifications.NotificationCopyingService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name="com.kunzisoft.keepass.autofill.KeeAutofillService"
|
||||
android:label="@string/autofill_service_name"
|
||||
android:permission="android.permission.BIND_AUTOFILL_SERVICE">
|
||||
<meta-data
|
||||
android:name="android.autofill"
|
||||
android:resource="@xml/dataset_service" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.service.autofill.AutofillService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
||||
</application>
|
||||
</manifest>
|
||||
Binary file not shown.
@@ -1,225 +0,0 @@
|
||||
// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
|
||||
// www.source-code.biz, www.inventec.ch/chdh
|
||||
//
|
||||
// This module is multi-licensed and may be used under the terms
|
||||
// of any of the following licenses:
|
||||
//
|
||||
// EPL, Eclipse Public License, http://www.eclipse.org/legal
|
||||
// LGPL, GNU Lesser General Public License, http://www.gnu.org/licenses/lgpl.html
|
||||
// AL, Apache License, http://www.apache.org/licenses
|
||||
// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
|
||||
//
|
||||
// Please contact the author if you need another license.
|
||||
// This module is provided "as is", without warranties of any kind.
|
||||
|
||||
package biz.source_code.base64Coder;
|
||||
|
||||
/**
|
||||
* A Base64 encoder/decoder.
|
||||
*
|
||||
* <p>
|
||||
* This class is used to encode and decode data in Base64 format as described in RFC 1521.
|
||||
*
|
||||
* <p>
|
||||
* Project home page: <a href="http://www.source-code.biz/base64coder/java/">www.source-code.biz/base64coder/java</a><br>
|
||||
* Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
|
||||
* Multi-licensed: EPL / LGPL / AL / BSD.
|
||||
*/
|
||||
public class Base64Coder {
|
||||
|
||||
// The line separator string of the operating system.
|
||||
private static final String systemLineSeparator = System.getProperty("line.separator");
|
||||
|
||||
// Mapping table from 6-bit nibbles to Base64 characters.
|
||||
private static char[] map1 = new char[64];
|
||||
static {
|
||||
int i=0;
|
||||
for (char c='A'; c<='Z'; c++) map1[i++] = c;
|
||||
for (char c='a'; c<='z'; c++) map1[i++] = c;
|
||||
for (char c='0'; c<='9'; c++) map1[i++] = c;
|
||||
map1[i++] = '+'; map1[i++] = '/'; }
|
||||
|
||||
// Mapping table from Base64 characters to 6-bit nibbles.
|
||||
private static byte[] map2 = new byte[128];
|
||||
static {
|
||||
for (int i=0; i<map2.length; i++) map2[i] = -1;
|
||||
for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
|
||||
|
||||
/**
|
||||
* Encodes a string into Base64 format.
|
||||
* No blanks or line breaks are inserted.
|
||||
* @param s A String to be encoded.
|
||||
* @return A String containing the Base64 encoded data.
|
||||
*/
|
||||
public static String encodeString (String s) {
|
||||
return new String(encode(s.getBytes())); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters.
|
||||
* This method is compatible with <code>sun.misc.BASE64Encoder.encodeBuffer(byte[])</code>.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @return A String containing the Base64 encoded data, broken into lines.
|
||||
*/
|
||||
public static String encodeLines (byte[] in) {
|
||||
return encodeLines(in, 0, in.length, 76, systemLineSeparator); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base 64 format and breaks the output into lines.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @param iOff Offset of the first byte in <code>in</code> to be processed.
|
||||
* @param iLen Number of bytes to be processed in <code>in</code>, starting at <code>iOff</code>.
|
||||
* @param lineLen Line length for the output data. Should be a multiple of 4.
|
||||
* @param lineSeparator The line separator to be used to separate the output lines.
|
||||
* @return A String containing the Base64 encoded data, broken into lines.
|
||||
*/
|
||||
public static String encodeLines (byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) {
|
||||
int blockLen = (lineLen*3) / 4;
|
||||
if (blockLen <= 0) throw new IllegalArgumentException();
|
||||
int lines = (iLen+blockLen-1) / blockLen;
|
||||
int bufLen = ((iLen+2)/3)*4 + lines*lineSeparator.length();
|
||||
StringBuilder buf = new StringBuilder(bufLen);
|
||||
int ip = 0;
|
||||
while (ip < iLen) {
|
||||
int l = Math.min(iLen-ip, blockLen);
|
||||
buf.append (encode(in, iOff+ip, l));
|
||||
buf.append (lineSeparator);
|
||||
ip += l; }
|
||||
return buf.toString(); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 format.
|
||||
* No blanks or line breaks are inserted in the output.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @return A character array containing the Base64 encoded data.
|
||||
*/
|
||||
public static char[] encode (byte[] in) {
|
||||
return encode(in, 0, in.length); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 format.
|
||||
* No blanks or line breaks are inserted in the output.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @param iLen Number of bytes to process in <code>in</code>.
|
||||
* @return A character array containing the Base64 encoded data.
|
||||
*/
|
||||
public static char[] encode (byte[] in, int iLen) {
|
||||
return encode(in, 0, iLen); }
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 format.
|
||||
* No blanks or line breaks are inserted in the output.
|
||||
* @param in An array containing the data bytes to be encoded.
|
||||
* @param iOff Offset of the first byte in <code>in</code> to be processed.
|
||||
* @param iLen Number of bytes to process in <code>in</code>, starting at <code>iOff</code>.
|
||||
* @return A character array containing the Base64 encoded data.
|
||||
*/
|
||||
public static char[] encode (byte[] in, int iOff, int iLen) {
|
||||
int oDataLen = (iLen*4+2)/3; // output length without padding
|
||||
int oLen = ((iLen+2)/3)*4; // output length including padding
|
||||
char[] out = new char[oLen];
|
||||
int ip = iOff;
|
||||
int iEnd = iOff + iLen;
|
||||
int op = 0;
|
||||
while (ip < iEnd) {
|
||||
int i0 = in[ip++] & 0xff;
|
||||
int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
|
||||
int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
|
||||
int o0 = i0 >>> 2;
|
||||
int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
|
||||
int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
|
||||
int o3 = i2 & 0x3F;
|
||||
out[op++] = map1[o0];
|
||||
out[op++] = map1[o1];
|
||||
out[op] = op < oDataLen ? map1[o2] : '='; op++;
|
||||
out[op] = op < oDataLen ? map1[o3] : '='; op++; }
|
||||
return out; }
|
||||
|
||||
/**
|
||||
* Decodes a string from Base64 format.
|
||||
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||
* @param s A Base64 String to be decoded.
|
||||
* @return A String containing the decoded data.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static String decodeString (String s) {
|
||||
return new String(decode(s)); }
|
||||
|
||||
/**
|
||||
* Decodes a byte array from Base64 format and ignores line separators, tabs and blanks.
|
||||
* CR, LF, Tab and Space characters are ignored in the input data.
|
||||
* This method is compatible with <code>sun.misc.BASE64Decoder.decodeBuffer(String)</code>.
|
||||
* @param s A Base64 String to be decoded.
|
||||
* @return An array containing the decoded data bytes.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static byte[] decodeLines (String s) {
|
||||
char[] buf = new char[s.length()];
|
||||
int p = 0;
|
||||
for (int ip = 0; ip < s.length(); ip++) {
|
||||
char c = s.charAt(ip);
|
||||
if (c != ' ' && c != '\r' && c != '\n' && c != '\t')
|
||||
buf[p++] = c; }
|
||||
return decode(buf, 0, p); }
|
||||
|
||||
/**
|
||||
* Decodes a byte array from Base64 format.
|
||||
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||
* @param s A Base64 String to be decoded.
|
||||
* @return An array containing the decoded data bytes.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static byte[] decode (String s) {
|
||||
return decode(s.toCharArray()); }
|
||||
|
||||
/**
|
||||
* Decodes a byte array from Base64 format.
|
||||
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||
* @param in A character array containing the Base64 encoded data.
|
||||
* @return An array containing the decoded data bytes.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static byte[] decode (char[] in) {
|
||||
return decode(in, 0, in.length); }
|
||||
|
||||
/**
|
||||
* Decodes a byte array from Base64 format.
|
||||
* No blanks or line breaks are allowed within the Base64 encoded input data.
|
||||
* @param in A character array containing the Base64 encoded data.
|
||||
* @param iOff Offset of the first character in <code>in</code> to be processed.
|
||||
* @param iLen Number of characters to process in <code>in</code>, starting at <code>iOff</code>.
|
||||
* @return An array containing the decoded data bytes.
|
||||
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
|
||||
*/
|
||||
public static byte[] decode (char[] in, int iOff, int iLen) {
|
||||
if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
|
||||
while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--;
|
||||
int oLen = (iLen*3) / 4;
|
||||
byte[] out = new byte[oLen];
|
||||
int ip = iOff;
|
||||
int iEnd = iOff + iLen;
|
||||
int op = 0;
|
||||
while (ip < iEnd) {
|
||||
int i0 = in[ip++];
|
||||
int i1 = in[ip++];
|
||||
int i2 = ip < iEnd ? in[ip++] : 'A';
|
||||
int i3 = ip < iEnd ? in[ip++] : 'A';
|
||||
if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
|
||||
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
|
||||
int b0 = map2[i0];
|
||||
int b1 = map2[i1];
|
||||
int b2 = map2[i2];
|
||||
int b3 = map2[i3];
|
||||
if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
|
||||
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
|
||||
int o0 = ( b0 <<2) | (b1>>>4);
|
||||
int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
|
||||
int o2 = ((b2 & 3)<<6) | b3;
|
||||
out[op++] = (byte)o0;
|
||||
if (op<oLen) out[op++] = (byte)o1;
|
||||
if (op<oLen) out[op++] = (byte)o2; }
|
||||
return out; }
|
||||
|
||||
// Dummy constructor.
|
||||
private Base64Coder() {}
|
||||
|
||||
} // end class Base64Coder
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.exception.ContentFileNotFoundException;
|
||||
import com.keepassdroid.database.exception.InvalidDBException;
|
||||
import com.keepassdroid.database.exception.InvalidPasswordException;
|
||||
import com.keepassdroid.database.exception.PwDbOutputException;
|
||||
import com.keepassdroid.database.load.Importer;
|
||||
import com.keepassdroid.database.load.ImporterFactory;
|
||||
import com.keepassdroid.database.save.PwDbOutput;
|
||||
import com.keepassdroid.icons.DrawableFactory;
|
||||
import com.keepassdroid.search.SearchDbHelper;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.SyncFailedException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author bpellin
|
||||
*/
|
||||
public class Database {
|
||||
public Set<PwGroup> dirty = new HashSet<PwGroup>();
|
||||
public PwDatabase pm;
|
||||
public Uri mUri;
|
||||
public SearchDbHelper searchHelper;
|
||||
public boolean readOnly = false;
|
||||
public boolean passwordEncodingError = false;
|
||||
|
||||
public DrawableFactory drawFactory = new DrawableFactory();
|
||||
|
||||
private boolean loaded = false;
|
||||
|
||||
public boolean Loaded() {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public void setLoaded() {
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, InputStream is, String password, InputStream keyInputStream) throws IOException, InvalidDBException {
|
||||
LoadData(ctx, is, password, keyInputStream, new UpdateStatus(), !Importer.DEBUG);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, Uri uri, String password, Uri keyfile) throws IOException, FileNotFoundException, InvalidDBException {
|
||||
LoadData(ctx, uri, password, keyfile, new UpdateStatus(), !Importer.DEBUG);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, Uri uri, String password, Uri keyfile, UpdateStatus status) throws IOException, FileNotFoundException, InvalidDBException {
|
||||
LoadData(ctx, uri, password, keyfile, status, !Importer.DEBUG);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, Uri uri, String password, Uri keyfile, UpdateStatus status, boolean debug) throws IOException, FileNotFoundException, InvalidDBException {
|
||||
mUri = uri;
|
||||
readOnly = false;
|
||||
if (uri.getScheme().equals("file")) {
|
||||
File file = new File(uri.getPath());
|
||||
readOnly = !file.canWrite();
|
||||
}
|
||||
|
||||
try {
|
||||
passUrisAsInputStreams(ctx, uri, password, keyfile, status, debug, 0);
|
||||
} catch (InvalidPasswordException e) {
|
||||
// Retry with rounds fix
|
||||
try {
|
||||
passUrisAsInputStreams(ctx, uri, password, keyfile, status, debug, getFixRounds(ctx));
|
||||
} catch (Exception e2) {
|
||||
// Rethrow original exception
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long getFixRounds(Context ctx) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
return prefs.getLong(ctx.getString(R.string.roundsFix_key), ctx.getResources().getInteger(R.integer.roundsFix_default));
|
||||
}
|
||||
|
||||
|
||||
private void passUrisAsInputStreams(Context ctx, Uri uri, String password, Uri keyfile, UpdateStatus status, boolean debug, long roundsFix) throws IOException, FileNotFoundException, InvalidDBException {
|
||||
InputStream is, kfIs;
|
||||
try {
|
||||
is = UriUtil.getUriInputStream(ctx, uri);
|
||||
} catch (Exception e) {
|
||||
Log.e("KPD", "Database::LoadData", e);
|
||||
throw ContentFileNotFoundException.getInstance(uri);
|
||||
}
|
||||
|
||||
try {
|
||||
kfIs = UriUtil.getUriInputStream(ctx, keyfile);
|
||||
} catch (Exception e) {
|
||||
Log.e("KPD", "Database::LoadData", e);
|
||||
throw ContentFileNotFoundException.getInstance(keyfile);
|
||||
}
|
||||
LoadData(ctx, is, password, kfIs, status, debug, roundsFix);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, boolean debug) throws IOException, InvalidDBException {
|
||||
LoadData(ctx, is, password, kfIs, new UpdateStatus(), debug);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, UpdateStatus status, boolean debug) throws IOException, InvalidDBException {
|
||||
LoadData(ctx, is, password, kfIs, status, debug, 0);
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, UpdateStatus status, boolean debug, long roundsFix) throws IOException, InvalidDBException {
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
|
||||
if ( ! bis.markSupported() ) {
|
||||
throw new IOException("Input stream does not support mark.");
|
||||
}
|
||||
|
||||
// We'll end up reading 8 bytes to identify the header. Might as well use two extra.
|
||||
bis.mark(10);
|
||||
|
||||
Importer imp = ImporterFactory.createImporter(bis, debug);
|
||||
|
||||
bis.reset(); // Return to the start
|
||||
|
||||
pm = imp.openDatabase(bis, password, kfIs, status, roundsFix);
|
||||
if ( pm != null ) {
|
||||
PwGroup root = pm.rootGroup;
|
||||
pm.populateGlobals(root);
|
||||
LoadData(ctx, pm, password, kfIs, status);
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public void LoadData(Context ctx, PwDatabase pm, String password, InputStream keyInputStream, UpdateStatus status) {
|
||||
if ( pm != null ) {
|
||||
passwordEncodingError = !pm.validatePasswordEncoding(password);
|
||||
}
|
||||
searchHelper = new SearchDbHelper(ctx);
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public PwGroup Search(String str) {
|
||||
if (searchHelper == null) { return null; }
|
||||
return searchHelper.search(this, str);
|
||||
}
|
||||
|
||||
public void SaveData(Context ctx) throws IOException, PwDbOutputException {
|
||||
SaveData(ctx, mUri);
|
||||
}
|
||||
|
||||
public void SaveData(Context ctx, Uri uri) throws IOException, PwDbOutputException {
|
||||
if (uri.getScheme().equals("file")) {
|
||||
String filename = uri.getPath();
|
||||
File tempFile = new File(filename + ".tmp");
|
||||
FileOutputStream fos = new FileOutputStream(tempFile);
|
||||
//BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||
|
||||
//PwDbV3Output pmo = new PwDbV3Output(pm, bos, App.getCalendar());
|
||||
PwDbOutput pmo = PwDbOutput.getInstance(pm, fos);
|
||||
pmo.output();
|
||||
//bos.flush();
|
||||
//bos.close();
|
||||
fos.close();
|
||||
|
||||
// Force data to disk before continuing
|
||||
try {
|
||||
fos.getFD().sync();
|
||||
} catch (SyncFailedException e) {
|
||||
// Ignore if fsync fails. We tried.
|
||||
}
|
||||
|
||||
File orig = new File(filename);
|
||||
|
||||
if (!tempFile.renameTo(orig)) {
|
||||
throw new IOException("Failed to store database.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
OutputStream os;
|
||||
try {
|
||||
os = ctx.getContentResolver().openOutputStream(uri);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Failed to store database.");
|
||||
}
|
||||
|
||||
PwDbOutput pmo = PwDbOutput.getInstance(pm, os);
|
||||
pmo.output();
|
||||
os.close();
|
||||
}
|
||||
mUri = uri;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
dirty.clear();
|
||||
drawFactory.clear();
|
||||
|
||||
pm = null;
|
||||
mUri = null;
|
||||
loaded = false;
|
||||
passwordEncodingError = false;
|
||||
}
|
||||
|
||||
public void markAllGroupsAsDirty() {
|
||||
for ( PwGroup group : pm.getGroups() ) {
|
||||
dirty.add(group);
|
||||
}
|
||||
|
||||
// TODO: This should probably be abstracted out
|
||||
// The root tree in v3 is not an 'official' tree
|
||||
if ( pm instanceof PwDatabaseV3 ) {
|
||||
dirty.add(pm.rootGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,496 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.compat.ActivityCompat;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.exception.SamsungClipboardException;
|
||||
import com.keepassdroid.intents.Intents;
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
import com.keepassdroid.utils.MenuUtil;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.keepassdroid.utils.Util;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.keepassdroid.settings.PrefsUtil.isClipboardNotificationsEnable;
|
||||
|
||||
public class EntryActivity extends LockCloseHideActivity {
|
||||
public static final String KEY_ENTRY = "entry";
|
||||
public static final String KEY_REFRESH_POS = "refresh_pos";
|
||||
|
||||
public static final int NOTIFY_USERNAME = 1;
|
||||
public static final int NOTIFY_PASSWORD = 2;
|
||||
|
||||
public static void Launch(Activity act, PwEntry pw, int pos) {
|
||||
Intent i;
|
||||
|
||||
if ( pw instanceof PwEntryV4 ) {
|
||||
i = new Intent(act, EntryActivityV4.class);
|
||||
} else {
|
||||
i = new Intent(act, EntryActivity.class);
|
||||
}
|
||||
|
||||
i.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID()));
|
||||
i.putExtra(KEY_REFRESH_POS, pos);
|
||||
|
||||
act.startActivityForResult(i,0);
|
||||
}
|
||||
|
||||
protected PwEntry mEntry;
|
||||
private Timer mTimer = new Timer();
|
||||
private boolean mShowPassword;
|
||||
private int mPos;
|
||||
private NotificationManager mNM;
|
||||
private BroadcastReceiver mIntentReceiver;
|
||||
protected boolean readOnly = false;
|
||||
|
||||
private DateFormat dateFormat;
|
||||
private DateFormat timeFormat;
|
||||
|
||||
protected void setEntryView() {
|
||||
setContentView(R.layout.entry_view);
|
||||
}
|
||||
|
||||
protected void setupEditButtons() {
|
||||
View edit = findViewById(R.id.entry_edit);
|
||||
edit.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
EntryEditActivity.Launch(EntryActivity.this, mEntry);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (readOnly) {
|
||||
edit.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mShowPassword = ! prefs.getBoolean(getString(R.string.maskpass_key), getResources().getBoolean(R.bool.maskpass_default));
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setEntryView();
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
Context appCtx = getApplicationContext();
|
||||
dateFormat = android.text.format.DateFormat.getDateFormat(appCtx);
|
||||
timeFormat = android.text.format.DateFormat.getTimeFormat(appCtx);
|
||||
|
||||
Database db = App.getDB();
|
||||
// Likely the app has been killed exit the activity
|
||||
if ( ! db.Loaded() ) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
readOnly = db.readOnly;
|
||||
|
||||
setResult(KeePass.EXIT_NORMAL);
|
||||
|
||||
Intent i = getIntent();
|
||||
UUID uuid = Types.bytestoUUID(i.getByteArrayExtra(KEY_ENTRY));
|
||||
mPos = i.getIntExtra(KEY_REFRESH_POS, -1);
|
||||
|
||||
mEntry = db.pm.entries.get(uuid);
|
||||
if (mEntry == null) {
|
||||
Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set
|
||||
ActivityCompat.invalidateOptionsMenu(this);
|
||||
|
||||
// Update last access time.
|
||||
mEntry.touch(false, false);
|
||||
|
||||
fillData(false);
|
||||
|
||||
setupEditButtons();
|
||||
|
||||
// If notifications enabled in settings
|
||||
if (isClipboardNotificationsEnable(getApplicationContext())) {
|
||||
// Notification Manager
|
||||
mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
|
||||
if (mEntry.getPassword().length() > 0) {
|
||||
// only show notification if password is available
|
||||
Notification password = getNotification(Intents.COPY_PASSWORD, R.string.copy_password);
|
||||
mNM.notify(NOTIFY_PASSWORD, password);
|
||||
}
|
||||
|
||||
if (mEntry.getUsername().length() > 0) {
|
||||
// only show notification if username is available
|
||||
Notification username = getNotification(Intents.COPY_USERNAME, R.string.copy_username);
|
||||
mNM.notify(NOTIFY_USERNAME, username);
|
||||
}
|
||||
}
|
||||
|
||||
mIntentReceiver = new BroadcastReceiver() {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if ( action != null) {
|
||||
if (action.equals(Intents.COPY_USERNAME)) {
|
||||
String username = mEntry.getUsername();
|
||||
if (username.length() > 0) {
|
||||
timeoutCopyToClipboard(username);
|
||||
}
|
||||
} else if (action.equals(Intents.COPY_PASSWORD)) {
|
||||
String password = mEntry.getPassword();
|
||||
if (password.length() > 0) {
|
||||
timeoutCopyToClipboard(password);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intents.COPY_USERNAME);
|
||||
filter.addAction(Intents.COPY_PASSWORD);
|
||||
registerReceiver(mIntentReceiver, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
// These members might never get initialized if the app timed out
|
||||
if ( mIntentReceiver != null ) {
|
||||
unregisterReceiver(mIntentReceiver);
|
||||
}
|
||||
|
||||
if ( mNM != null ) {
|
||||
try {
|
||||
mNM.cancelAll();
|
||||
} catch (SecurityException e) {
|
||||
// Some android devices give a SecurityException when trying to cancel notifications without the WAKE_LOCK permission,
|
||||
// we'll ignore these.
|
||||
}
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private Notification getNotification(String intentText, int descResId) {
|
||||
|
||||
String desc = getString(descResId);
|
||||
|
||||
Intent intent = new Intent(intentText);
|
||||
PendingIntent pending = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
// no longer supported for api level >22
|
||||
// notify.setLatestEventInfo(this, getString(R.string.app_name), desc, pending);
|
||||
// so instead using compat builder and create new notification
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
Notification notify = builder.setContentIntent(pending).setContentText(desc).setContentTitle(getString(R.string.app_name))
|
||||
.setSmallIcon(R.drawable.notify).setTicker(desc).setWhen(System.currentTimeMillis()).build();
|
||||
|
||||
return notify;
|
||||
}
|
||||
|
||||
private String getDateTime(Date dt) {
|
||||
return dateFormat.format(dt) + " " + timeFormat.format(dt);
|
||||
|
||||
}
|
||||
|
||||
protected void fillData(boolean trimList) {
|
||||
ImageView iv = (ImageView) findViewById(R.id.entry_icon);
|
||||
Database db = App.getDB();
|
||||
db.drawFactory.assignDrawableTo(iv, getResources(), mEntry.getIcon());
|
||||
|
||||
PwDatabase pm = db.pm;
|
||||
|
||||
populateText(R.id.entry_title, mEntry.getTitle(true, pm));
|
||||
populateText(R.id.entry_user_name, mEntry.getUsername(true, pm));
|
||||
|
||||
populateText(R.id.entry_url, mEntry.getUrl(true, pm));
|
||||
populateText(R.id.entry_password, mEntry.getPassword(true, pm));
|
||||
setPasswordStyle();
|
||||
|
||||
populateText(R.id.entry_created, getDateTime(mEntry.getCreationTime()));
|
||||
populateText(R.id.entry_modified, getDateTime(mEntry.getLastModificationTime()));
|
||||
populateText(R.id.entry_accessed, getDateTime(mEntry.getLastAccessTime()));
|
||||
|
||||
Date expires = mEntry.getExpiryTime();
|
||||
if ( mEntry.expires() ) {
|
||||
populateText(R.id.entry_expires, getDateTime(expires));
|
||||
} else {
|
||||
populateText(R.id.entry_expires, R.string.never);
|
||||
}
|
||||
populateText(R.id.entry_comment, mEntry.getNotes(true, pm));
|
||||
|
||||
}
|
||||
|
||||
private void populateText(int viewId, int resId) {
|
||||
TextView tv = (TextView) findViewById(viewId);
|
||||
tv.setText(resId);
|
||||
}
|
||||
|
||||
private void populateText(int viewId, String text) {
|
||||
TextView tv = (TextView) findViewById(viewId);
|
||||
tv.setText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if ( resultCode == KeePass.EXIT_REFRESH || resultCode == KeePass.EXIT_REFRESH_TITLE ) {
|
||||
fillData(true);
|
||||
if ( resultCode == KeePass.EXIT_REFRESH_TITLE ) {
|
||||
Intent ret = new Intent();
|
||||
ret.putExtra(KEY_REFRESH_POS, mPos);
|
||||
setResult(KeePass.EXIT_REFRESH, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
MenuUtil.donationMenuInflater(inflater, menu);
|
||||
inflater.inflate(R.menu.entry, menu);
|
||||
inflater.inflate(R.menu.lock_database, menu);
|
||||
|
||||
MenuItem togglePassword = menu.findItem(R.id.menu_toggle_pass);
|
||||
if ( mShowPassword ) {
|
||||
togglePassword.setTitle(R.string.menu_hide_password);
|
||||
togglePassword.setIcon(R.drawable.ic_visibility_off_white_24dp);
|
||||
} else {
|
||||
togglePassword.setTitle(R.string.menu_showpass);
|
||||
togglePassword.setIcon(R.drawable.ic_visibility_white_24dp);
|
||||
}
|
||||
|
||||
MenuItem gotoUrl = menu.findItem(R.id.menu_goto_url);
|
||||
MenuItem copyUser = menu.findItem(R.id.menu_copy_user);
|
||||
MenuItem copyPass = menu.findItem(R.id.menu_copy_pass);
|
||||
|
||||
// In API >= 11 onCreateOptionsMenu may be called before onCreate completes
|
||||
// so mEntry may not be set
|
||||
if (mEntry == null) {
|
||||
gotoUrl.setVisible(false);
|
||||
copyUser.setVisible(false);
|
||||
copyPass.setVisible(false);
|
||||
}
|
||||
else {
|
||||
String url = mEntry.getUrl();
|
||||
if (EmptyUtils.isNullOrEmpty(url)) {
|
||||
// disable button if url is not available
|
||||
gotoUrl.setVisible(false);
|
||||
}
|
||||
if ( mEntry.getUsername().length() == 0 ) {
|
||||
// disable button if username is not available
|
||||
copyUser.setVisible(false);
|
||||
}
|
||||
if ( mEntry.getPassword().length() == 0 ) {
|
||||
// disable button if password is not available
|
||||
copyPass.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setPasswordStyle() {
|
||||
TextView password = (TextView) findViewById(R.id.entry_password);
|
||||
|
||||
if ( mShowPassword ) {
|
||||
password.setTransformationMethod(null);
|
||||
} else {
|
||||
password.setTransformationMethod(PasswordTransformationMethod.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch ( item.getItemId() ) {
|
||||
case R.id.menu_donate:
|
||||
return MenuUtil.onDonationItemSelected(this);
|
||||
|
||||
case R.id.menu_toggle_pass:
|
||||
if ( mShowPassword ) {
|
||||
item.setTitle(R.string.menu_showpass);
|
||||
item.setIcon(R.drawable.ic_visibility_white_24dp);
|
||||
mShowPassword = false;
|
||||
} else {
|
||||
item.setTitle(R.string.menu_hide_password);
|
||||
item.setIcon(R.drawable.ic_visibility_off_white_24dp);
|
||||
mShowPassword = true;
|
||||
}
|
||||
setPasswordStyle();
|
||||
return true;
|
||||
|
||||
case R.id.menu_goto_url:
|
||||
String url;
|
||||
url = mEntry.getUrl();
|
||||
|
||||
// Default http:// if no protocol specified
|
||||
if ( ! url.contains("://") ) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
|
||||
try {
|
||||
Util.gotoUrl(this, url);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.menu_copy_user:
|
||||
timeoutCopyToClipboard(mEntry.getUsername(true, App.getDB().pm));
|
||||
return true;
|
||||
|
||||
case R.id.menu_copy_pass:
|
||||
timeoutCopyToClipboard(mEntry.getPassword(true, App.getDB().pm));
|
||||
return true;
|
||||
|
||||
case R.id.menu_lock:
|
||||
App.setShutdown();
|
||||
setResult(KeePass.EXIT_LOCK);
|
||||
finish();
|
||||
return true;
|
||||
|
||||
case android.R.id.home :
|
||||
finish(); // close this activity and return to preview activity (if there is any)
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void timeoutCopyToClipboard(String text) {
|
||||
try {
|
||||
Util.copyToClipboard(this, text);
|
||||
} catch (SamsungClipboardException e) {
|
||||
showSamsungDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
String sClipClear = prefs.getString(getString(R.string.clipboard_timeout_key), getString(R.string.clipboard_timeout_default));
|
||||
|
||||
long clipClearTime = Long.parseLong(sClipClear);
|
||||
|
||||
if ( clipClearTime > 0 ) {
|
||||
mTimer.schedule(new ClearClipboardTask(this, text), clipClearTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Setup to allow the toast to happen in the foreground
|
||||
final Handler uiThreadCallback = new Handler();
|
||||
|
||||
// Task which clears the clipboard, and sends a toast to the foreground.
|
||||
private class ClearClipboardTask extends TimerTask {
|
||||
|
||||
private final String mClearText;
|
||||
private final Context mCtx;
|
||||
|
||||
ClearClipboardTask(Context ctx, String clearText) {
|
||||
mClearText = clearText;
|
||||
mCtx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String currentClip = Util.getClipboard(mCtx);
|
||||
|
||||
if ( currentClip.equals(mClearText) ) {
|
||||
try {
|
||||
Util.copyToClipboard(mCtx, "");
|
||||
uiThreadCallback.post(new UIToastTask(mCtx, R.string.ClearClipboard));
|
||||
} catch (SamsungClipboardException e) {
|
||||
uiThreadCallback.post(new UIToastTask(mCtx, R.string.clipboard_error_clear));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showSamsungDialog() {
|
||||
String text = getString(R.string.clipboard_error).concat(System.getProperty("line.separator")).concat(getString(R.string.clipboard_error_url));
|
||||
SpannableString s = new SpannableString(text);
|
||||
TextView tv = new TextView(this);
|
||||
tv.setText(s);
|
||||
tv.setAutoLinkMask(RESULT_OK);
|
||||
tv.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
Linkify.addLinks(s, Linkify.WEB_URLS);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.clipboard_error_title)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.setView(tv)
|
||||
.show();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
import com.keepassdroid.utils.SprEngine;
|
||||
import com.keepassdroid.utils.SprEngineV4;
|
||||
import com.keepassdroid.view.EntrySection;
|
||||
|
||||
|
||||
public class EntryActivityV4 extends EntryActivity {
|
||||
|
||||
@Override
|
||||
protected void setEntryView() {
|
||||
setContentView(R.layout.entry_view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillData(boolean trimList) {
|
||||
super.fillData(trimList);
|
||||
|
||||
ViewGroup group = (ViewGroup) findViewById(R.id.extra_strings);
|
||||
|
||||
if (trimList) {
|
||||
group.removeAllViews();
|
||||
}
|
||||
|
||||
PwEntryV4 entry = (PwEntryV4) mEntry;
|
||||
|
||||
PwDatabase pm = App.getDB().pm;
|
||||
SprEngine spr = SprEngineV4.getInstance(pm);
|
||||
|
||||
// Display custom strings
|
||||
if (entry.strings.size() > 0) {
|
||||
for (Map.Entry<String, ProtectedString> pair : entry.strings.entrySet()) {
|
||||
String key = pair.getKey();
|
||||
|
||||
if (!PwEntryV4.IsStandardString(key)) {
|
||||
String text = pair.getValue().toString();
|
||||
View view = new EntrySection(this, null, key, spr.compile(text, entry, pm));
|
||||
group.addView(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwEntryV3;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
import com.keepassdroid.database.PwGroupV4;
|
||||
import com.keepassdroid.database.PwIconStandard;
|
||||
import com.keepassdroid.database.edit.AddEntry;
|
||||
import com.keepassdroid.database.edit.OnFinish;
|
||||
import com.keepassdroid.database.edit.RunnableOnFinish;
|
||||
import com.keepassdroid.database.edit.UpdateEntry;
|
||||
import com.keepassdroid.icons.Icons;
|
||||
import com.keepassdroid.utils.MenuUtil;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.keepassdroid.utils.Util;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class EntryEditActivity extends LockCloseHideActivity
|
||||
implements IconPickerFragment.IconPickerListener,
|
||||
GeneratePasswordFragment.GeneratePasswordListener {
|
||||
public static final String KEY_ENTRY = "entry";
|
||||
public static final String KEY_PARENT = "parent";
|
||||
|
||||
protected PwEntry mEntry;
|
||||
protected boolean mIsNew;
|
||||
protected int mSelectedIconID = -1;
|
||||
|
||||
public static void Launch(Activity act, PwEntry pw) {
|
||||
Intent i;
|
||||
if (pw instanceof PwEntryV3) {
|
||||
i = new Intent(act, EntryEditActivityV3.class);
|
||||
}
|
||||
else if (pw instanceof PwEntryV4) {
|
||||
i = new Intent(act, EntryEditActivityV4.class);
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Not yet implemented.");
|
||||
}
|
||||
|
||||
i.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID()));
|
||||
|
||||
act.startActivityForResult(i, 0);
|
||||
}
|
||||
|
||||
public static void Launch(Activity act, PwGroup pw) {
|
||||
Intent i;
|
||||
if (pw instanceof PwGroupV3) {
|
||||
i = new Intent(act, EntryEditActivityV3.class);
|
||||
EntryEditActivityV3.putParentId(i, KEY_PARENT, (PwGroupV3)pw);
|
||||
}
|
||||
else if (pw instanceof PwGroupV4) {
|
||||
i = new Intent(act, EntryEditActivityV4.class);
|
||||
EntryEditActivityV4.putParentId(i, KEY_PARENT, (PwGroupV4)pw);
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Not yet implemented.");
|
||||
}
|
||||
|
||||
act.startActivityForResult(i, 0);
|
||||
}
|
||||
|
||||
protected abstract PwGroupId getParentGroupId(Intent i, String key);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.entry_edit);
|
||||
setResult(KeePass.EXIT_NORMAL);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
toolbar.setTitle(getString(R.string.app_name));
|
||||
setSupportActionBar(toolbar);
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
// Likely the app has been killed exit the activity
|
||||
Database db = App.getDB();
|
||||
if ( ! db.Loaded() ) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent i = getIntent();
|
||||
byte[] uuidBytes = i.getByteArrayExtra(KEY_ENTRY);
|
||||
|
||||
PwDatabase pm = db.pm;
|
||||
if ( uuidBytes == null ) {
|
||||
|
||||
PwGroupId parentId = getParentGroupId(i, KEY_PARENT);
|
||||
PwGroup parent = pm.groups.get(parentId);
|
||||
mEntry = PwEntry.getInstance(parent);
|
||||
mIsNew = true;
|
||||
|
||||
} else {
|
||||
UUID uuid = Types.bytestoUUID(uuidBytes);
|
||||
mEntry = pm.entries.get(uuid);
|
||||
mIsNew = false;
|
||||
fillData();
|
||||
}
|
||||
|
||||
View scrollView = findViewById(R.id.entry_scroll);
|
||||
scrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
|
||||
|
||||
View iconButton = findViewById(R.id.icon_button);
|
||||
iconButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
IconPickerFragment.Launch(EntryEditActivity.this);
|
||||
}
|
||||
});
|
||||
|
||||
// Generate password button
|
||||
View generatePassword = findViewById(R.id.generate_button);
|
||||
generatePassword.setOnClickListener(new OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
GeneratePasswordFragment generatePasswordFragment = new GeneratePasswordFragment();
|
||||
generatePasswordFragment.show(getSupportFragmentManager(), "PasswordGeneratorFragment");
|
||||
}
|
||||
});
|
||||
|
||||
// Save button
|
||||
View save = findViewById(R.id.entry_save);
|
||||
save.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
EntryEditActivity act = EntryEditActivity.this;
|
||||
|
||||
if (!validateBeforeSaving()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PwEntry newEntry = populateNewEntry();
|
||||
|
||||
if ( newEntry.getTitle().equals(mEntry.getTitle()) ) {
|
||||
setResult(KeePass.EXIT_REFRESH);
|
||||
} else {
|
||||
setResult(KeePass.EXIT_REFRESH_TITLE);
|
||||
}
|
||||
|
||||
RunnableOnFinish task;
|
||||
OnFinish onFinish = act.new AfterSave(new Handler());
|
||||
|
||||
if ( mIsNew ) {
|
||||
task = AddEntry.getInstance(EntryEditActivity.this, App.getDB(), newEntry, onFinish);
|
||||
} else {
|
||||
task = new UpdateEntry(EntryEditActivity.this, App.getDB(), mEntry, newEntry, onFinish);
|
||||
}
|
||||
ProgressTask pt = new ProgressTask(act, task, R.string.saving_database);
|
||||
pt.run();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean validateBeforeSaving() {
|
||||
// Require title
|
||||
String title = Util.getEditText(this, R.id.entry_title);
|
||||
if ( title.length() == 0 ) {
|
||||
Toast.makeText(this, R.string.error_title_required, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate password
|
||||
String pass = Util.getEditText(this, R.id.entry_password);
|
||||
String conf = Util.getEditText(this, R.id.entry_confpassword);
|
||||
if ( ! pass.equals(conf) ) {
|
||||
Toast.makeText(this, R.string.error_pass_match, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected PwEntry populateNewEntry() {
|
||||
return populateNewEntry(null);
|
||||
}
|
||||
|
||||
protected PwEntry populateNewEntry(PwEntry entry) {
|
||||
PwEntry newEntry;
|
||||
if (entry == null) {
|
||||
newEntry = mEntry.clone(true);
|
||||
}
|
||||
else {
|
||||
newEntry = entry;
|
||||
}
|
||||
|
||||
Date now = Calendar.getInstance().getTime();
|
||||
newEntry.setLastAccessTime(now);
|
||||
newEntry.setLastModificationTime(now);
|
||||
|
||||
PwDatabase db = App.getDB().pm;
|
||||
newEntry.setTitle(Util.getEditText(this, R.id.entry_title), db);
|
||||
if(mSelectedIconID != -1)
|
||||
newEntry.setIcon(new PwIconStandard(mSelectedIconID));
|
||||
newEntry.setUrl(Util.getEditText(this, R.id.entry_url), db);
|
||||
newEntry.setUsername(Util.getEditText(this, R.id.entry_user_name), db);
|
||||
newEntry.setNotes(Util.getEditText(this, R.id.entry_comment), db);
|
||||
newEntry.setPassword(Util.getEditText(this, R.id.entry_password), db);
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
MenuUtil.donationMenuInflater(inflater, menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch ( item.getItemId() ) {
|
||||
case R.id.menu_donate:
|
||||
return MenuUtil.onDonationItemSelected(this);
|
||||
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
protected void fillData() {
|
||||
ImageButton currIconButton = (ImageButton) findViewById(R.id.icon_button);
|
||||
App.getDB().drawFactory.assignDrawableTo(currIconButton, getResources(), mEntry.getIcon());
|
||||
|
||||
populateText(R.id.entry_title, mEntry.getTitle());
|
||||
populateText(R.id.entry_user_name, mEntry.getUsername());
|
||||
populateText(R.id.entry_url, mEntry.getUrl());
|
||||
|
||||
String password = mEntry.getPassword();
|
||||
populateText(R.id.entry_password, password);
|
||||
populateText(R.id.entry_confpassword, password);
|
||||
|
||||
populateText(R.id.entry_comment, mEntry.getNotes());
|
||||
}
|
||||
|
||||
private void populateText(int viewId, String text) {
|
||||
TextView tv = (TextView) findViewById(viewId);
|
||||
tv.setText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iconPicked(Bundle bundle) {
|
||||
mSelectedIconID = bundle.getInt(IconPickerFragment.KEY_ICON_ID);
|
||||
ImageButton currIconButton = (ImageButton) findViewById(R.id.icon_button);
|
||||
currIconButton.setImageResource(Icons.iconToResId(mSelectedIconID));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptPassword(Bundle bundle) {
|
||||
String generatedPassword = bundle.getString(GeneratePasswordFragment.KEY_PASSWORD_ID);
|
||||
EditText password = (EditText) findViewById(R.id.entry_password);
|
||||
EditText confPassword = (EditText) findViewById(R.id.entry_confpassword);
|
||||
|
||||
password.setText(generatedPassword);
|
||||
confPassword.setText(generatedPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelPassword(Bundle bundle) {
|
||||
// Do nothing here
|
||||
}
|
||||
|
||||
private final class AfterSave extends OnFinish {
|
||||
|
||||
AfterSave(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if ( mSuccess ) {
|
||||
finish();
|
||||
} else {
|
||||
displayMessage(EntryEditActivity.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupIdV3;
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
|
||||
public class EntryEditActivityV3 extends EntryEditActivity {
|
||||
|
||||
@Override
|
||||
protected PwEntry populateNewEntry(PwEntry entry) {
|
||||
PwEntry newEntry = super.populateNewEntry(entry);
|
||||
|
||||
if (mSelectedIconID == -1) {
|
||||
if (mIsNew) {
|
||||
newEntry.icon = App.getDB().pm.iconFactory.getIcon(0);
|
||||
}
|
||||
else {
|
||||
// Keep previous icon, if no new one was selected
|
||||
newEntry.icon = mEntry.icon;
|
||||
}
|
||||
}
|
||||
else {
|
||||
newEntry.icon = App.getDB().pm.iconFactory.getIcon(mSelectedIconID);
|
||||
}
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
protected static void putParentId(Intent i, String parentKey, PwGroupV3 parent) {
|
||||
i.putExtra(parentKey, parent.groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PwGroupId getParentGroupId(Intent i, String key) {
|
||||
int groupId = i.getIntExtra(key, -1);
|
||||
|
||||
return new PwGroupIdV3(groupId);
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupIdV4;
|
||||
import com.keepassdroid.database.PwGroupV4;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
import com.keepassdroid.utils.Types;
|
||||
import com.keepassdroid.view.EntryEditSection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
public class EntryEditActivityV4 extends EntryEditActivity {
|
||||
|
||||
private ScrollView scroll;
|
||||
private LayoutInflater inflater;
|
||||
|
||||
protected static void putParentId(Intent i, String parentKey, PwGroupV4 parent) {
|
||||
PwGroupId id = parent.getId();
|
||||
PwGroupIdV4 id4 = (PwGroupIdV4) id;
|
||||
|
||||
i.putExtra(parentKey, Types.UUIDtoBytes(id4.getId()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PwGroupId getParentGroupId(Intent i, String key) {
|
||||
byte[] buf = i.getByteArrayExtra(key);
|
||||
UUID id = Types.bytestoUUID(buf);
|
||||
|
||||
return new PwGroupIdV4(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
scroll = (ScrollView) findViewById(R.id.entry_scroll);
|
||||
|
||||
View add = findViewById(R.id.add_advanced);
|
||||
add.setVisibility(View.VISIBLE);
|
||||
add.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
|
||||
EntryEditSection ees = (EntryEditSection) inflater.inflate(R.layout.entry_edit_section, container, false);
|
||||
ees.setData("", new ProtectedString(false, ""));
|
||||
container.addView(ees);
|
||||
|
||||
// Scroll bottom
|
||||
scroll.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scroll.fullScroll(ScrollView.FOCUS_DOWN);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillData() {
|
||||
super.fillData();
|
||||
|
||||
PwEntryV4 entry = (PwEntryV4) mEntry;
|
||||
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
|
||||
if (entry.strings.size() > 0) {
|
||||
for (Entry<String, ProtectedString> pair : entry.strings.entrySet()) {
|
||||
String key = pair.getKey();
|
||||
|
||||
if (!PwEntryV4.IsStandardString(key)) {
|
||||
EntryEditSection ees = (EntryEditSection) inflater.inflate(R.layout.entry_edit_section, container, false);
|
||||
ees.setData(key, pair.getValue());
|
||||
container.addView(ees);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected PwEntry populateNewEntry() {
|
||||
PwEntryV4 newEntry = (PwEntryV4) mEntry.clone(true);
|
||||
newEntry.history = (ArrayList<PwEntryV4>) newEntry.history.clone();
|
||||
newEntry.createBackup((PwDatabaseV4)App.getDB().pm);
|
||||
|
||||
newEntry = (PwEntryV4) super.populateNewEntry(newEntry);
|
||||
|
||||
Map<String, ProtectedString> strings = newEntry.strings;
|
||||
|
||||
// Delete all new standard strings
|
||||
Iterator<Entry<String, ProtectedString>> iter = strings.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entry<String, ProtectedString> pair = iter.next();
|
||||
if (!PwEntryV4.IsStandardString(pair.getKey())) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
View view = container.getChildAt(i);
|
||||
|
||||
TextView keyView = (TextView)view.findViewById(R.id.title);
|
||||
String key = keyView.getText().toString();
|
||||
|
||||
TextView valueView = (TextView)view.findViewById(R.id.value);
|
||||
String value = valueView.getText().toString();
|
||||
|
||||
CheckBox cb = (CheckBox)view.findViewById(R.id.protection);
|
||||
boolean protect = cb.isChecked();
|
||||
|
||||
strings.put(key, new ProtectedString(protect, value));
|
||||
}
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
public void deleteAdvancedString(View view) {
|
||||
ViewGroup section = (ViewGroup) view.getParent();
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
ViewGroup ees = (ViewGroup) container.getChildAt(i);
|
||||
if (ees == section) {
|
||||
container.removeViewAt(i);
|
||||
container.invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean validateBeforeSaving() {
|
||||
if(!super.validateBeforeSaving()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container);
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
EntryEditSection ees = (EntryEditSection) container.getChildAt(i);
|
||||
|
||||
TextView keyView = (TextView) ees.findViewById(R.id.title);
|
||||
CharSequence key = keyView.getText();
|
||||
|
||||
if (key == null || key.length() == 0) {
|
||||
Toast.makeText(this, R.string.error_string_key, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwDatabaseV3;
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
import com.keepassdroid.database.PwGroupV4;
|
||||
import com.keepassdroid.database.edit.AddGroup;
|
||||
import com.keepassdroid.dialog.ReadOnlyDialog;
|
||||
import com.keepassdroid.view.ClickView;
|
||||
import com.keepassdroid.view.GroupAddEntryView;
|
||||
import com.keepassdroid.view.GroupRootView;
|
||||
import com.keepassdroid.view.GroupViewOnlyView;
|
||||
|
||||
public abstract class GroupActivity extends GroupBaseActivity
|
||||
implements GroupEditFragment.CreateGroupListener, IconPickerFragment.IconPickerListener {
|
||||
|
||||
private static final String TAG_CREATE_GROUP = "TAG_CREATE_GROUP";
|
||||
|
||||
protected boolean addGroupEnabled = false;
|
||||
protected boolean addEntryEnabled = false;
|
||||
protected boolean isRoot = false;
|
||||
protected boolean readOnly = false;
|
||||
|
||||
private static final String TAG = "Group Activity:";
|
||||
|
||||
public static void Launch(Activity act) {
|
||||
Launch(act, null);
|
||||
}
|
||||
|
||||
public static void Launch(Activity act, PwGroup group) {
|
||||
Intent i;
|
||||
|
||||
// Need to use PwDatabase since tree may be null
|
||||
PwDatabase db = App.getDB().pm;
|
||||
if ( db instanceof PwDatabaseV3 ) {
|
||||
i = new Intent(act, GroupActivityV3.class);
|
||||
|
||||
if ( group != null ) {
|
||||
PwGroupV3 g = (PwGroupV3) group;
|
||||
i.putExtra(KEY_ENTRY, g.groupId);
|
||||
}
|
||||
} else if ( db instanceof PwDatabaseV4 ) {
|
||||
i = new Intent(act, GroupActivityV4.class);
|
||||
|
||||
if ( group != null ) {
|
||||
PwGroupV4 g = (PwGroupV4) group;
|
||||
i.putExtra(KEY_ENTRY, g.uuid.toString());
|
||||
}
|
||||
} else {
|
||||
// Reached if db is null
|
||||
Log.d(TAG, "Tried to launch with null db");
|
||||
return;
|
||||
}
|
||||
|
||||
act.startActivityForResult(i,0);
|
||||
}
|
||||
|
||||
protected abstract PwGroupId retrieveGroupId(Intent i);
|
||||
|
||||
protected void setupButtons() {
|
||||
addGroupEnabled = !readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if ( isFinishing() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setResult(KeePass.EXIT_NORMAL);
|
||||
|
||||
Log.w(TAG, "Creating tree view");
|
||||
Intent intent = getIntent();
|
||||
|
||||
PwGroupId id = retrieveGroupId(intent);
|
||||
|
||||
Database db = App.getDB();
|
||||
readOnly = db.readOnly;
|
||||
PwGroup root = db.pm.rootGroup;
|
||||
if ( id == null ) {
|
||||
mGroup = root;
|
||||
} else {
|
||||
mGroup = db.pm.groups.get(id);
|
||||
}
|
||||
|
||||
Log.w(TAG, "Retrieved tree");
|
||||
if ( mGroup == null ) {
|
||||
Log.w(TAG, "Group was null");
|
||||
return;
|
||||
}
|
||||
|
||||
isRoot = mGroup == root;
|
||||
|
||||
setupButtons();
|
||||
|
||||
if ( addGroupEnabled && addEntryEnabled ) {
|
||||
setContentView(new GroupAddEntryView(this));
|
||||
} else if ( addGroupEnabled ) {
|
||||
setContentView(new GroupRootView(this));
|
||||
} else if ( addEntryEnabled ) {
|
||||
setContentView(new GroupAddEntryView(this));
|
||||
View addGroup = findViewById(R.id.add_group);
|
||||
addGroup.setVisibility(View.GONE);
|
||||
} else {
|
||||
setContentView(new GroupViewOnlyView(this));
|
||||
}
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
toolbar.setTitle("");
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
if ( mGroup.getParent() != null )
|
||||
toolbar.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp);
|
||||
|
||||
Log.w(TAG, "Set view");
|
||||
|
||||
if ( addGroupEnabled ) {
|
||||
// Add Group button
|
||||
View addGroup = findViewById(R.id.add_group);
|
||||
addGroup.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
GroupEditFragment groupEditFragment = new GroupEditFragment();
|
||||
groupEditFragment.show(getSupportFragmentManager(), TAG_CREATE_GROUP);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ( addEntryEnabled ) {
|
||||
// Add Entry button
|
||||
View addEntry = findViewById(R.id.add_entry);
|
||||
addEntry.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
EntryEditActivity.Launch(GroupActivity.this, mGroup);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setGroupTitle();
|
||||
setGroupIcon();
|
||||
|
||||
setListAdapter(new PwGroupListAdapter(this, mGroup));
|
||||
registerForContextMenu(getListView());
|
||||
Log.w(TAG, "Finished creating tree");
|
||||
|
||||
if (isRoot) {
|
||||
showWarnings();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
|
||||
AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
|
||||
ClickView cv = (ClickView) acmi.targetView;
|
||||
cv.onCreateMenu(menu, menuInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) item.getMenuInfo();
|
||||
ClickView cv = (ClickView) acmi.targetView;
|
||||
|
||||
return cv.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void approveCreateGroup(Bundle bundle) {
|
||||
String GroupName = bundle.getString(GroupEditFragment.KEY_NAME);
|
||||
int GroupIconID = bundle.getInt(GroupEditFragment.KEY_ICON_ID);
|
||||
GroupActivity act = GroupActivity.this;
|
||||
Handler handler = new Handler();
|
||||
AddGroup task = AddGroup.getInstance(this, App.getDB(), GroupName, GroupIconID, mGroup, act.new RefreshTask(handler), false);
|
||||
ProgressTask pt = new ProgressTask(act, task, R.string.saving_database);
|
||||
pt.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelCreateGroup(Bundle bundle) {
|
||||
// Do nothing here
|
||||
}
|
||||
|
||||
@Override
|
||||
// For icon in create tree dialog
|
||||
public void iconPicked(Bundle bundle) {
|
||||
GroupEditFragment groupEditFragment = (GroupEditFragment) getSupportFragmentManager().findFragmentByTag(TAG_CREATE_GROUP);
|
||||
if (groupEditFragment != null) {
|
||||
groupEditFragment.iconPicked(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
protected void showWarnings() {
|
||||
if (App.getDB().readOnly) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
if (prefs.getBoolean(getString(R.string.show_read_only_warning), true)) {
|
||||
Dialog dialog = new ReadOnlyDialog(this);
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.keepassdroid.database.PwGroupId;
|
||||
import com.keepassdroid.database.PwGroupIdV4;
|
||||
|
||||
public class GroupActivityV4 extends GroupActivity {
|
||||
|
||||
@Override
|
||||
protected PwGroupId retrieveGroupId(Intent i) {
|
||||
String uuid = i.getStringExtra(KEY_ENTRY);
|
||||
|
||||
if ( uuid == null || uuid.length() == 0 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PwGroupIdV4(UUID.fromString(uuid));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupButtons() {
|
||||
super.setupButtons();
|
||||
addEntryEnabled = !readOnly;
|
||||
}
|
||||
}
|
||||
@@ -1,345 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Build;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.compat.ActivityCompat;
|
||||
import com.keepassdroid.compat.EditorCompat;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.edit.OnFinish;
|
||||
import com.keepassdroid.search.SearchResultsActivity;
|
||||
import com.keepassdroid.utils.MenuUtil;
|
||||
import com.keepassdroid.view.AssignPasswordHelper;
|
||||
import com.keepassdroid.view.ClickView;
|
||||
import com.keepassdroid.view.GroupViewOnlyView;
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
public abstract class GroupBaseActivity extends LockCloseListActivity
|
||||
implements AssignMasterKeyDialog.AssignPasswordDialogListener {
|
||||
protected ListView mList;
|
||||
protected ListAdapter mAdapter;
|
||||
|
||||
public static final String KEY_ENTRY = "entry";
|
||||
|
||||
private SharedPreferences prefs;
|
||||
|
||||
protected PwGroup mGroup;
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
refreshIfDirty();
|
||||
}
|
||||
|
||||
public void refreshIfDirty() {
|
||||
Database db = App.getDB();
|
||||
if ( db.dirty.contains(mGroup) ) {
|
||||
db.dirty.remove(mGroup);
|
||||
((BaseAdapter) mAdapter).notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
ClickView cv = (ClickView) mAdapter.getView(position, null, null);
|
||||
cv.onClick();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Likely the app has been killed exit the activity
|
||||
if ( ! App.getDB().Loaded() ) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
ActivityCompat.invalidateOptionsMenu(this);
|
||||
|
||||
setContentView(new GroupViewOnlyView(this));
|
||||
setResult(KeePass.EXIT_NORMAL);
|
||||
|
||||
styleScrollBars();
|
||||
|
||||
}
|
||||
|
||||
protected void styleScrollBars() {
|
||||
ensureCorrectListView();
|
||||
mList.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
|
||||
mList.setTextFilterEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
protected void setGroupTitle() {
|
||||
if ( mGroup != null ) {
|
||||
String name = mGroup.getName();
|
||||
TextView tv = (TextView) findViewById(R.id.group_name);
|
||||
if ( name != null && name.length() > 0 ) {
|
||||
if ( tv != null ) {
|
||||
tv.setText(name);
|
||||
}
|
||||
} else {
|
||||
if ( tv != null ) {
|
||||
tv.setText(getText(R.string.root));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setGroupIcon() {
|
||||
if (mGroup != null) {
|
||||
ImageView iv = (ImageView) findViewById(R.id.icon);
|
||||
App.getDB().drawFactory.assignDrawableTo(iv, getResources(), mGroup.getIcon());
|
||||
}
|
||||
}
|
||||
|
||||
protected void setListAdapter(ListAdapter adapter) {
|
||||
ensureCorrectListView();
|
||||
mAdapter = adapter;
|
||||
mList.setAdapter(adapter);
|
||||
}
|
||||
|
||||
protected ListView getListView() {
|
||||
ensureCorrectListView();
|
||||
return mList;
|
||||
}
|
||||
|
||||
private void ensureCorrectListView(){
|
||||
mList = (ListView)findViewById(R.id.group_list);
|
||||
if (mList != null) {
|
||||
mList.setOnItemClickListener(
|
||||
new AdapterView.OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
onListItemClick((ListView) parent, v, position, id);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.search, menu);
|
||||
MenuUtil.donationMenuInflater(inflater, menu);
|
||||
inflater.inflate(R.menu.tree, menu);
|
||||
inflater.inflate(R.menu.database, menu);
|
||||
inflater.inflate(R.menu.default_menu, menu);
|
||||
|
||||
// Get the SearchView and set the searchable configuration
|
||||
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
assert searchManager != null;
|
||||
|
||||
MenuItem searchItem = menu.findItem(R.id.menu_search);
|
||||
SearchView searchView = null;
|
||||
if (searchItem != null) {
|
||||
searchView = (SearchView) searchItem.getActionView();
|
||||
}
|
||||
if (searchView != null) {
|
||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, SearchResultsActivity.class)));
|
||||
searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setSortMenuText(Menu menu) {
|
||||
boolean sortByName = false;
|
||||
|
||||
// Will be null if onPrepareOptionsMenu is called before onCreate
|
||||
if (prefs != null) {
|
||||
sortByName = prefs.getBoolean(getString(R.string.sort_key), getResources().getBoolean(R.bool.sort_default));
|
||||
}
|
||||
|
||||
int resId;
|
||||
if ( sortByName ) {
|
||||
resId = R.string.sort_db;
|
||||
} else {
|
||||
resId = R.string.sort_name;
|
||||
}
|
||||
|
||||
menu.findItem(R.id.menu_sort).setTitle(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
if ( ! super.onPrepareOptionsMenu(menu) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setSortMenuText(menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch ( item.getItemId() ) {
|
||||
|
||||
case R.id.menu_search:
|
||||
onSearchRequested();
|
||||
return true;
|
||||
|
||||
case R.id.menu_sort:
|
||||
toggleSort();
|
||||
return true;
|
||||
|
||||
case R.id.menu_lock:
|
||||
App.setShutdown();
|
||||
setResult(KeePass.EXIT_LOCK);
|
||||
finish();
|
||||
return true;
|
||||
|
||||
case R.id.menu_change_master_key:
|
||||
setPassword();
|
||||
return true;
|
||||
|
||||
default:
|
||||
MenuUtil.onDefaultMenuOptionsItemSelected(this, item);
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleSort() {
|
||||
// Toggle setting
|
||||
String sortKey = getString(R.string.sort_key);
|
||||
boolean sortByName = prefs.getBoolean(sortKey, getResources().getBoolean(R.bool.sort_default));
|
||||
Editor editor = prefs.edit();
|
||||
editor.putBoolean(sortKey, ! sortByName);
|
||||
EditorCompat.apply(editor);
|
||||
|
||||
// Refresh menu titles
|
||||
ActivityCompat.invalidateOptionsMenu(this);
|
||||
|
||||
// Mark all groups as dirty now to refresh them on load
|
||||
Database db = App.getDB();
|
||||
db.markAllGroupsAsDirty();
|
||||
// We'll manually refresh this tree so we can remove it
|
||||
db.dirty.remove(mGroup);
|
||||
|
||||
// Tell the adapter to refresh it's list
|
||||
((BaseAdapter) mAdapter).notifyDataSetChanged();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssignKeyDialogPositiveClick(
|
||||
boolean masterPasswordChecked, String masterPassword,
|
||||
boolean keyFileChecked, Uri keyFile) {
|
||||
|
||||
AssignPasswordHelper assignPasswordHelper =
|
||||
new AssignPasswordHelper(this,
|
||||
masterPassword, keyFile);
|
||||
assignPasswordHelper.assignPasswordInDatabase(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssignKeyDialogNegativeClick(
|
||||
boolean masterPasswordChecked, String masterPassword,
|
||||
boolean keyFileChecked, Uri keyFile) {
|
||||
|
||||
}
|
||||
|
||||
private void setPassword() {
|
||||
AssignMasterKeyDialog dialog = new AssignMasterKeyDialog();
|
||||
dialog.show(getSupportFragmentManager(), "passwordDialog");
|
||||
}
|
||||
|
||||
public class RefreshTask extends OnFinish {
|
||||
public RefreshTask(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if ( mSuccess) {
|
||||
refreshIfDirty();
|
||||
} else {
|
||||
displayMessage(GroupBaseActivity.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
|
||||
/*
|
||||
* ACTION_SEARCH automatically forces a new task. This occurs when you open a kdb file in
|
||||
* another app such as Files or GoogleDrive and then Search for an entry. Here we remove the
|
||||
* FLAG_ACTIVITY_NEW_TASK flag bit allowing search to open it's activity in the current task.
|
||||
*/
|
||||
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||
int flags = intent.getFlags();
|
||||
flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
intent.setFlags(flags);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
super.startActivityForResult(intent, requestCode, options);
|
||||
}
|
||||
}
|
||||
|
||||
public class AfterDeleteGroup extends OnFinish {
|
||||
public AfterDeleteGroup(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if ( mSuccess) {
|
||||
refreshIfDirty();
|
||||
} else {
|
||||
mHandler.post(new UIToastTask(GroupBaseActivity.this, "Unrecoverable error: " + mMessage));
|
||||
App.setShutdown();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import com.keepassdroid.compat.BuildCompat;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
|
||||
/**
|
||||
* Locking Close Activity that sets FLAG_SECURE to prevent screenshots, and from
|
||||
* appearing in the recent app preview
|
||||
* @author Brian Pellin
|
||||
*
|
||||
*/
|
||||
public abstract class LockCloseHideActivity extends LockCloseActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Several gingerbread devices have problems with FLAG_SECURE
|
||||
int ver = BuildCompat.getSdkVersion();
|
||||
if (ver >= BuildCompat.VERSION_CODE_ICE_CREAM_SANDWICH || ver < BuildCompat.VERSION_CODE_GINGERBREAD) {
|
||||
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.settings.PrefsUtil;
|
||||
import com.keepassdroid.stylish.StylishActivity;
|
||||
import com.keepassdroid.timeout.TimeoutHelper;
|
||||
import com.kunzisoft.keepass.KeePass;
|
||||
|
||||
|
||||
public abstract class LockingActivity extends StylishActivity {
|
||||
|
||||
private ScreenReceiver screenReceiver;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (PrefsUtil.isLockDatabaseWhenScreenShutOffEnable(this)) {
|
||||
screenReceiver = new ScreenReceiver();
|
||||
registerReceiver(screenReceiver, new IntentFilter((Intent.ACTION_SCREEN_OFF)));
|
||||
} else
|
||||
screenReceiver = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
checkShutdown();
|
||||
TimeoutHelper.resume(this);
|
||||
}
|
||||
|
||||
private void checkShutdown() {
|
||||
if ( App.isShutdown() && App.getDB().Loaded() ) {
|
||||
setResult(KeePass.EXIT_LOCK);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
TimeoutHelper.pause(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if(screenReceiver != null)
|
||||
unregisterReceiver(screenReceiver);
|
||||
}
|
||||
|
||||
public class ScreenReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
if(intent.getAction() != null) {
|
||||
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
|
||||
if (PrefsUtil.isLockDatabaseWhenScreenShutOffEnable(LockingActivity.this)) {
|
||||
App.setShutdown();
|
||||
checkShutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.view.PwEntryView;
|
||||
import com.keepassdroid.view.PwGroupView;
|
||||
|
||||
public class PwGroupListAdapter extends BaseAdapter {
|
||||
|
||||
private GroupBaseActivity mAct;
|
||||
private PwGroup mGroup;
|
||||
private List<PwGroup> groupsForViewing;
|
||||
private List<PwEntry> entriesForViewing;
|
||||
private Comparator<PwEntry> entryComp = new PwEntry.EntryNameComparator();
|
||||
private Comparator<PwGroup> groupComp = new PwGroup.GroupNameComparator();
|
||||
private SharedPreferences prefs;
|
||||
|
||||
public PwGroupListAdapter(GroupBaseActivity act, PwGroup group) {
|
||||
mAct = act;
|
||||
mGroup = group;
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(act);
|
||||
|
||||
filterAndSort();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
super.notifyDataSetChanged();
|
||||
|
||||
filterAndSort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetInvalidated() {
|
||||
super.notifyDataSetInvalidated();
|
||||
|
||||
filterAndSort();
|
||||
}
|
||||
|
||||
private void filterAndSort() {
|
||||
entriesForViewing = new ArrayList<PwEntry>();
|
||||
|
||||
for (int i = 0; i < mGroup.childEntries.size(); i++) {
|
||||
PwEntry entry = mGroup.childEntries.get(i);
|
||||
if ( ! entry.isMetaStream() ) {
|
||||
entriesForViewing.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
boolean sortLists = prefs.getBoolean(mAct.getString(R.string.sort_key), mAct.getResources().getBoolean(R.bool.sort_default));
|
||||
if ( sortLists ) {
|
||||
groupsForViewing = new ArrayList<PwGroup>(mGroup.childGroups);
|
||||
|
||||
Collections.sort(entriesForViewing, entryComp);
|
||||
Collections.sort(groupsForViewing, groupComp);
|
||||
} else {
|
||||
groupsForViewing = mGroup.childGroups;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
|
||||
return groupsForViewing.size() + entriesForViewing.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
int size = groupsForViewing.size();
|
||||
|
||||
if ( position < size ) {
|
||||
return createGroupView(position, convertView);
|
||||
} else {
|
||||
return createEntryView(position - size, convertView);
|
||||
}
|
||||
}
|
||||
|
||||
private View createGroupView(int position, View convertView) {
|
||||
PwGroup group = groupsForViewing.get(position);
|
||||
PwGroupView gv;
|
||||
|
||||
if (convertView == null || !(convertView instanceof PwGroupView)) {
|
||||
|
||||
gv = PwGroupView.getInstance(mAct, group);
|
||||
}
|
||||
else {
|
||||
gv = (PwGroupView) convertView;
|
||||
gv.convertView(group);
|
||||
|
||||
}
|
||||
|
||||
return gv;
|
||||
}
|
||||
|
||||
private PwEntryView createEntryView(int position, View convertView) {
|
||||
PwEntry entry = entriesForViewing.get(position);
|
||||
PwEntryView ev;
|
||||
|
||||
if (convertView == null || !(convertView instanceof PwEntryView)) {
|
||||
ev = PwEntryView.getInstance(mAct, entry, position);
|
||||
}
|
||||
else {
|
||||
ev = (PwEntryView) convertView;
|
||||
ev.convertView(entry, position);
|
||||
}
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
public class UnavailableFeatureDialog extends DialogFragment {
|
||||
|
||||
private static final String MIN_REQUIRED_VERSION_ARG = "MIN_REQUIRED_VERSION_ARG";
|
||||
private int minVersionRequired = Build.VERSION_CODES.M;
|
||||
|
||||
public static UnavailableFeatureDialog getInstance(int minVersionRequired) {
|
||||
UnavailableFeatureDialog fragment = new UnavailableFeatureDialog();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(MIN_REQUIRED_VERSION_ARG, minVersionRequired);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
if (getArguments() != null && getArguments().containsKey(MIN_REQUIRED_VERSION_ARG))
|
||||
minVersionRequired = getArguments().getInt(MIN_REQUIRED_VERSION_ARG);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
String message = getString(R.string.unavailable_feature_text).concat("\n");
|
||||
if(Build.VERSION.SDK_INT <= minVersionRequired)
|
||||
message = message.concat(getString(R.string.unavailable_feature_version,
|
||||
Build.VERSION.SDK_INT,
|
||||
minVersionRequired));
|
||||
else
|
||||
message = message.concat(getString(R.string.unavailable_feature_hardware));
|
||||
|
||||
builder.setMessage(message)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) { }
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.keepassdroid;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
interface UriIntentInitTaskCallback {
|
||||
void onPostInitTask(Uri dbUri, Uri keyFileUri, Integer errorStringId);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.compat;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
public class ActivityCompat {
|
||||
private static Method invalidateOptMenu;
|
||||
|
||||
static {
|
||||
try {
|
||||
invalidateOptMenu = Activity.class.getMethod("invalidateOptionsMenu", (Class<Activity>[]) null);
|
||||
} catch (Exception e) {
|
||||
// Do nothing if method dosen't exist
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidateOptionsMenu(Activity act) {
|
||||
if (invalidateOptMenu != null) {
|
||||
try {
|
||||
invalidateOptMenu.invoke(act, (Object[]) null);
|
||||
} catch (Exception e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin.
|
||||
*
|
||||
* This file is part of KeePassDroid.
|
||||
*
|
||||
* KeePassDroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.compat;
|
||||
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
|
||||
public class KeyGenParameterSpecCompat {
|
||||
private static Class builder;
|
||||
private static Constructor buildConst;
|
||||
private static Method builderBuild;
|
||||
private static Method setBlockModes;
|
||||
private static Method setUserAuthReq;
|
||||
private static Method setEncPad;
|
||||
|
||||
private static boolean available;
|
||||
|
||||
static {
|
||||
try {
|
||||
builder = Class.forName("android.security.keystore.KeyGenParameterSpec$Builder");
|
||||
buildConst = builder.getConstructor(String.class, int.class);
|
||||
builderBuild = builder.getMethod("build", (Class [])null);
|
||||
setBlockModes = builder.getMethod("setBlockModes", String[].class);
|
||||
setUserAuthReq = builder.getMethod("setUserAuthenticationRequired", new Class []{boolean.class});
|
||||
setEncPad = builder.getMethod("setEncryptionPaddings", String[].class);
|
||||
|
||||
|
||||
available = true;
|
||||
} catch (Exception e) {
|
||||
available = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static AlgorithmParameterSpec build(String keystoreAlias, int purpose, String blockMode,
|
||||
boolean userAuthReq, String encPadding) {
|
||||
|
||||
if (!available) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Object inst = buildConst.newInstance(keystoreAlias, purpose);
|
||||
inst = setBlockModes.invoke(inst, new Object[] {new String[] {blockMode}});
|
||||
inst = setUserAuthReq.invoke(inst, userAuthReq);
|
||||
inst = setEncPad.invoke(inst, new Object[] {new String[] {encPadding}});
|
||||
|
||||
return (AlgorithmParameterSpec) builderBuild.invoke(inst, null);
|
||||
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin.
|
||||
*
|
||||
* This file is part of KeePassDroid.
|
||||
*
|
||||
* KeePassDroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.compat;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class KeyguardManagerCompat {
|
||||
private static Method isKeyguardSecure;
|
||||
|
||||
private static boolean available;
|
||||
|
||||
static {
|
||||
try {
|
||||
isKeyguardSecure = KeyguardManager.class.getMethod("isKeyguardSecure", (Class[]) null);
|
||||
|
||||
available = true;
|
||||
} catch (Exception e) {
|
||||
available = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isKeyguardSecure(KeyguardManager inst) {
|
||||
if (!available) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return (boolean) isKeyguardSecure.invoke(inst, null);
|
||||
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.keepassdroid.database.iterator.EntrySearchStringIterator;
|
||||
|
||||
public abstract class EntrySearchHandler extends EntryHandler<PwEntry> {
|
||||
private List<PwEntry> listStorage;
|
||||
private SearchParameters sp;
|
||||
private Date now;
|
||||
|
||||
public static EntrySearchHandler getInstance(PwGroup group, SearchParameters sp, List<PwEntry> listStorage) {
|
||||
if (group instanceof PwGroupV3) {
|
||||
return new EntrySearchHandlerV4(sp, listStorage);
|
||||
} else if (group instanceof PwGroupV4) {
|
||||
return new EntrySearchHandlerV4(sp, listStorage);
|
||||
} else {
|
||||
throw new RuntimeException("Not implemented.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected EntrySearchHandler(SearchParameters sp, List<PwEntry> listStorage) {
|
||||
this.sp = sp;
|
||||
this.listStorage = listStorage;
|
||||
now = new Date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean operate(PwEntry entry) {
|
||||
if (sp.respectEntrySearchingDisabled && !entry.isSearchingEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sp.excludeExpired && entry.expires() && now.after(entry.getExpiryTime())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String term = sp.searchString;
|
||||
if (sp.ignoreCase) {
|
||||
term = term.toLowerCase();
|
||||
}
|
||||
|
||||
if (searchStrings(entry, term)) {
|
||||
listStorage.add(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sp.searchInGroupNames) {
|
||||
PwGroup parent = entry.getParent();
|
||||
if (parent != null) {
|
||||
String groupName = parent.getName();
|
||||
if (groupName != null) {
|
||||
if (sp.ignoreCase) {
|
||||
groupName = groupName.toLowerCase();
|
||||
}
|
||||
|
||||
if (groupName.indexOf(term) >= 0) {
|
||||
listStorage.add(entry);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (searchID(entry)) {
|
||||
listStorage.add(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean searchID(PwEntry entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean searchStrings(PwEntry entry, String term) {
|
||||
EntrySearchStringIterator iter = EntrySearchStringIterator.getInstance(entry, sp);
|
||||
while (iter.hasNext()) {
|
||||
String str = iter.next();
|
||||
if (str != null & str.length() > 0) {
|
||||
if (sp.ignoreCase) {
|
||||
str = str.toLowerCase();
|
||||
}
|
||||
|
||||
if (str.indexOf(term) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import com.keepassdroid.utils.StrUtil;
|
||||
import com.keepassdroid.utils.UuidUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class EntrySearchHandlerV4 extends EntrySearchHandler {
|
||||
private SearchParametersV4 sp;
|
||||
|
||||
protected EntrySearchHandlerV4(SearchParameters sp, List<PwEntry> listStorage) {
|
||||
super(sp, listStorage);
|
||||
this.sp = (SearchParametersV4) sp;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean searchID(PwEntry e) {
|
||||
PwEntryV4 entry = (PwEntryV4) e;
|
||||
if (sp.searchInUUIDs) {
|
||||
String hex = UuidUtil.toHexString(entry.uuid);
|
||||
return StrUtil.indexOfIgnoreCase(hex, sp.searchString, Locale.ENGLISH) >= 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface ITimeLogger {
|
||||
Date getLastModificationTime();
|
||||
void setLastModificationTime(Date date);
|
||||
|
||||
Date getCreationTime();
|
||||
void setCreationTime(Date date);
|
||||
|
||||
Date getLastAccessTime();
|
||||
void setLastAccessTime(Date date);
|
||||
|
||||
Date getExpiryTime();
|
||||
void setExpiryTime(Date date);
|
||||
|
||||
boolean expires();
|
||||
void setExpires(boolean exp);
|
||||
|
||||
long getUsageCount();
|
||||
void setUsageCount(long count);
|
||||
|
||||
Date getLocationChanged();
|
||||
void setLocationChanged(Date date);
|
||||
|
||||
}
|
||||
@@ -1,533 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import android.webkit.URLUtil;
|
||||
|
||||
import com.keepassdroid.collections.VariantDictionary;
|
||||
import com.keepassdroid.crypto.CryptoUtil;
|
||||
import com.keepassdroid.crypto.engine.AesEngine;
|
||||
import com.keepassdroid.crypto.engine.CipherEngine;
|
||||
import com.keepassdroid.crypto.keyDerivation.AesKdf;
|
||||
import com.keepassdroid.crypto.keyDerivation.KdfEngine;
|
||||
import com.keepassdroid.crypto.keyDerivation.KdfFactory;
|
||||
import com.keepassdroid.crypto.keyDerivation.KdfParameters;
|
||||
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.w3c.dom.Text;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import biz.source_code.base64Coder.Base64Coder;
|
||||
|
||||
|
||||
public class PwDatabaseV4 extends PwDatabase {
|
||||
|
||||
public static final Date DEFAULT_NOW = new Date();
|
||||
public static final UUID UUID_ZERO = new UUID(0,0);
|
||||
public static final int DEFAULT_ROUNDS = 6000;
|
||||
private static final int DEFAULT_HISTORY_MAX_ITEMS = 10; // -1 unlimited
|
||||
private static final long DEFAULT_HISTORY_MAX_SIZE = 6 * 1024 * 1024; // -1 unlimited
|
||||
private static final String RECYCLEBIN_NAME = "RecycleBin";
|
||||
|
||||
public byte[] hmacKey;
|
||||
public UUID dataCipher = AesEngine.CIPHER_UUID;
|
||||
public CipherEngine dataEngine = new AesEngine();
|
||||
public PwCompressionAlgorithm compressionAlgorithm = PwCompressionAlgorithm.Gzip;
|
||||
// TODO: Refactor me away to get directly from kdfParameters
|
||||
public long numKeyEncRounds = 6000;
|
||||
public Date nameChanged = DEFAULT_NOW;
|
||||
public Date settingsChanged = DEFAULT_NOW;
|
||||
public String description = "";
|
||||
public Date descriptionChanged = DEFAULT_NOW;
|
||||
public String defaultUserName = "";
|
||||
public Date defaultUserNameChanged = DEFAULT_NOW;
|
||||
|
||||
public Date keyLastChanged = DEFAULT_NOW;
|
||||
public long keyChangeRecDays = -1;
|
||||
public long keyChangeForceDays = 1;
|
||||
public boolean keyChangeForceOnce = false;
|
||||
|
||||
public long maintenanceHistoryDays = 365;
|
||||
public String color = "";
|
||||
public boolean recycleBinEnabled = true;
|
||||
public UUID recycleBinUUID = UUID_ZERO;
|
||||
public Date recycleBinChanged = DEFAULT_NOW;
|
||||
public UUID entryTemplatesGroup = UUID_ZERO;
|
||||
public Date entryTemplatesGroupChanged = DEFAULT_NOW;
|
||||
public int historyMaxItems = DEFAULT_HISTORY_MAX_ITEMS;
|
||||
public long historyMaxSize = DEFAULT_HISTORY_MAX_SIZE;
|
||||
public UUID lastSelectedGroup = UUID_ZERO;
|
||||
public UUID lastTopVisibleGroup = UUID_ZERO;
|
||||
public MemoryProtectionConfig memoryProtection = new MemoryProtectionConfig();
|
||||
public List<PwDeletedObject> deletedObjects = new ArrayList<PwDeletedObject>();
|
||||
public List<PwIconCustom> customIcons = new ArrayList<PwIconCustom>();
|
||||
public Map<String, String> customData = new HashMap<String, String>();
|
||||
public KdfParameters kdfParameters = KdfFactory.getDefaultParameters();
|
||||
public VariantDictionary publicCustomData = new VariantDictionary();
|
||||
public BinaryPool binPool = new BinaryPool();
|
||||
|
||||
public String localizedAppName = "KeePassDroid";
|
||||
|
||||
public class MemoryProtectionConfig {
|
||||
public boolean protectTitle = false;
|
||||
public boolean protectUserName = false;
|
||||
public boolean protectPassword = false;
|
||||
public boolean protectUrl = false;
|
||||
public boolean protectNotes = false;
|
||||
|
||||
public boolean autoEnableVisualHiding = false;
|
||||
|
||||
public boolean GetProtection(String field) {
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.TITLE_FIELD)) return protectTitle;
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.USERNAME_FIELD)) return protectUserName;
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.PASSWORD_FIELD)) return protectPassword;
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.URL_FIELD)) return protectUrl;
|
||||
if ( field.equalsIgnoreCase(PwDefsV4.NOTES_FIELD)) return protectNotes;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMasterKey(String key, InputStream keyInputStream)
|
||||
throws InvalidKeyFileException, IOException {
|
||||
assert(key != null);
|
||||
|
||||
byte[] fKey = new byte[]{};
|
||||
|
||||
if ( key.length() > 0 && keyInputStream != null) {
|
||||
return getCompositeKey(key, keyInputStream);
|
||||
} else if ( key.length() > 0 ) {
|
||||
fKey = getPasswordKey(key);
|
||||
} else if ( keyInputStream != null) {
|
||||
fKey = getFileKey(keyInputStream);
|
||||
}
|
||||
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException("No SHA-256 implementation");
|
||||
}
|
||||
|
||||
return md.digest(fKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeFinalKey(byte[] masterSeed, byte[] masterSeed2, int numRounds) throws IOException {
|
||||
|
||||
byte[] transformedMasterKey = transformMasterKey(masterSeed2, masterKey, numRounds);
|
||||
|
||||
|
||||
byte[] cmpKey = new byte[65];
|
||||
System.arraycopy(masterSeed, 0, cmpKey, 0, 32);
|
||||
System.arraycopy(transformedMasterKey, 0, cmpKey, 32, 32);
|
||||
finalKey = CryptoUtil.resizeKey(cmpKey, 0, 64, dataEngine.keyLength());
|
||||
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-512");
|
||||
cmpKey[64] = 1;
|
||||
hmacKey = md.digest(cmpKey);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException("No SHA-512 implementation");
|
||||
} finally {
|
||||
Arrays.fill(cmpKey, (byte)0);
|
||||
}
|
||||
}
|
||||
public void makeFinalKey(byte[] masterSeed, KdfParameters kdfP) throws IOException {
|
||||
makeFinalKey(masterSeed, kdfP, 0);
|
||||
}
|
||||
|
||||
public void makeFinalKey(byte[] masterSeed, KdfParameters kdfP, long roundsFix)
|
||||
throws IOException {
|
||||
|
||||
KdfEngine kdfEngine = KdfFactory.get(kdfP.kdfUUID);
|
||||
if (kdfEngine == null) {
|
||||
throw new IOException("Unknown key derivation function");
|
||||
}
|
||||
|
||||
// Set to 6000 rounds to open corrupted database
|
||||
if (roundsFix > 0 && kdfP.kdfUUID.equals(AesKdf.CIPHER_UUID)) {
|
||||
kdfP.setUInt32(AesKdf.ParamRounds, roundsFix);
|
||||
numKeyEncRounds = roundsFix;
|
||||
}
|
||||
|
||||
byte[] transformedMasterKey = kdfEngine.transform(masterKey, kdfP);
|
||||
if (transformedMasterKey.length != 32) {
|
||||
transformedMasterKey = CryptoUtil.hashSha256(transformedMasterKey);
|
||||
}
|
||||
|
||||
byte[] cmpKey = new byte[65];
|
||||
System.arraycopy(masterSeed, 0, cmpKey, 0, 32);
|
||||
System.arraycopy(transformedMasterKey, 0, cmpKey, 32, 32);
|
||||
finalKey = CryptoUtil.resizeKey(cmpKey, 0, 64, dataEngine.keyLength());
|
||||
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-512");
|
||||
cmpKey[64] = 1;
|
||||
hmacKey = md.digest(cmpKey);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException("No SHA-512 implementation");
|
||||
} finally {
|
||||
Arrays.fill(cmpKey, (byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPasswordEncoding() {
|
||||
return "UTF-8";
|
||||
}
|
||||
|
||||
private static final String RootElementName = "KeyFile";
|
||||
//private static final String MetaElementName = "Meta";
|
||||
//private static final String VersionElementName = "Version";
|
||||
private static final String KeyElementName = "Key";
|
||||
private static final String KeyDataElementName = "Data";
|
||||
|
||||
@Override
|
||||
protected byte[] loadXmlKeyFile(InputStream keyInputStream) {
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document doc = db.parse(keyInputStream);
|
||||
|
||||
Element el = doc.getDocumentElement();
|
||||
if (el == null || ! el.getNodeName().equalsIgnoreCase(RootElementName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NodeList children = el.getChildNodes();
|
||||
if (children.getLength() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < children.getLength(); i++ ) {
|
||||
Node child = children.item(i);
|
||||
|
||||
if ( child.getNodeName().equalsIgnoreCase(KeyElementName) ) {
|
||||
NodeList keyChildren = child.getChildNodes();
|
||||
for ( int j = 0; j < keyChildren.getLength(); j++ ) {
|
||||
Node keyChild = keyChildren.item(j);
|
||||
if ( keyChild.getNodeName().equalsIgnoreCase(KeyDataElementName) ) {
|
||||
NodeList children2 = keyChild.getChildNodes();
|
||||
for ( int k = 0; k < children2.getLength(); k++) {
|
||||
Node text = children2.item(k);
|
||||
if (text.getNodeType() == Node.TEXT_NODE) {
|
||||
Text txt = (Text) text;
|
||||
return Base64Coder.decode(txt.getNodeValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PwGroup> getGroups() {
|
||||
List<PwGroup> list = new ArrayList<PwGroup>();
|
||||
PwGroupV4 root = (PwGroupV4) rootGroup;
|
||||
root.buildChildGroupsRecursive(list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PwGroup> getGrpRoots() {
|
||||
return rootGroup.childGroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PwEntry> getEntries() {
|
||||
List<PwEntry> list = new ArrayList<PwEntry>();
|
||||
PwGroupV4 root = (PwGroupV4) rootGroup;
|
||||
root.buildChildEntriesRecursive(list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNumRounds() {
|
||||
return numKeyEncRounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumRounds(long rounds) throws NumberFormatException {
|
||||
numKeyEncRounds = rounds;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appSettingsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwEncryptionAlgorithm getEncAlgorithm() {
|
||||
return PwEncryptionAlgorithm.Rjindal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupIdV4 newGroupId() {
|
||||
PwGroupIdV4 id = new PwGroupIdV4(UUID_ZERO);
|
||||
|
||||
while (true) {
|
||||
id = new PwGroupIdV4(UUID.randomUUID());
|
||||
|
||||
if (!isGroupIdUsed(id)) break;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroup createGroup() {
|
||||
return new PwGroupV4();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBackup(PwGroup group) {
|
||||
if (!recycleBinEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return group.isContainedIn(getRecycleBin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateGlobals(PwGroup currentGroup) {
|
||||
groups.put(rootGroup.getId(), rootGroup);
|
||||
|
||||
super.populateGlobals(currentGroup);
|
||||
}
|
||||
|
||||
/** Ensure that the recycle bin tree exists, if enabled and create it
|
||||
* if it doesn't exist
|
||||
*
|
||||
*/
|
||||
private void ensureRecycleBin() {
|
||||
if (getRecycleBin() == null) {
|
||||
// Create recycle bin
|
||||
|
||||
PwGroupV4 recycleBin = new PwGroupV4(true, true, RECYCLEBIN_NAME, iconFactory.getIcon(PwIconStandard.TRASH_BIN));
|
||||
recycleBin.enableAutoType = false;
|
||||
recycleBin.enableSearching = false;
|
||||
recycleBin.isExpanded = false;
|
||||
addGroupTo(recycleBin, rootGroup);
|
||||
|
||||
recycleBinUUID = recycleBin.uuid;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRecycle(PwGroup group) {
|
||||
if (!recycleBinEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PwGroup recycle = getRecycleBin();
|
||||
|
||||
return (recycle == null) || (!group.isContainedIn(recycle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRecycle(PwEntry entry) {
|
||||
if (!recycleBinEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PwGroup parent = entry.getParent();
|
||||
return (parent != null) && canRecycle(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle(PwEntry entry) {
|
||||
ensureRecycleBin();
|
||||
|
||||
PwGroup parent = entry.getParent();
|
||||
removeEntryFrom(entry, parent);
|
||||
parent.touch(false, true);
|
||||
|
||||
PwGroup recycleBin = getRecycleBin();
|
||||
addEntryTo(entry, recycleBin);
|
||||
|
||||
entry.touch(false, true);
|
||||
entry.touchLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoRecycle(PwEntry entry, PwGroup origParent) {
|
||||
|
||||
PwGroup recycleBin = getRecycleBin();
|
||||
removeEntryFrom(entry, recycleBin);
|
||||
|
||||
addEntryTo(entry, origParent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteEntry(PwEntry entry) {
|
||||
super.deleteEntry(entry);
|
||||
|
||||
deletedObjects.add(new PwDeletedObject(entry.getUUID()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoDeleteEntry(PwEntry entry, PwGroup origParent) {
|
||||
super.undoDeleteEntry(entry, origParent);
|
||||
|
||||
deletedObjects.remove(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupV4 getRecycleBin() {
|
||||
if (recycleBinUUID == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PwGroupId recycleId = new PwGroupIdV4(recycleBinUUID);
|
||||
return (PwGroupV4) groups.get(recycleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGroupSearchable(PwGroup group, boolean omitBackup) {
|
||||
if (!super.isGroupSearchable(group, omitBackup)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PwGroupV4 g = (PwGroupV4) group;
|
||||
|
||||
return g.isSearchEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validatePasswordEncoding(String key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initNew(String dbPath) {
|
||||
String filename = URLUtil.guessFileName(dbPath, null, null);
|
||||
|
||||
rootGroup = new PwGroupV4(true, true, dbNameFromPath(dbPath), iconFactory.getIcon(PwIconStandard.FOLDER));
|
||||
groups.put(rootGroup.getId(), rootGroup);
|
||||
}
|
||||
|
||||
private String dbNameFromPath(String dbPath) {
|
||||
String filename = URLUtil.guessFileName(dbPath, null, null);
|
||||
|
||||
if (EmptyUtils.isNullOrEmpty(filename)) {
|
||||
return "KeePass Database";
|
||||
}
|
||||
int lastExtDot = filename.lastIndexOf(".");
|
||||
if (lastExtDot == -1) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
return filename.substring(0, lastExtDot);
|
||||
}
|
||||
|
||||
private class GroupHasCustomData extends GroupHandler<PwGroup> {
|
||||
|
||||
public boolean hasCustomData = false;
|
||||
|
||||
@Override
|
||||
public boolean operate(PwGroup group) {
|
||||
if (group == null) {
|
||||
return true;
|
||||
}
|
||||
PwGroupV4 g4 = (PwGroupV4) group;
|
||||
if (g4.customData.size() > 0) {
|
||||
hasCustomData = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryHasCustomData extends EntryHandler<PwEntry> {
|
||||
|
||||
public boolean hasCustomData = false;
|
||||
|
||||
@Override
|
||||
public boolean operate(PwEntry entry) {
|
||||
if (entry == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PwEntryV4 e4 = (PwEntryV4)entry;
|
||||
if (e4.customData.size() > 0) {
|
||||
hasCustomData = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMinKdbxVersion() {
|
||||
if (!AesKdf.CIPHER_UUID.equals(kdfParameters.kdfUUID)) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
if (publicCustomData.size() > 0) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
EntryHasCustomData entryHandler = new EntryHasCustomData();
|
||||
GroupHasCustomData groupHandler = new GroupHasCustomData();
|
||||
|
||||
if (rootGroup == null ) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32_3;
|
||||
}
|
||||
rootGroup.preOrderTraverseTree(groupHandler, entryHandler);
|
||||
if (groupHandler.hasCustomData || entryHandler.hasCustomData) {
|
||||
return PwDbHeaderV4.FILE_VERSION_32;
|
||||
}
|
||||
|
||||
return PwDbHeaderV4.FILE_VERSION_32_3;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.keepassdroid.database.iterator.EntrySearchStringIterator;
|
||||
import com.keepassdroid.utils.SprEngine;
|
||||
|
||||
public abstract class PwEntry implements Cloneable {
|
||||
|
||||
protected static final String PMS_TAN_ENTRY = "<TAN>";
|
||||
|
||||
public static class EntryNameComparator implements Comparator<PwEntry> {
|
||||
|
||||
public int compare(PwEntry object1, PwEntry object2) {
|
||||
return object1.getTitle().compareToIgnoreCase(object2.getTitle());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public PwIconStandard icon = PwIconStandard.FIRST;
|
||||
|
||||
public PwEntry() {
|
||||
|
||||
}
|
||||
|
||||
public static PwEntry getInstance(PwGroup parent) {
|
||||
return PwEntry.getInstance(parent, true, true);
|
||||
}
|
||||
|
||||
public static PwEntry getInstance(PwGroup parent, boolean initId, boolean initDates) {
|
||||
if (parent instanceof PwGroupV3) {
|
||||
return new PwEntryV3((PwGroupV3)parent);
|
||||
}
|
||||
else if (parent instanceof PwGroupV4) {
|
||||
return new PwEntryV4((PwGroupV4)parent);
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Unknown PwGroup instance.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
PwEntry newEntry;
|
||||
try {
|
||||
newEntry = (PwEntry) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
assert(false);
|
||||
throw new RuntimeException("Clone should be supported");
|
||||
}
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
public PwEntry clone(boolean deepStrings) {
|
||||
return (PwEntry) clone();
|
||||
}
|
||||
|
||||
public void assign(PwEntry source) {
|
||||
icon = source.icon;
|
||||
}
|
||||
|
||||
public abstract UUID getUUID();
|
||||
public abstract void setUUID(UUID u);
|
||||
|
||||
public String getTitle() {
|
||||
return getTitle(false, null);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return getUsername(false, null);
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return getPassword(false, null);
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return getUrl(false, null);
|
||||
}
|
||||
|
||||
public String getNotes() {
|
||||
return getNotes(false, null);
|
||||
}
|
||||
|
||||
public abstract String getTitle(boolean decodeRef, PwDatabase db);
|
||||
public abstract String getUsername(boolean decodeRef, PwDatabase db);
|
||||
public abstract String getPassword(boolean decodeRef, PwDatabase db);
|
||||
public abstract String getUrl(boolean decodeRef, PwDatabase db);
|
||||
public abstract String getNotes(boolean decodeRef, PwDatabase db);
|
||||
public abstract Date getCreationTime();
|
||||
public abstract Date getLastModificationTime();
|
||||
public abstract Date getLastAccessTime();
|
||||
public abstract Date getExpiryTime();
|
||||
public abstract boolean expires();
|
||||
public abstract PwGroup getParent();
|
||||
|
||||
public abstract void setTitle(String title, PwDatabase db);
|
||||
public abstract void setUsername(String user, PwDatabase db);
|
||||
public abstract void setPassword(String pass, PwDatabase db);
|
||||
public abstract void setUrl(String url, PwDatabase db);
|
||||
public abstract void setNotes(String notes, PwDatabase db);
|
||||
public abstract void setCreationTime(Date create);
|
||||
public abstract void setLastModificationTime(Date mod);
|
||||
public abstract void setLastAccessTime(Date access);
|
||||
public abstract void setExpires(boolean exp);
|
||||
public abstract void setExpiryTime(Date expires);
|
||||
|
||||
public PwIcon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(PwIconStandard icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public boolean isTan() {
|
||||
return getTitle().equals(PMS_TAN_ENTRY) && (getUsername().length() > 0);
|
||||
}
|
||||
|
||||
public String getDisplayTitle() {
|
||||
if ( isTan() ) {
|
||||
return PMS_TAN_ENTRY + " " + getUsername();
|
||||
} else {
|
||||
return getTitle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isMetaStream() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EntrySearchStringIterator stringIterator() {
|
||||
return EntrySearchStringIterator.getInstance(this);
|
||||
}
|
||||
|
||||
public void touch(boolean modified, boolean touchParents) {
|
||||
Date now = new Date();
|
||||
|
||||
setLastAccessTime(now);
|
||||
|
||||
if (modified) {
|
||||
setLastModificationTime(now);
|
||||
}
|
||||
|
||||
PwGroup parent = getParent();
|
||||
if (touchParents && parent != null) {
|
||||
parent.touch(modified, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void touchLocation() { }
|
||||
|
||||
public abstract void setParent(PwGroup parent);
|
||||
|
||||
public boolean isSearchingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,530 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
|
||||
This file was derived from
|
||||
|
||||
Copyright 2007 Naomaru Itoi <nao@phoneid.org>
|
||||
|
||||
This file was derived from
|
||||
|
||||
Java clone of KeePass - A KeePass file viewer for Java
|
||||
Copyright 2006 Bill Zwicky <billzwicky@users.sourceforge.net>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package com.keepassdroid.database;
|
||||
|
||||
// PhoneID
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.keepassdroid.utils.Types;
|
||||
|
||||
|
||||
/**
|
||||
* Structure containing information about one entry.
|
||||
*
|
||||
* <PRE>
|
||||
* One entry: [FIELDTYPE(FT)][FIELDSIZE(FS)][FIELDDATA(FD)]
|
||||
* [FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)]...
|
||||
*
|
||||
* [ 2 bytes] FIELDTYPE
|
||||
* [ 4 bytes] FIELDSIZE, size of FIELDDATA in bytes
|
||||
* [ n bytes] FIELDDATA, n = FIELDSIZE
|
||||
*
|
||||
* Notes:
|
||||
* - Strings are stored in UTF-8 encoded form and are null-terminated.
|
||||
* - FIELDTYPE can be one of the FT_ constants.
|
||||
* </PRE>
|
||||
*
|
||||
* @author Naomaru Itoi <nao@phoneid.org>
|
||||
* @author Bill Zwicky <wrzwicky@pobox.com>
|
||||
* @author Dominik Reichl <dominik.reichl@t-online.de>
|
||||
*/
|
||||
public class PwEntryV3 extends PwEntry {
|
||||
|
||||
public static final Date NEVER_EXPIRE = getNeverExpire();
|
||||
public static final Date NEVER_EXPIRE_BUG = getNeverExpireBug();
|
||||
public static final Date DEFAULT_DATE = getDefaultDate();
|
||||
public static final PwDate PW_NEVER_EXPIRE = new PwDate(NEVER_EXPIRE);
|
||||
public static final PwDate PW_NEVER_EXPIRE_BUG = new PwDate(NEVER_EXPIRE_BUG);
|
||||
public static final PwDate DEFAULT_PWDATE = new PwDate(DEFAULT_DATE);
|
||||
|
||||
|
||||
/** Size of byte buffer needed to hold this struct. */
|
||||
public static final String PMS_ID_BINDESC = "bin-stream";
|
||||
public static final String PMS_ID_TITLE = "Meta-Info";
|
||||
public static final String PMS_ID_USER = "SYSTEM";
|
||||
public static final String PMS_ID_URL = "$";
|
||||
|
||||
|
||||
|
||||
public int groupId;
|
||||
public String username;
|
||||
private byte[] password;
|
||||
private byte[] uuid;
|
||||
public String title;
|
||||
public String url;
|
||||
public String additional;
|
||||
|
||||
|
||||
public PwDate tCreation;
|
||||
public PwDate tLastMod;
|
||||
public PwDate tLastAccess;
|
||||
public PwDate tExpire;
|
||||
|
||||
/** A string describing what is in pBinaryData */
|
||||
public String binaryDesc;
|
||||
private byte[] binaryData;
|
||||
|
||||
private static Date getDefaultDate() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(Calendar.YEAR, 2004);
|
||||
cal.set(Calendar.MONTH, Calendar.JANUARY);
|
||||
cal.set(Calendar.DAY_OF_MONTH, 1);
|
||||
cal.set(Calendar.HOUR, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
private static Date getNeverExpire() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(Calendar.YEAR, 2999);
|
||||
cal.set(Calendar.MONTH, 11);
|
||||
cal.set(Calendar.DAY_OF_MONTH, 28);
|
||||
cal.set(Calendar.HOUR, 23);
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
/** This date was was accidentally being written
|
||||
* out when an entry was supposed to be marked as
|
||||
* expired. We'll use this to silently correct those
|
||||
* entries.
|
||||
* @return
|
||||
*/
|
||||
private static Date getNeverExpireBug() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(Calendar.YEAR, 2999);
|
||||
cal.set(Calendar.MONTH, 11);
|
||||
cal.set(Calendar.DAY_OF_MONTH, 30);
|
||||
cal.set(Calendar.HOUR, 23);
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
public static boolean IsNever(Date date) {
|
||||
return PwDate.IsSameDate(NEVER_EXPIRE, date);
|
||||
}
|
||||
|
||||
// for tree traversing
|
||||
public PwGroupV3 parent = null;
|
||||
|
||||
|
||||
public PwEntryV3() {
|
||||
super();
|
||||
}
|
||||
|
||||
/*
|
||||
public PwEntryV3(PwEntryV3 source) {
|
||||
assign(source);
|
||||
}
|
||||
*/
|
||||
|
||||
public PwEntryV3(PwGroupV3 p) {
|
||||
this(p, true, true);
|
||||
}
|
||||
|
||||
public PwEntryV3(PwGroupV3 p, boolean initId, boolean initDates) {
|
||||
|
||||
parent = p;
|
||||
groupId = ((PwGroupIdV3)parent.getId()).getId();
|
||||
|
||||
if (initId) {
|
||||
Random random = new Random();
|
||||
uuid = new byte[16];
|
||||
random.nextBytes(uuid);
|
||||
}
|
||||
|
||||
if (initDates) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
Date now = cal.getTime();
|
||||
tCreation = new PwDate(now);
|
||||
tLastAccess = new PwDate(now);
|
||||
tLastMod = new PwDate(now);
|
||||
tExpire = new PwDate(NEVER_EXPIRE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the actual password byte array.
|
||||
*/
|
||||
@Override
|
||||
public String getPassword(boolean decodeRef, PwDatabase db) {
|
||||
if (password == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return new String(password);
|
||||
}
|
||||
|
||||
public byte[] getPasswordBytes() {
|
||||
return password;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fill byte array
|
||||
*/
|
||||
private static void fill(byte[] array, byte value)
|
||||
{
|
||||
for (int i=0; i<array.length; i++)
|
||||
array[i] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
/** Securely erase old password before copying new. */
|
||||
public void setPassword( byte[] buf, int offset, int len ) {
|
||||
if( password != null ) {
|
||||
fill( password, (byte)0 );
|
||||
password = null;
|
||||
}
|
||||
password = new byte[len];
|
||||
System.arraycopy( buf, offset, password, 0, len );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void setPassword(String pass, PwDatabase db) {
|
||||
byte[] password;
|
||||
try {
|
||||
password = pass.getBytes("UTF-8");
|
||||
setPassword(password, 0, password.length);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
assert false;
|
||||
password = pass.getBytes();
|
||||
setPassword(password, 0, password.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the actual binaryData byte array.
|
||||
*/
|
||||
public byte[] getBinaryData() {
|
||||
return binaryData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Securely erase old data before copying new. */
|
||||
public void setBinaryData( byte[] buf, int offset, int len ) {
|
||||
if( binaryData != null ) {
|
||||
fill( binaryData, (byte)0 );
|
||||
binaryData = null;
|
||||
}
|
||||
binaryData = new byte[len];
|
||||
System.arraycopy( buf, offset, binaryData, 0, len );
|
||||
}
|
||||
|
||||
// Determine if this is a MetaStream entry
|
||||
@Override
|
||||
public boolean isMetaStream() {
|
||||
if ( binaryData == null ) return false;
|
||||
if ( additional == null || additional.length() == 0 ) return false;
|
||||
if ( ! binaryDesc.equals(PMS_ID_BINDESC) ) return false;
|
||||
if ( title == null ) return false;
|
||||
if ( ! title.equals(PMS_ID_TITLE) ) return false;
|
||||
if ( username == null ) return false;
|
||||
if ( ! username.equals(PMS_ID_USER) ) return false;
|
||||
if ( url == null ) return false;
|
||||
if ( ! url.equals(PMS_ID_URL)) return false;
|
||||
if ( !icon.isMetaStreamIcon() ) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void assign(PwEntry source) {
|
||||
|
||||
if ( ! (source instanceof PwEntryV3) ) {
|
||||
throw new RuntimeException("DB version mix");
|
||||
}
|
||||
|
||||
super.assign(source);
|
||||
|
||||
PwEntryV3 src = (PwEntryV3) source;
|
||||
assign(src);
|
||||
|
||||
}
|
||||
|
||||
private void assign(PwEntryV3 source) {
|
||||
title = source.title;
|
||||
url = source.url;
|
||||
groupId = source.groupId;
|
||||
username = source.username;
|
||||
additional = source.additional;
|
||||
uuid = source.uuid;
|
||||
|
||||
int passLen = source.password.length;
|
||||
password = new byte[passLen];
|
||||
System.arraycopy(source.password, 0, password, 0, passLen);
|
||||
|
||||
tCreation = (PwDate) source.tCreation.clone();
|
||||
tLastMod = (PwDate) source.tLastMod.clone();
|
||||
tLastAccess = (PwDate) source.tLastAccess.clone();
|
||||
tExpire = (PwDate) source.tExpire.clone();
|
||||
|
||||
binaryDesc = source.binaryDesc;
|
||||
|
||||
if ( source.binaryData != null ) {
|
||||
int descLen = source.binaryData.length;
|
||||
binaryData = new byte[descLen];
|
||||
System.arraycopy(source.binaryData, 0, binaryData, 0, descLen);
|
||||
}
|
||||
|
||||
parent = source.parent;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
PwEntryV3 newEntry = (PwEntryV3) super.clone();
|
||||
|
||||
if (password != null) {
|
||||
int passLen = password.length;
|
||||
password = new byte[passLen];
|
||||
System.arraycopy(password, 0, newEntry.password, 0, passLen);
|
||||
}
|
||||
|
||||
newEntry.tCreation = (PwDate) tCreation.clone();
|
||||
newEntry.tLastMod = (PwDate) tLastMod.clone();
|
||||
newEntry.tLastAccess = (PwDate) tLastAccess.clone();
|
||||
newEntry.tExpire = (PwDate) tExpire.clone();
|
||||
|
||||
newEntry.binaryDesc = binaryDesc;
|
||||
|
||||
if ( binaryData != null ) {
|
||||
int descLen = binaryData.length;
|
||||
newEntry.binaryData = new byte[descLen];
|
||||
System.arraycopy(binaryData, 0, newEntry.binaryData, 0, descLen);
|
||||
}
|
||||
|
||||
newEntry.parent = parent;
|
||||
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastAccessTime() {
|
||||
return tLastAccess.getJDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreationTime() {
|
||||
return tCreation.getJDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpiryTime() {
|
||||
return tExpire.getJDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastModificationTime() {
|
||||
return tLastMod.getJDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreationTime(Date create) {
|
||||
tCreation = new PwDate(create);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModificationTime(Date mod) {
|
||||
tLastMod = new PwDate(mod);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastAccessTime(Date access) {
|
||||
tLastAccess = new PwDate(access);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpires(boolean expires) {
|
||||
if (!expires) {
|
||||
tExpire = PW_NEVER_EXPIRE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpiryTime(Date expires) {
|
||||
tExpire = new PwDate(expires);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupV3 getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return Types.bytestoUUID(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUUID(UUID u) {
|
||||
uuid = Types.UUIDtoBytes(u);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername(boolean decodeRef, PwDatabase db) {
|
||||
if (username == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String user, PwDatabase db) {
|
||||
username = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle(boolean decodeRef, PwDatabase db) {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title, PwDatabase db) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNotes(boolean decodeRef, PwDatabase db) {
|
||||
return additional;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotes(String notes, PwDatabase db) {
|
||||
additional = notes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl(boolean decodeRef, PwDatabase db) {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUrl(String url, PwDatabase db) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expires() {
|
||||
return ! IsNever(tExpire.getJDate());
|
||||
}
|
||||
|
||||
public void populateBlankFields(PwDatabaseV3 db) {
|
||||
if (icon == null) {
|
||||
icon = db.iconFactory.getIcon(1);
|
||||
}
|
||||
|
||||
if (username == null) {
|
||||
username = "";
|
||||
}
|
||||
|
||||
if (password == null) {
|
||||
password = new byte[0];
|
||||
}
|
||||
|
||||
if (uuid == null) {
|
||||
uuid = Types.UUIDtoBytes(UUID.randomUUID());
|
||||
}
|
||||
|
||||
if (title == null) {
|
||||
title = "";
|
||||
}
|
||||
|
||||
if (url == null) {
|
||||
url = "";
|
||||
}
|
||||
|
||||
if (additional == null) {
|
||||
additional = "";
|
||||
}
|
||||
|
||||
if (tCreation == null) {
|
||||
tCreation = DEFAULT_PWDATE;
|
||||
}
|
||||
|
||||
if (tLastMod == null) {
|
||||
tLastMod = DEFAULT_PWDATE;
|
||||
}
|
||||
|
||||
if (tLastAccess == null) {
|
||||
tLastAccess = DEFAULT_PWDATE;
|
||||
}
|
||||
|
||||
if (tExpire == null) {
|
||||
tExpire = PW_NEVER_EXPIRE;
|
||||
}
|
||||
|
||||
if (binaryDesc == null) {
|
||||
binaryDesc = "";
|
||||
}
|
||||
|
||||
if (binaryData == null) {
|
||||
binaryData = new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(PwGroup parent) {
|
||||
this.parent = (PwGroupV3) parent;
|
||||
}
|
||||
}
|
||||
@@ -1,491 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.keepassdroid.database.security.ProtectedBinary;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
import com.keepassdroid.utils.SprEngine;
|
||||
|
||||
public class PwEntryV4 extends PwEntry implements ITimeLogger {
|
||||
public static final String STR_TITLE = "Title";
|
||||
public static final String STR_USERNAME = "UserName";
|
||||
public static final String STR_PASSWORD = "Password";
|
||||
public static final String STR_URL = "URL";
|
||||
public static final String STR_NOTES = "Notes";
|
||||
|
||||
public PwGroupV4 parent;
|
||||
public UUID uuid = PwDatabaseV4.UUID_ZERO;
|
||||
public HashMap<String, ProtectedString> strings = new HashMap<String, ProtectedString>();
|
||||
public HashMap<String, ProtectedBinary> binaries = new HashMap<String, ProtectedBinary>();
|
||||
public PwIconCustom customIcon = PwIconCustom.ZERO;
|
||||
public String foregroundColor = "";
|
||||
public String backgroupColor = "";
|
||||
public String overrideURL = "";
|
||||
public AutoType autoType = new AutoType();
|
||||
public ArrayList<PwEntryV4> history = new ArrayList<PwEntryV4>();
|
||||
|
||||
private Date parentGroupLastMod = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date creation = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date lastMod = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date lastAccess = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date expireDate = PwDatabaseV4.DEFAULT_NOW;
|
||||
private boolean expires = false;
|
||||
private long usageCount = 0;
|
||||
public String url = "";
|
||||
public String additional = "";
|
||||
public String tags = "";
|
||||
public Map<String, String> customData = new HashMap<String, String>();
|
||||
|
||||
public class AutoType implements Cloneable {
|
||||
private static final long OBF_OPT_NONE = 0;
|
||||
|
||||
public boolean enabled = true;
|
||||
public long obfuscationOptions = OBF_OPT_NONE;
|
||||
public String defaultSequence = "";
|
||||
|
||||
private HashMap<String, String> windowSeqPairs = new HashMap<String, String>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object clone() {
|
||||
AutoType auto;
|
||||
try {
|
||||
auto = (AutoType) super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
assert(false);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
auto.windowSeqPairs = (HashMap<String, String>) windowSeqPairs.clone();
|
||||
|
||||
return auto;
|
||||
|
||||
}
|
||||
|
||||
public void put(String key, String value) {
|
||||
windowSeqPairs.put(key, value);
|
||||
}
|
||||
|
||||
public Set<Entry<String, String>> entrySet() {
|
||||
return windowSeqPairs.entrySet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public PwEntryV4() {
|
||||
|
||||
}
|
||||
|
||||
public PwEntryV4(PwGroupV4 p) {
|
||||
this(p, true, true);
|
||||
}
|
||||
|
||||
public PwEntryV4(PwGroupV4 p, boolean initId, boolean initDates) {
|
||||
parent = p;
|
||||
|
||||
if (initId) {
|
||||
uuid = UUID.randomUUID();
|
||||
}
|
||||
|
||||
if (initDates) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
Date now = cal.getTime();
|
||||
creation = now;
|
||||
lastAccess = now;
|
||||
lastMod = now;
|
||||
expires = false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public PwEntry clone(boolean deepStrings) {
|
||||
PwEntryV4 entry = (PwEntryV4) super.clone(deepStrings);
|
||||
|
||||
if (deepStrings) {
|
||||
entry.strings = (HashMap<String, ProtectedString>) strings.clone();
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PwEntryV4 cloneDeep() {
|
||||
PwEntryV4 entry = (PwEntryV4) clone(true);
|
||||
|
||||
entry.binaries = (HashMap<String, ProtectedBinary>) binaries.clone();
|
||||
entry.history = (ArrayList<PwEntryV4>) history.clone();
|
||||
entry.autoType = (AutoType) autoType.clone();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assign(PwEntry source) {
|
||||
|
||||
if ( ! (source instanceof PwEntryV4) ) {
|
||||
throw new RuntimeException("DB version mix.");
|
||||
}
|
||||
|
||||
super.assign(source);
|
||||
|
||||
PwEntryV4 src = (PwEntryV4) source;
|
||||
assign(src);
|
||||
}
|
||||
|
||||
private void assign(PwEntryV4 source) {
|
||||
parent = source.parent;
|
||||
uuid = source.uuid;
|
||||
strings = source.strings;
|
||||
binaries = source.binaries;
|
||||
customIcon = source.customIcon;
|
||||
foregroundColor = source.foregroundColor;
|
||||
backgroupColor = source.backgroupColor;
|
||||
overrideURL = source.overrideURL;
|
||||
autoType = source.autoType;
|
||||
history = source.history;
|
||||
parentGroupLastMod = source.parentGroupLastMod;
|
||||
creation = source.creation;
|
||||
lastMod = source.lastMod;
|
||||
lastAccess = source.lastAccess;
|
||||
expireDate = source.expireDate;
|
||||
expires = source.expires;
|
||||
usageCount = source.usageCount;
|
||||
url = source.url;
|
||||
additional = source.additional;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
PwEntryV4 newEntry = (PwEntryV4) super.clone();
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
private String decodeRefKey(boolean decodeRef, String key, PwDatabase db) {
|
||||
String text = getString(key);
|
||||
if (decodeRef) {
|
||||
text = decodeRef(text, db);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private String decodeRef(String text, PwDatabase db) {
|
||||
if (db == null) { return text; }
|
||||
|
||||
SprEngine spr = SprEngine.getInstance(db);
|
||||
return spr.compile(text, this, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_USERNAME, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_TITLE, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_PASSWORD, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastAccessTime() {
|
||||
return lastAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreationTime() {
|
||||
return creation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpiryTime() {
|
||||
return expireDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastModificationTime() {
|
||||
return lastMod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectTitle;
|
||||
|
||||
setString(STR_TITLE, title, protect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String user, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectUserName;
|
||||
|
||||
setString(STR_USERNAME, user, protect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPassword(String pass, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectPassword;
|
||||
|
||||
setString(STR_PASSWORD, pass, protect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUrl(String url, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectUrl;
|
||||
|
||||
setString(STR_URL, url, protect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotes(String notes, PwDatabase d) {
|
||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
||||
boolean protect = db.memoryProtection.protectNotes;
|
||||
|
||||
setString(STR_NOTES, notes, protect);
|
||||
}
|
||||
|
||||
public void setCreationTime(Date date) {
|
||||
creation = date;
|
||||
}
|
||||
|
||||
public void setExpiryTime(Date date) {
|
||||
expireDate = date;
|
||||
}
|
||||
|
||||
public void setLastAccessTime(Date date) {
|
||||
lastAccess = date;
|
||||
}
|
||||
|
||||
public void setLastModificationTime(Date date) {
|
||||
lastMod = date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupV4 getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setUUID(UUID u) {
|
||||
uuid = u;
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
ProtectedString value = strings.get(key);
|
||||
|
||||
if ( value == null ) return new String("");
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
public void setString(String key, String value, boolean protect) {
|
||||
ProtectedString ps = new ProtectedString(protect, value);
|
||||
strings.put(key, ps);
|
||||
}
|
||||
|
||||
public Date getLocationChanged() {
|
||||
return parentGroupLastMod;
|
||||
}
|
||||
|
||||
public long getUsageCount() {
|
||||
return usageCount;
|
||||
}
|
||||
|
||||
public void setLocationChanged(Date date) {
|
||||
parentGroupLastMod = date;
|
||||
}
|
||||
|
||||
public void setUsageCount(long count) {
|
||||
usageCount = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expires() {
|
||||
return expires;
|
||||
}
|
||||
|
||||
public void setExpires(boolean exp) {
|
||||
expires = exp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNotes(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_NOTES, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl(boolean decodeRef, PwDatabase db) {
|
||||
return decodeRefKey(decodeRef, STR_URL, db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwIcon getIcon() {
|
||||
if (customIcon == null || customIcon.uuid.equals(PwDatabaseV4.UUID_ZERO)) {
|
||||
return super.getIcon();
|
||||
} else {
|
||||
return customIcon;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean IsStandardString(String key) {
|
||||
return key.equals(STR_TITLE) || key.equals(STR_USERNAME)
|
||||
|| key.equals(STR_PASSWORD) || key.equals(STR_URL)
|
||||
|| key.equals(STR_NOTES);
|
||||
}
|
||||
|
||||
public void createBackup(PwDatabaseV4 db) {
|
||||
PwEntryV4 copy = cloneDeep();
|
||||
copy.history = new ArrayList<PwEntryV4>();
|
||||
history.add(copy);
|
||||
|
||||
if (db != null) maintainBackups(db);
|
||||
}
|
||||
|
||||
private boolean maintainBackups(PwDatabaseV4 db) {
|
||||
boolean deleted = false;
|
||||
|
||||
int maxItems = db.historyMaxItems;
|
||||
if (maxItems >= 0) {
|
||||
while (history.size() > maxItems) {
|
||||
removeOldestBackup();
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
long maxSize = db.historyMaxSize;
|
||||
if (maxSize >= 0) {
|
||||
while(true) {
|
||||
long histSize = 0;
|
||||
for (PwEntryV4 entry : history) {
|
||||
histSize += entry.getSize();
|
||||
}
|
||||
|
||||
if (histSize > maxSize) {
|
||||
removeOldestBackup();
|
||||
deleted = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
private void removeOldestBackup() {
|
||||
Date min = null;
|
||||
int index = -1;
|
||||
|
||||
for (int i = 0; i < history.size(); i++) {
|
||||
PwEntry entry = history.get(i);
|
||||
Date lastMod = entry.getLastModificationTime();
|
||||
if ((min == null) || lastMod.before(min)) {
|
||||
index = i;
|
||||
min = lastMod;
|
||||
}
|
||||
}
|
||||
|
||||
if (index != -1) {
|
||||
history.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final long FIXED_LENGTH_SIZE = 128; // Approximate fixed length size
|
||||
public long getSize() {
|
||||
long size = FIXED_LENGTH_SIZE;
|
||||
|
||||
for (Entry<String, ProtectedString> pair : strings.entrySet()) {
|
||||
size += pair.getKey().length();
|
||||
size += pair.getValue().length();
|
||||
}
|
||||
|
||||
for (Entry<String, ProtectedBinary> pair : binaries.entrySet()) {
|
||||
size += pair.getKey().length();
|
||||
size += pair.getValue().length();
|
||||
}
|
||||
|
||||
size += autoType.defaultSequence.length();
|
||||
for (Entry<String, String> pair : autoType.entrySet()) {
|
||||
size += pair.getKey().length();
|
||||
size += pair.getValue().length();
|
||||
}
|
||||
|
||||
for (PwEntryV4 entry : history) {
|
||||
size += entry.getSize();
|
||||
}
|
||||
|
||||
size += overrideURL.length();
|
||||
size += tags.length();
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touch(boolean modified, boolean touchParents) {
|
||||
super.touch(modified, touchParents);
|
||||
|
||||
++usageCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchLocation() {
|
||||
parentGroupLastMod = new Date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(PwGroup parent) {
|
||||
this.parent = (PwGroupV4) parent;
|
||||
}
|
||||
|
||||
public boolean isSearchingEnabled() {
|
||||
if (parent != null) {
|
||||
return parent.isSearchEnabled();
|
||||
}
|
||||
|
||||
return PwGroupV4.DEFAULT_SEARCHING_ENABLED;
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.keepassdroid.utils.StrUtil;
|
||||
|
||||
public abstract class PwGroup {
|
||||
public List<PwGroup> childGroups = new ArrayList<PwGroup>();
|
||||
public List<PwEntry> childEntries = new ArrayList<PwEntry>();
|
||||
public String name = "";
|
||||
public PwIconStandard icon;
|
||||
|
||||
public abstract PwGroup getParent();
|
||||
public abstract void setParent(PwGroup parent);
|
||||
|
||||
public abstract PwGroupId getId();
|
||||
public abstract void setId(PwGroupId id);
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public abstract Date getLastMod();
|
||||
|
||||
public PwIcon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void sortGroupsByName() {
|
||||
Collections.sort(childGroups, new GroupNameComparator());
|
||||
}
|
||||
|
||||
public static class GroupNameComparator implements Comparator<PwGroup> {
|
||||
|
||||
public int compare(PwGroup object1, PwGroup object2) {
|
||||
return object1.getName().compareToIgnoreCase(object2.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public abstract void setLastAccessTime(Date date);
|
||||
|
||||
public abstract void setLastModificationTime(Date date);
|
||||
|
||||
public void sortEntriesByName() {
|
||||
Collections.sort(childEntries, new PwEntry.EntryNameComparator());
|
||||
}
|
||||
|
||||
public void initNewGroup(String nm, PwGroupId newId) {
|
||||
setId(newId);
|
||||
name = nm;
|
||||
}
|
||||
|
||||
public boolean isContainedIn(PwGroup container) {
|
||||
PwGroup cur = this;
|
||||
while (cur != null) {
|
||||
if (cur == container) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cur = cur.getParent();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void touch(boolean modified, boolean touchParents) {
|
||||
Date now = new Date();
|
||||
|
||||
setLastAccessTime(now);
|
||||
|
||||
if (modified) {
|
||||
setLastModificationTime(now);
|
||||
}
|
||||
|
||||
PwGroup parent = getParent();
|
||||
if (touchParents && parent != null) {
|
||||
parent.touch(modified, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void searchEntries(SearchParameters sp, List<PwEntry> listStorage) {
|
||||
if (sp == null) { return; }
|
||||
if (listStorage == null) { return; }
|
||||
|
||||
List<String> terms = StrUtil.splitSearchTerms(sp.searchString);
|
||||
if (terms.size() <= 1 || sp.regularExpression) {
|
||||
searchEntriesSingle(sp, listStorage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Search longest term first
|
||||
Comparator<String> stringLengthComparator = new Comparator<String>() {
|
||||
|
||||
@Override
|
||||
public int compare(String lhs, String rhs) {
|
||||
return lhs.length() - rhs.length();
|
||||
}
|
||||
|
||||
};
|
||||
Collections.sort(terms, stringLengthComparator);
|
||||
|
||||
String fullSearch = sp.searchString;
|
||||
List<PwEntry> pg = this.childEntries;
|
||||
for (int i = 0; i < terms.size(); i ++) {
|
||||
List<PwEntry> pgNew = new ArrayList<PwEntry>();
|
||||
|
||||
sp.searchString = terms.get(i);
|
||||
|
||||
boolean negate = false;
|
||||
if (sp.searchString.startsWith("-")) {
|
||||
sp.searchString.substring(1);
|
||||
negate = sp.searchString.length() > 0;
|
||||
}
|
||||
|
||||
if (!searchEntriesSingle(sp, pgNew)) {
|
||||
pg = null;
|
||||
break;
|
||||
}
|
||||
|
||||
List<PwEntry> complement = new ArrayList<PwEntry>();
|
||||
if (negate) {
|
||||
for (PwEntry entry: pg) {
|
||||
if (!pgNew.contains(entry)) {
|
||||
complement.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
pg = complement;
|
||||
}
|
||||
else {
|
||||
pg = pgNew;
|
||||
}
|
||||
}
|
||||
|
||||
if (pg != null) {
|
||||
listStorage.addAll(pg);
|
||||
}
|
||||
sp.searchString = fullSearch;
|
||||
|
||||
}
|
||||
|
||||
private boolean searchEntriesSingle(SearchParameters spIn, List<PwEntry> listStorage) {
|
||||
SearchParameters sp = (SearchParameters) spIn.clone();
|
||||
|
||||
EntryHandler<PwEntry> eh;
|
||||
if (sp.searchString.length() <= 0) {
|
||||
eh = new EntrySearchHandlerAll(sp, listStorage);
|
||||
} else {
|
||||
eh = EntrySearchHandler.getInstance(this, sp, listStorage);
|
||||
}
|
||||
|
||||
if (!preOrderTraverseTree(null, eh)) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean preOrderTraverseTree(GroupHandler<PwGroup> groupHandler, EntryHandler<PwEntry> entryHandler) {
|
||||
if (entryHandler != null) {
|
||||
for (PwEntry entry : childEntries) {
|
||||
if (!entryHandler.operate(entry)) return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (PwGroup group : childGroups) {
|
||||
|
||||
if ((groupHandler != null) && !groupHandler.operate(group)) return false;
|
||||
|
||||
group.preOrderTraverseTree(groupHandler, entryHandler);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.keepassdroid.database;
|
||||
|
||||
public abstract class PwGroupId {
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PwGroupIdV4 extends PwGroupId {
|
||||
private UUID uuid;
|
||||
|
||||
public PwGroupIdV4(UUID u) {
|
||||
uuid = u;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object id) {
|
||||
if ( ! (id instanceof PwGroupIdV4) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PwGroupIdV4 v4 = (PwGroupIdV4) id;
|
||||
|
||||
return uuid.equals(v4.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return uuid.hashCode();
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
|
||||
This file was derived from
|
||||
|
||||
Copyright 2007 Naomaru Itoi <nao@phoneid.org>
|
||||
|
||||
This file was derived from
|
||||
|
||||
Java clone of KeePass - A KeePass file viewer for Java
|
||||
Copyright 2006 Bill Zwicky <billzwicky@users.sourceforge.net>
|
||||
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @author Brian Pellin <bpellin@gmail.com>
|
||||
* @author Naomaru Itoi <nao@phoneid.org>
|
||||
* @author Bill Zwicky <wrzwicky@pobox.com>
|
||||
* @author Dominik Reichl <dominik.reichl@t-online.de>
|
||||
*/
|
||||
public class PwGroupV3 extends PwGroup {
|
||||
public PwGroupV3() {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static final Date NEVER_EXPIRE = PwEntryV3.NEVER_EXPIRE;
|
||||
|
||||
/** Size of byte buffer needed to hold this struct. */
|
||||
public static final int BUF_SIZE = 124;
|
||||
|
||||
// for tree traversing
|
||||
public PwGroupV3 parent = null;
|
||||
|
||||
public int groupId;
|
||||
|
||||
public PwDate tCreation;
|
||||
public PwDate tLastMod;
|
||||
public PwDate tLastAccess;
|
||||
public PwDate tExpire;
|
||||
|
||||
public int level; // short
|
||||
|
||||
/** Used by KeePass internally, don't use */
|
||||
public int flags;
|
||||
|
||||
public void setGroups(List<PwGroup> groups) {
|
||||
childGroups = groups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroup getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupId getId() {
|
||||
return new PwGroupIdV3(groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(PwGroupId id) {
|
||||
PwGroupIdV3 id3 = (PwGroupIdV3) id;
|
||||
groupId = id3.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastMod() {
|
||||
return tLastMod.getJDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(PwGroup prt) {
|
||||
parent = (PwGroupV3) prt;
|
||||
level = parent.level + 1;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initNewGroup(String nm, PwGroupId newId) {
|
||||
super.initNewGroup(nm, newId);
|
||||
|
||||
Date now = Calendar.getInstance().getTime();
|
||||
tCreation = new PwDate(now);
|
||||
tLastAccess = new PwDate(now);
|
||||
tLastMod = new PwDate(now);
|
||||
tExpire = new PwDate(PwGroupV3.NEVER_EXPIRE);
|
||||
|
||||
}
|
||||
|
||||
public void populateBlankFields(PwDatabaseV3 db) {
|
||||
if (icon == null) {
|
||||
icon = db.iconFactory.getIcon(1);
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
name = "";
|
||||
}
|
||||
|
||||
if (tCreation == null) {
|
||||
tCreation = PwEntryV3.DEFAULT_PWDATE;
|
||||
}
|
||||
|
||||
if (tLastMod == null) {
|
||||
tLastMod = PwEntryV3.DEFAULT_PWDATE;
|
||||
}
|
||||
|
||||
if (tLastAccess == null) {
|
||||
tLastAccess = PwEntryV3.DEFAULT_PWDATE;
|
||||
}
|
||||
|
||||
if (tExpire == null) {
|
||||
tExpire = PwEntryV3.DEFAULT_PWDATE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastAccessTime(Date date) {
|
||||
tLastAccess = new PwDate(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModificationTime(Date date) {
|
||||
tLastMod = new PwDate(date);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PwGroupV4 extends PwGroup implements ITimeLogger {
|
||||
|
||||
//public static final int FOLDER_ICON = 48;
|
||||
public static final boolean DEFAULT_SEARCHING_ENABLED = true;
|
||||
|
||||
public PwGroupV4 parent = null;
|
||||
public UUID uuid = PwDatabaseV4.UUID_ZERO;
|
||||
public String notes = "";
|
||||
public PwIconCustom customIcon = PwIconCustom.ZERO;
|
||||
public boolean isExpanded = true;
|
||||
public String defaultAutoTypeSequence = "";
|
||||
public Boolean enableAutoType = null;
|
||||
public Boolean enableSearching = null;
|
||||
public UUID lastTopVisibleEntry = PwDatabaseV4.UUID_ZERO;
|
||||
private Date parentGroupLastMod = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date creation = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date lastMod = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date lastAccess = PwDatabaseV4.DEFAULT_NOW;
|
||||
private Date expireDate = PwDatabaseV4.DEFAULT_NOW;
|
||||
private boolean expires = false;
|
||||
private long usageCount = 0;
|
||||
public Map<String, String> customData = new HashMap<String, String>();
|
||||
|
||||
public PwGroupV4() {
|
||||
|
||||
}
|
||||
|
||||
public PwGroupV4(boolean createUUID, boolean setTimes, String name, PwIconStandard icon) {
|
||||
if (createUUID) {
|
||||
uuid = UUID.randomUUID();
|
||||
}
|
||||
|
||||
if (setTimes) {
|
||||
creation = lastMod = lastAccess = new Date();
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public void AddGroup(PwGroupV4 subGroup, boolean takeOwnership) {
|
||||
AddGroup(subGroup, takeOwnership, false);
|
||||
}
|
||||
|
||||
public void AddGroup(PwGroupV4 subGroup, boolean takeOwnership, boolean updateLocationChanged) {
|
||||
if ( subGroup == null ) throw new RuntimeException("subGroup");
|
||||
|
||||
childGroups.add(subGroup);
|
||||
|
||||
if ( takeOwnership ) subGroup.parent = this;
|
||||
|
||||
if ( updateLocationChanged ) subGroup.parentGroupLastMod = new Date(System.currentTimeMillis());
|
||||
|
||||
}
|
||||
|
||||
public void AddEntry(PwEntryV4 pe, boolean takeOwnership) {
|
||||
AddEntry(pe, takeOwnership, false);
|
||||
}
|
||||
|
||||
public void AddEntry(PwEntryV4 pe, boolean takeOwnership, boolean updateLocationChanged) {
|
||||
assert(pe != null);
|
||||
|
||||
childEntries.add(pe);
|
||||
|
||||
if ( takeOwnership ) pe.parent = this;
|
||||
|
||||
if ( updateLocationChanged ) pe.setLocationChanged(new Date(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroup getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void buildChildGroupsRecursive(List<PwGroup> list) {
|
||||
list.add(this);
|
||||
|
||||
for ( int i = 0; i < childGroups.size(); i++) {
|
||||
PwGroupV4 child = (PwGroupV4) childGroups.get(i);
|
||||
child.buildChildGroupsRecursive(list);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void buildChildEntriesRecursive(List<PwEntry> list) {
|
||||
for ( int i = 0; i < childEntries.size(); i++ ) {
|
||||
list.add(childEntries.get(i));
|
||||
}
|
||||
|
||||
for ( int i = 0; i < childGroups.size(); i++ ) {
|
||||
PwGroupV4 child = (PwGroupV4) childGroups.get(i);
|
||||
child.buildChildEntriesRecursive(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwGroupId getId() {
|
||||
return new PwGroupIdV4(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(PwGroupId id) {
|
||||
PwGroupIdV4 id4 = (PwGroupIdV4) id;
|
||||
uuid = id4.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastMod() {
|
||||
return parentGroupLastMod;
|
||||
}
|
||||
|
||||
public Date getCreationTime() {
|
||||
return creation;
|
||||
}
|
||||
|
||||
public Date getExpiryTime() {
|
||||
return expireDate;
|
||||
}
|
||||
|
||||
public Date getLastAccessTime() {
|
||||
return lastAccess;
|
||||
}
|
||||
|
||||
public Date getLastModificationTime() {
|
||||
return lastMod;
|
||||
}
|
||||
|
||||
public Date getLocationChanged() {
|
||||
return parentGroupLastMod;
|
||||
}
|
||||
|
||||
public long getUsageCount() {
|
||||
return usageCount;
|
||||
}
|
||||
|
||||
public void setCreationTime(Date date) {
|
||||
creation = date;
|
||||
|
||||
}
|
||||
|
||||
public void setExpiryTime(Date date) {
|
||||
expireDate = date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastAccessTime(Date date) {
|
||||
lastAccess = date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModificationTime(Date date) {
|
||||
lastMod = date;
|
||||
}
|
||||
|
||||
public void setLocationChanged(Date date) {
|
||||
parentGroupLastMod = date;
|
||||
}
|
||||
|
||||
public void setUsageCount(long count) {
|
||||
usageCount = count;
|
||||
}
|
||||
|
||||
public boolean expires() {
|
||||
return expires;
|
||||
}
|
||||
|
||||
public void setExpires(boolean exp) {
|
||||
expires = exp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(PwGroup prt) {
|
||||
parent = (PwGroupV4) prt;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwIcon getIcon() {
|
||||
if (customIcon == null || customIcon.uuid.equals(PwDatabaseV4.UUID_ZERO)) {
|
||||
return super.getIcon();
|
||||
} else {
|
||||
return customIcon;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initNewGroup(String nm, PwGroupId newId) {
|
||||
super.initNewGroup(nm, newId);
|
||||
|
||||
lastAccess = lastMod = creation = parentGroupLastMod = new Date();
|
||||
}
|
||||
|
||||
public boolean isSearchEnabled() {
|
||||
PwGroupV4 group = this;
|
||||
while (group != null) {
|
||||
Boolean search = group.enableSearching;
|
||||
if (search != null) {
|
||||
return search;
|
||||
}
|
||||
|
||||
group = group.parent;
|
||||
}
|
||||
|
||||
// If we get to the root tree and its null, default to true
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.keepassdroid.database;
|
||||
|
||||
public abstract class PwIcon {
|
||||
|
||||
public boolean isMetaStreamIcon() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void writeBytes() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database;
|
||||
|
||||
public class SearchParametersFactory {
|
||||
public static SearchParameters getNone(PwDatabase db) {
|
||||
SearchParameters sp = getInstance(db);
|
||||
sp.setupNone();
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
public static SearchParameters getInstance(PwDatabase db) {
|
||||
if (db instanceof PwDatabase) {
|
||||
return new SearchParametersV4();
|
||||
}
|
||||
else {
|
||||
return new SearchParameters();
|
||||
}
|
||||
}
|
||||
|
||||
public static SearchParameters getInstance(PwGroup group) {
|
||||
if (group instanceof PwGroupV4) {
|
||||
return new SearchParametersV4();
|
||||
}
|
||||
else {
|
||||
return new SearchParameters();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database.edit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.GroupBaseActivity;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
|
||||
public class DeleteGroup extends RunnableOnFinish {
|
||||
|
||||
private Database mDb;
|
||||
private PwGroup mGroup;
|
||||
private GroupBaseActivity mAct;
|
||||
private boolean mDontSave;
|
||||
|
||||
public DeleteGroup(Database db, PwGroup group, GroupBaseActivity act, OnFinish finish) {
|
||||
super(finish);
|
||||
setMembers(db, group, act, false);
|
||||
}
|
||||
|
||||
public DeleteGroup(Database db, PwGroup group, GroupBaseActivity act, OnFinish finish, boolean dontSave) {
|
||||
super(finish);
|
||||
setMembers(db, group, act, dontSave);
|
||||
}
|
||||
|
||||
|
||||
public DeleteGroup(Database db, PwGroup group, OnFinish finish, boolean dontSave) {
|
||||
super(finish);
|
||||
setMembers(db, group, null, dontSave);
|
||||
}
|
||||
|
||||
private void setMembers(Database db, PwGroup group, GroupBaseActivity act, boolean dontSave) {
|
||||
mDb = db;
|
||||
mGroup = group;
|
||||
mAct = act;
|
||||
mDontSave = dontSave;
|
||||
|
||||
mFinish = new AfterDelete(mFinish);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// Remove child entries
|
||||
List<PwEntry> childEnt = new ArrayList<PwEntry>(mGroup.childEntries);
|
||||
for ( int i = 0; i < childEnt.size(); i++ ) {
|
||||
DeleteEntry task = new DeleteEntry(mAct, mDb, childEnt.get(i), null, true);
|
||||
task.run();
|
||||
}
|
||||
|
||||
// Remove child groups
|
||||
List<PwGroup> childGrp = new ArrayList<PwGroup>(mGroup.childGroups);
|
||||
for ( int i = 0; i < childGrp.size(); i++ ) {
|
||||
DeleteGroup task = new DeleteGroup(mDb, childGrp.get(i), mAct, null, true);
|
||||
task.run();
|
||||
}
|
||||
|
||||
|
||||
// Remove from parent
|
||||
PwGroup parent = mGroup.getParent();
|
||||
if ( parent != null ) {
|
||||
parent.childGroups.remove(mGroup);
|
||||
}
|
||||
|
||||
// Remove from PwDatabaseV3
|
||||
mDb.pm.getGroups().remove(mGroup);
|
||||
|
||||
// Save
|
||||
SaveDB save = new SaveDB(mAct, mDb, mFinish, mDontSave);
|
||||
save.run();
|
||||
|
||||
}
|
||||
|
||||
private class AfterDelete extends OnFinish {
|
||||
public AfterDelete(OnFinish finish) {
|
||||
super(finish);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if ( mSuccess ) {
|
||||
// Remove from tree global
|
||||
mDb.pm.groups.remove(mGroup.getId());
|
||||
|
||||
// Remove tree from the dirty global (if it is present), not a big deal if this fails
|
||||
mDb.dirty.remove(mGroup);
|
||||
|
||||
// Mark parent dirty
|
||||
PwGroup parent = mGroup.getParent();
|
||||
if ( parent != null ) {
|
||||
mDb.dirty.add(parent);
|
||||
}
|
||||
mDb.dirty.add(mDb.pm.rootGroup);
|
||||
} else {
|
||||
// Let's not bother recovering from a failure to save a deleted tree. It is too much work.
|
||||
App.setShutdown();
|
||||
}
|
||||
|
||||
super.run();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database.load;
|
||||
|
||||
import com.keepassdroid.UpdateStatus;
|
||||
import com.keepassdroid.database.PwDatabaseV4Debug;
|
||||
import com.keepassdroid.database.exception.InvalidDBException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ImporterV4Debug extends ImporterV4 {
|
||||
|
||||
@Override
|
||||
protected PwDatabaseV4Debug createDB() {
|
||||
return new PwDatabaseV4Debug();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwDatabaseV4Debug openDatabase(InputStream inStream, String password,
|
||||
InputStream keyInputFile, UpdateStatus status, long roundsFix) throws IOException,
|
||||
InvalidDBException {
|
||||
return (PwDatabaseV4Debug) super.openDatabase(inStream, password, keyInputFile, status,
|
||||
roundsFix);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,840 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.database.save;
|
||||
|
||||
import android.util.Xml;
|
||||
|
||||
import com.keepassdroid.crypto.CipherFactory;
|
||||
import com.keepassdroid.crypto.PwStreamCipherFactory;
|
||||
import com.keepassdroid.crypto.engine.CipherEngine;
|
||||
import com.keepassdroid.crypto.keyDerivation.KdfEngine;
|
||||
import com.keepassdroid.crypto.keyDerivation.KdfFactory;
|
||||
import com.keepassdroid.database.CrsAlgorithm;
|
||||
import com.keepassdroid.database.EntryHandler;
|
||||
import com.keepassdroid.database.GroupHandler;
|
||||
import com.keepassdroid.database.ITimeLogger;
|
||||
import com.keepassdroid.database.PwCompressionAlgorithm;
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwDatabaseV4.MemoryProtectionConfig;
|
||||
import com.keepassdroid.database.PwDatabaseV4XML;
|
||||
import com.keepassdroid.database.PwDbHeader;
|
||||
import com.keepassdroid.database.PwDbHeaderV4;
|
||||
import com.keepassdroid.database.PwDefsV4;
|
||||
import com.keepassdroid.database.PwDeletedObject;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.PwEntryV4;
|
||||
import com.keepassdroid.database.PwEntryV4.AutoType;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.PwGroupV4;
|
||||
import com.keepassdroid.database.PwIconCustom;
|
||||
import com.keepassdroid.database.exception.PwDbOutputException;
|
||||
import com.keepassdroid.database.security.ProtectedBinary;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
import com.keepassdroid.stream.HashedBlockOutputStream;
|
||||
import com.keepassdroid.stream.HmacBlockOutputStream;
|
||||
import com.keepassdroid.stream.LEDataOutputStream;
|
||||
import com.keepassdroid.utils.DateUtil;
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
import com.keepassdroid.utils.MemUtil;
|
||||
import com.keepassdroid.utils.Types;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.spongycastle.crypto.StreamCipher;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Stack;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
|
||||
import biz.source_code.base64Coder.Base64Coder;
|
||||
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.AttrCompressed;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.AttrId;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.AttrProtected;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.AttrRef;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoType;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoTypeDefaultSeq;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoTypeEnabled;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoTypeItem;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoTypeObfuscation;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemBgColor;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemBinaries;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemBinary;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCreationTime;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomData;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIconID;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIconItem;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIconItemData;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIconItemID;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIcons;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbColor;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbDefaultUser;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbDefaultUserChanged;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbDesc;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbDescChanged;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbKeyChangeForce;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbKeyChangeRec;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbKeyChanged;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbMntncHistoryDays;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbName;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbNameChanged;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDeletedObject;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDeletedObjects;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDeletionTime;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDocNode;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEnableAutoType;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEnableSearching;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEntry;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEntryTemplatesGroup;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEntryTemplatesGroupChanged;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemExpires;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemExpiryTime;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemFgColor;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemGenerator;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemGroup;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemGroupDefaultAutoTypeSeq;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemHeaderHash;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemHistory;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemHistoryMaxItems;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemHistoryMaxSize;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemIcon;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemIsExpanded;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemKey;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemKeystrokeSequence;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastAccessTime;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastModTime;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastSelectedGroup;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastTopVisibleEntry;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastTopVisibleGroup;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLocationChanged;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemMemoryProt;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemMeta;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemName;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemNotes;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemOverrideUrl;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtNotes;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtPassword;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtTitle;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtURL;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtUserName;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemRecycleBinChanged;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemRecycleBinEnabled;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemRecycleBinUuid;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemRoot;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemString;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemStringDictExItem;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemTags;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemTimes;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemUsageCount;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemUuid;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemValue;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ElemWindow;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ValFalse;
|
||||
import static com.keepassdroid.database.PwDatabaseV4XML.ValTrue;
|
||||
|
||||
public class PwDbV4Output extends PwDbOutput {
|
||||
|
||||
PwDatabaseV4 mPM;
|
||||
private StreamCipher randomStream;
|
||||
private XmlSerializer xml;
|
||||
private PwDbHeaderV4 header;
|
||||
private byte[] hashOfHeader;
|
||||
private byte[] headerHmac;
|
||||
private CipherEngine engine = null;
|
||||
|
||||
protected PwDbV4Output(PwDatabaseV4 pm, OutputStream os) {
|
||||
super(os);
|
||||
|
||||
mPM = pm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output() throws PwDbOutputException {
|
||||
|
||||
try {
|
||||
try {
|
||||
engine = CipherFactory.getInstance(mPM.dataCipher);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new PwDbOutputException("No such cipher", e);
|
||||
}
|
||||
|
||||
header = (PwDbHeaderV4) outputHeader(mOS);
|
||||
|
||||
OutputStream osPlain;
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
CipherOutputStream cos = attachStreamEncryptor(header, mOS);
|
||||
cos.write(header.streamStartBytes);
|
||||
|
||||
HashedBlockOutputStream hashed = new HashedBlockOutputStream(cos);
|
||||
osPlain = hashed;
|
||||
} else {
|
||||
mOS.write(hashOfHeader);
|
||||
mOS.write(headerHmac);
|
||||
|
||||
HmacBlockOutputStream hbos = new HmacBlockOutputStream(mOS, mPM.hmacKey);
|
||||
osPlain = attachStreamEncryptor(header, hbos);
|
||||
}
|
||||
|
||||
OutputStream osXml;
|
||||
try {
|
||||
|
||||
|
||||
if (mPM.compressionAlgorithm == PwCompressionAlgorithm.Gzip) {
|
||||
osXml = new GZIPOutputStream(osPlain);
|
||||
} else {
|
||||
osXml = osPlain;
|
||||
}
|
||||
|
||||
if (header.version >= PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
PwDbInnerHeaderOutputV4 ihOut = new PwDbInnerHeaderOutputV4((PwDatabaseV4)mPM, header, osXml);
|
||||
ihOut.output();
|
||||
}
|
||||
|
||||
|
||||
outputDatabase(osXml);
|
||||
osXml.close();
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new PwDbOutputException(e);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new PwDbOutputException(e);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PwDbOutputException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private class GroupWriter extends GroupHandler<PwGroup> {
|
||||
private Stack<PwGroupV4> groupStack;
|
||||
|
||||
public GroupWriter(Stack<PwGroupV4> gs) {
|
||||
groupStack = gs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean operate(PwGroup g) {
|
||||
PwGroupV4 group = (PwGroupV4) g;
|
||||
assert(group != null);
|
||||
|
||||
while(true) {
|
||||
try {
|
||||
if (group.parent == groupStack.peek()) {
|
||||
groupStack.push(group);
|
||||
startGroup(group);
|
||||
break;
|
||||
} else {
|
||||
groupStack.pop();
|
||||
if (groupStack.size() <= 0) return false;
|
||||
endGroup();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryWriter extends EntryHandler<PwEntry> {
|
||||
|
||||
@Override
|
||||
public boolean operate(PwEntry e) {
|
||||
PwEntryV4 entry = (PwEntryV4) e;
|
||||
assert(entry != null);
|
||||
|
||||
try {
|
||||
writeEntry(entry, false);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void outputDatabase(OutputStream os) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
|
||||
xml = Xml.newSerializer();
|
||||
|
||||
xml.setOutput(os, "UTF-8");
|
||||
xml.startDocument("UTF-8", true);
|
||||
|
||||
xml.startTag(null, ElemDocNode);
|
||||
|
||||
writeMeta();
|
||||
|
||||
PwGroupV4 root = (PwGroupV4) mPM.rootGroup;
|
||||
xml.startTag(null, ElemRoot);
|
||||
startGroup(root);
|
||||
Stack<PwGroupV4> groupStack = new Stack<PwGroupV4>();
|
||||
groupStack.push(root);
|
||||
|
||||
if (!root.preOrderTraverseTree(new GroupWriter(groupStack), new EntryWriter())) throw new RuntimeException("Writing groups failed");
|
||||
|
||||
while (groupStack.size() > 1) {
|
||||
xml.endTag(null, ElemGroup);
|
||||
groupStack.pop();
|
||||
}
|
||||
|
||||
endGroup();
|
||||
|
||||
writeList(ElemDeletedObjects, mPM.deletedObjects);
|
||||
|
||||
xml.endTag(null, ElemRoot);
|
||||
|
||||
xml.endTag(null, ElemDocNode);
|
||||
xml.endDocument();
|
||||
|
||||
}
|
||||
|
||||
private void writeMeta() throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
xml.startTag(null, ElemMeta);
|
||||
|
||||
writeObject(ElemGenerator, mPM.localizedAppName);
|
||||
|
||||
if (hashOfHeader != null) {
|
||||
writeObject(ElemHeaderHash, String.valueOf(Base64Coder.encode(hashOfHeader)));
|
||||
}
|
||||
|
||||
writeObject(ElemDbName, mPM.name, true);
|
||||
writeObject(ElemDbNameChanged, mPM.nameChanged);
|
||||
writeObject(ElemDbDesc, mPM.description, true);
|
||||
writeObject(ElemDbDescChanged, mPM.descriptionChanged);
|
||||
writeObject(ElemDbDefaultUser, mPM.defaultUserName, true);
|
||||
writeObject(ElemDbDefaultUserChanged, mPM.defaultUserNameChanged);
|
||||
writeObject(ElemDbMntncHistoryDays, mPM.maintenanceHistoryDays);
|
||||
writeObject(ElemDbColor, mPM.color);
|
||||
writeObject(ElemDbKeyChanged, mPM.keyLastChanged);
|
||||
writeObject(ElemDbKeyChangeRec, mPM.keyChangeRecDays);
|
||||
writeObject(ElemDbKeyChangeForce, mPM.keyChangeForceDays);
|
||||
|
||||
|
||||
writeList(ElemMemoryProt, mPM.memoryProtection);
|
||||
|
||||
writeCustomIconList();
|
||||
|
||||
writeObject(ElemRecycleBinEnabled, mPM.recycleBinEnabled);
|
||||
writeObject(ElemRecycleBinUuid, mPM.recycleBinUUID);
|
||||
writeObject(ElemRecycleBinChanged, mPM.recycleBinChanged);
|
||||
writeObject(ElemEntryTemplatesGroup, mPM.entryTemplatesGroup);
|
||||
writeObject(ElemEntryTemplatesGroupChanged, mPM.entryTemplatesGroupChanged);
|
||||
writeObject(ElemHistoryMaxItems, mPM.historyMaxItems);
|
||||
writeObject(ElemHistoryMaxSize, mPM.historyMaxSize);
|
||||
writeObject(ElemLastSelectedGroup, mPM.lastSelectedGroup);
|
||||
writeObject(ElemLastTopVisibleGroup, mPM.lastTopVisibleGroup);
|
||||
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
writeBinPool();
|
||||
}
|
||||
writeList(ElemCustomData, mPM.customData);
|
||||
|
||||
xml.endTag(null, ElemMeta);
|
||||
|
||||
}
|
||||
|
||||
private CipherOutputStream attachStreamEncryptor(PwDbHeaderV4 header, OutputStream os) throws PwDbOutputException {
|
||||
Cipher cipher;
|
||||
try {
|
||||
//mPM.makeFinalKey(header.masterSeed, mPM.kdfParameters);
|
||||
|
||||
cipher = engine.getCipher(Cipher.ENCRYPT_MODE, mPM.finalKey, header.encryptionIV);
|
||||
} catch (Exception e) {
|
||||
throw new PwDbOutputException("Invalid algorithm.", e);
|
||||
}
|
||||
|
||||
CipherOutputStream cos = new CipherOutputStream(os, cipher);
|
||||
|
||||
return cos;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SecureRandom setIVs(PwDbHeader header) throws PwDbOutputException {
|
||||
SecureRandom random = super.setIVs(header);
|
||||
|
||||
PwDbHeaderV4 h = (PwDbHeaderV4) header;
|
||||
random.nextBytes(h.masterSeed);
|
||||
|
||||
int ivLength = engine.ivLength();
|
||||
if (ivLength != h.encryptionIV.length) {
|
||||
h.encryptionIV = new byte[ivLength];
|
||||
}
|
||||
random.nextBytes(h.encryptionIV);
|
||||
|
||||
UUID kdfUUID = mPM.kdfParameters.kdfUUID;
|
||||
KdfEngine kdf = KdfFactory.get(kdfUUID);
|
||||
kdf.randomize(mPM.kdfParameters);
|
||||
|
||||
if (h.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
h.innerRandomStream = CrsAlgorithm.Salsa20;
|
||||
h.innerRandomStreamKey = new byte[32];
|
||||
} else {
|
||||
h.innerRandomStream = CrsAlgorithm.ChaCha20;
|
||||
h.innerRandomStreamKey = new byte[64];
|
||||
}
|
||||
random.nextBytes(h.innerRandomStreamKey);
|
||||
|
||||
randomStream = PwStreamCipherFactory.getInstance(h.innerRandomStream, h.innerRandomStreamKey);
|
||||
if (randomStream == null) {
|
||||
throw new PwDbOutputException("Invalid random cipher");
|
||||
}
|
||||
|
||||
if ( h.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
random.nextBytes(h.streamStartBytes);
|
||||
}
|
||||
|
||||
return random;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PwDbHeader outputHeader(OutputStream os) throws PwDbOutputException {
|
||||
PwDbHeaderV4 header = new PwDbHeaderV4(mPM);
|
||||
setIVs(header);
|
||||
|
||||
PwDbHeaderOutputV4 pho = new PwDbHeaderOutputV4(mPM, header, os);
|
||||
try {
|
||||
pho.output();
|
||||
} catch (IOException e) {
|
||||
throw new PwDbOutputException("Failed to output the header.", e);
|
||||
}
|
||||
|
||||
hashOfHeader = pho.getHashOfHeader();
|
||||
headerHmac = pho.headerHmac;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private void startGroup(PwGroupV4 group) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
xml.startTag(null, ElemGroup);
|
||||
writeObject(ElemUuid, group.uuid);
|
||||
writeObject(ElemName, group.name);
|
||||
writeObject(ElemNotes, group.notes);
|
||||
writeObject(ElemIcon, group.icon.iconId);
|
||||
|
||||
if (!group.customIcon.equals(PwIconCustom.ZERO)) {
|
||||
writeObject(ElemCustomIconID, group.customIcon.uuid);
|
||||
}
|
||||
|
||||
writeList(ElemTimes, group);
|
||||
writeObject(ElemIsExpanded, group.isExpanded);
|
||||
writeObject(ElemGroupDefaultAutoTypeSeq, group.defaultAutoTypeSequence);
|
||||
writeObject(ElemEnableAutoType, group.enableAutoType);
|
||||
writeObject(ElemEnableSearching, group.enableSearching);
|
||||
writeObject(ElemLastTopVisibleEntry, group.lastTopVisibleEntry);
|
||||
|
||||
}
|
||||
|
||||
private void endGroup() throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
xml.endTag(null, ElemGroup);
|
||||
}
|
||||
|
||||
private void writeEntry(PwEntryV4 entry, boolean isHistory) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(entry != null);
|
||||
|
||||
xml.startTag(null, ElemEntry);
|
||||
|
||||
writeObject(ElemUuid, entry.uuid);
|
||||
writeObject(ElemIcon, entry.icon.iconId);
|
||||
|
||||
if (!entry.customIcon.equals(PwIconCustom.ZERO)) {
|
||||
writeObject(ElemCustomIconID, entry.customIcon.uuid);
|
||||
}
|
||||
|
||||
writeObject(ElemFgColor, entry.foregroundColor);
|
||||
writeObject(ElemBgColor, entry.backgroupColor);
|
||||
writeObject(ElemOverrideUrl, entry.overrideURL);
|
||||
writeObject(ElemTags, entry.tags);
|
||||
|
||||
writeList(ElemTimes, entry);
|
||||
|
||||
writeList(entry.strings, true);
|
||||
writeList(entry.binaries);
|
||||
writeList(ElemAutoType, entry.autoType);
|
||||
|
||||
if (!isHistory) {
|
||||
writeList(ElemHistory, entry.history, true);
|
||||
} else {
|
||||
assert(entry.history.size() == 0);
|
||||
}
|
||||
|
||||
xml.endTag(null, ElemEntry);
|
||||
}
|
||||
|
||||
|
||||
private void writeObject(String key, ProtectedBinary value, boolean allowRef) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(key != null && value != null);
|
||||
|
||||
xml.startTag(null, ElemBinary);
|
||||
xml.startTag(null, ElemKey);
|
||||
xml.text(safeXmlString(key));
|
||||
xml.endTag(null, ElemKey);
|
||||
|
||||
xml.startTag(null, ElemValue);
|
||||
String strRef = null;
|
||||
if (allowRef) {
|
||||
int ref = mPM.binPool.poolFind(value);
|
||||
strRef = Integer.toString(ref);
|
||||
}
|
||||
|
||||
if (strRef != null) {
|
||||
xml.attribute(null, AttrRef, strRef);
|
||||
}
|
||||
else {
|
||||
subWriteValue(value);
|
||||
}
|
||||
xml.endTag(null, ElemValue);
|
||||
|
||||
xml.endTag(null, ElemBinary);
|
||||
}
|
||||
|
||||
private void subWriteValue(ProtectedBinary value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
if (value.isProtected()) {
|
||||
xml.attribute(null, AttrProtected, ValTrue);
|
||||
|
||||
int valLength = value.length();
|
||||
if (valLength > 0) {
|
||||
byte[] encoded = new byte[valLength];
|
||||
randomStream.processBytes(value.getData(), 0, valLength, encoded, 0);
|
||||
|
||||
xml.text(String.valueOf(Base64Coder.encode(encoded)));
|
||||
}
|
||||
|
||||
} else {
|
||||
if (mPM.compressionAlgorithm == PwCompressionAlgorithm.Gzip) {
|
||||
xml.attribute(null, AttrCompressed, ValTrue);
|
||||
byte[] raw = value.getData();
|
||||
byte[] compressed = MemUtil.compress(raw);
|
||||
xml.text(String.valueOf(Base64Coder.encode(compressed)));
|
||||
} else {
|
||||
byte[] raw = value.getData();
|
||||
xml.text(String.valueOf(Base64Coder.encode(raw)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void writeObject(String name, String value, boolean filterXmlChars) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(name != null && value != null);
|
||||
|
||||
xml.startTag(null, name);
|
||||
|
||||
if (filterXmlChars) {
|
||||
value = safeXmlString(value);
|
||||
}
|
||||
|
||||
xml.text(value);
|
||||
xml.endTag(null, name);
|
||||
}
|
||||
|
||||
private void writeObject(String name, String value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
writeObject(name, value, false);
|
||||
}
|
||||
|
||||
private void writeObject(String name, Date value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||
writeObject(name, PwDatabaseV4XML.dateFormatter.get().format(value));
|
||||
} else {
|
||||
DateTime dt = new DateTime(value);
|
||||
long seconds = DateUtil.convertDateToKDBX4Time(dt);
|
||||
byte[] buf = LEDataOutputStream.writeLongBuf(seconds);
|
||||
String b64 = new String(Base64Coder.encode(buf));
|
||||
writeObject(name, b64);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void writeObject(String name, long value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
writeObject(name, String.valueOf(value));
|
||||
}
|
||||
|
||||
private void writeObject(String name, Boolean value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
String text;
|
||||
if (value == null) {
|
||||
text = "null";
|
||||
}
|
||||
else if (value) {
|
||||
text = ValTrue;
|
||||
}
|
||||
else {
|
||||
text = ValFalse;
|
||||
}
|
||||
|
||||
writeObject(name, text);
|
||||
}
|
||||
|
||||
private void writeObject(String name, UUID uuid) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
byte[] data = Types.UUIDtoBytes(uuid);
|
||||
writeObject(name, String.valueOf(Base64Coder.encode(data)));
|
||||
}
|
||||
|
||||
private void writeObject(String name, String keyName, String keyValue, String valueName, String valueValue) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
xml.startTag(null, name);
|
||||
|
||||
xml.startTag(null, keyName);
|
||||
xml.text(safeXmlString(keyValue));
|
||||
xml.endTag(null, keyName);
|
||||
|
||||
xml.startTag(null, valueName);
|
||||
xml.text(safeXmlString(valueValue));
|
||||
xml.endTag(null, valueName);
|
||||
|
||||
xml.endTag(null, name);
|
||||
}
|
||||
|
||||
private void writeList(String name, AutoType autoType) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(name != null && autoType != null);
|
||||
|
||||
xml.startTag(null, name);
|
||||
|
||||
writeObject(ElemAutoTypeEnabled, autoType.enabled);
|
||||
writeObject(ElemAutoTypeObfuscation, autoType.obfuscationOptions);
|
||||
|
||||
if (autoType.defaultSequence.length() > 0) {
|
||||
writeObject(ElemAutoTypeDefaultSeq, autoType.defaultSequence, true);
|
||||
}
|
||||
|
||||
for (Entry<String, String> pair : autoType.entrySet()) {
|
||||
writeObject(ElemAutoTypeItem, ElemWindow, pair.getKey(), ElemKeystrokeSequence, pair.getValue());
|
||||
}
|
||||
|
||||
xml.endTag(null, name);
|
||||
|
||||
}
|
||||
|
||||
private void writeList(Map<String, ProtectedString> strings, boolean isEntryString) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert (strings != null);
|
||||
|
||||
for (Entry<String, ProtectedString> pair : strings.entrySet()) {
|
||||
writeObject(pair.getKey(), pair.getValue(), isEntryString);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void writeObject(String key, ProtectedString value, boolean isEntryString) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(key !=null && value != null);
|
||||
|
||||
xml.startTag(null, ElemString);
|
||||
xml.startTag(null, ElemKey);
|
||||
xml.text(safeXmlString(key));
|
||||
xml.endTag(null, ElemKey);
|
||||
|
||||
xml.startTag(null, ElemValue);
|
||||
boolean protect = value.isProtected();
|
||||
if (isEntryString) {
|
||||
if (key.equals(PwDefsV4.TITLE_FIELD)) {
|
||||
protect = mPM.memoryProtection.protectTitle;
|
||||
}
|
||||
else if (key.equals(PwDefsV4.USERNAME_FIELD)) {
|
||||
protect = mPM.memoryProtection.protectUserName;
|
||||
}
|
||||
else if (key.equals(PwDefsV4.PASSWORD_FIELD)) {
|
||||
protect = mPM.memoryProtection.protectPassword;
|
||||
}
|
||||
else if (key.equals(PwDefsV4.URL_FIELD)) {
|
||||
protect = mPM.memoryProtection.protectUrl;
|
||||
}
|
||||
else if (key.equals(PwDefsV4.NOTES_FIELD)) {
|
||||
protect = mPM.memoryProtection.protectNotes;
|
||||
}
|
||||
}
|
||||
|
||||
if (protect) {
|
||||
xml.attribute(null, AttrProtected, ValTrue);
|
||||
|
||||
byte[] data = value.toString().getBytes("UTF-8");
|
||||
int valLength = data.length;
|
||||
|
||||
if (valLength > 0) {
|
||||
byte[] encoded = new byte[valLength];
|
||||
randomStream.processBytes(data, 0, valLength, encoded, 0);
|
||||
xml.text(String.valueOf(Base64Coder.encode(encoded)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
xml.text(safeXmlString(value.toString()));
|
||||
}
|
||||
|
||||
xml.endTag(null, ElemValue);
|
||||
xml.endTag(null, ElemString);
|
||||
|
||||
}
|
||||
|
||||
private void writeObject(String name, PwDeletedObject value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(name != null && value != null);
|
||||
|
||||
xml.startTag(null, name);
|
||||
|
||||
writeObject(ElemUuid, value.uuid);
|
||||
writeObject(ElemDeletionTime, value.getDeletionTime());
|
||||
|
||||
xml.endTag(null, name);
|
||||
}
|
||||
|
||||
private void writeList(Map<String, ProtectedBinary> binaries) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(binaries != null);
|
||||
|
||||
for (Entry<String, ProtectedBinary> pair : binaries.entrySet()) {
|
||||
writeObject(pair.getKey(), pair.getValue(), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void writeList(String name, List<PwDeletedObject> value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(name != null && value != null);
|
||||
|
||||
xml.startTag(null, name);
|
||||
|
||||
for (PwDeletedObject pdo : value) {
|
||||
writeObject(ElemDeletedObject, pdo);
|
||||
}
|
||||
|
||||
xml.endTag(null, name);
|
||||
|
||||
}
|
||||
|
||||
private void writeList(String name, MemoryProtectionConfig value) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(name != null && value != null);
|
||||
|
||||
xml.startTag(null, name);
|
||||
|
||||
writeObject(ElemProtTitle, value.protectTitle);
|
||||
writeObject(ElemProtUserName, value.protectUserName);
|
||||
writeObject(ElemProtPassword, value.protectPassword);
|
||||
writeObject(ElemProtURL, value.protectUrl);
|
||||
writeObject(ElemProtNotes, value.protectNotes);
|
||||
|
||||
xml.endTag(null, name);
|
||||
|
||||
}
|
||||
|
||||
private void writeList(String name, Map<String, String> customData) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(name != null && customData != null);
|
||||
|
||||
xml.startTag(null, name);
|
||||
|
||||
for (Entry<String, String> pair : customData.entrySet()) {
|
||||
writeObject(ElemStringDictExItem, ElemKey, pair.getKey(), ElemValue, pair.getValue());
|
||||
|
||||
}
|
||||
|
||||
xml.endTag(null, name);
|
||||
|
||||
}
|
||||
|
||||
private void writeList(String name, ITimeLogger it) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(name != null && it != null);
|
||||
|
||||
xml.startTag(null, name);
|
||||
|
||||
writeObject(ElemLastModTime, it.getLastModificationTime());
|
||||
writeObject(ElemCreationTime, it.getCreationTime());
|
||||
writeObject(ElemLastAccessTime, it.getLastAccessTime());
|
||||
writeObject(ElemExpiryTime, it.getExpiryTime());
|
||||
writeObject(ElemExpires, it.expires());
|
||||
writeObject(ElemUsageCount, it.getUsageCount());
|
||||
writeObject(ElemLocationChanged, it.getLocationChanged());
|
||||
|
||||
xml.endTag(null, name);
|
||||
}
|
||||
|
||||
private void writeList(String name, List<PwEntryV4> value, boolean isHistory) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
assert(name != null && value != null);
|
||||
|
||||
xml.startTag(null, name);
|
||||
|
||||
for (PwEntryV4 entry : value) {
|
||||
writeEntry(entry, isHistory);
|
||||
}
|
||||
|
||||
xml.endTag(null, name);
|
||||
|
||||
}
|
||||
|
||||
private void writeCustomIconList() throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
List<PwIconCustom> customIcons = mPM.customIcons;
|
||||
if (customIcons.size() == 0) return;
|
||||
|
||||
xml.startTag(null, ElemCustomIcons);
|
||||
|
||||
for (PwIconCustom icon : customIcons) {
|
||||
xml.startTag(null, ElemCustomIconItem);
|
||||
|
||||
writeObject(ElemCustomIconItemID, icon.uuid);
|
||||
writeObject(ElemCustomIconItemData, String.valueOf(Base64Coder.encode(icon.imageData)));
|
||||
|
||||
xml.endTag(null, ElemCustomIconItem);
|
||||
}
|
||||
|
||||
xml.endTag(null, ElemCustomIcons);
|
||||
}
|
||||
|
||||
private void writeBinPool() throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
xml.startTag(null, ElemBinaries);
|
||||
|
||||
for (Entry<Integer, ProtectedBinary> pair : mPM.binPool.entrySet()) {
|
||||
xml.startTag(null, ElemBinary);
|
||||
xml.attribute(null, AttrId, Integer.toString(pair.getKey()));
|
||||
|
||||
subWriteValue(pair.getValue());
|
||||
|
||||
xml.endTag(null, ElemBinary);
|
||||
|
||||
}
|
||||
|
||||
xml.endTag(null, ElemBinaries);
|
||||
|
||||
}
|
||||
|
||||
private String safeXmlString(String text) {
|
||||
if (EmptyUtils.isNullOrEmpty(text)) {
|
||||
return text;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
char ch;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
ch = text.charAt(i);
|
||||
|
||||
if(((ch >= 0x20) && (ch <= 0xD7FF)) ||
|
||||
(ch == 0x9) || (ch == 0xA) || (ch == 0xD) ||
|
||||
((ch >= 0xE000) && (ch <= 0xFFFD))) {
|
||||
|
||||
sb.append(ch);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.fileselect;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.utils.Util;
|
||||
|
||||
public class BrowserDialog extends Dialog {
|
||||
|
||||
public BrowserDialog(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.browser_install);
|
||||
setTitle(R.string.file_browser);
|
||||
|
||||
Button cancel = (Button) findViewById(R.id.cancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
BrowserDialog.this.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
Button market = (Button) findViewById(R.id.install_market);
|
||||
market.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
Util.gotoUrl(getContext(), R.string.oi_filemanager_market);
|
||||
BrowserDialog.this.cancel();
|
||||
}
|
||||
});
|
||||
if (!isMarketInstalled()) {
|
||||
market.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
Button web = (Button) findViewById(R.id.install_web);
|
||||
web.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
Util.gotoUrl(getContext(), R.string.oi_filemanager_web);
|
||||
BrowserDialog.this.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isMarketInstalled() {
|
||||
PackageManager pm = getContext().getPackageManager();
|
||||
|
||||
try {
|
||||
pm.getPackageInfo("com.android.vending", 0);
|
||||
} catch (NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,575 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.fileselect;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.keepassdroid.AssignMasterKeyDialog;
|
||||
import com.keepassdroid.CreateFileDialog;
|
||||
import com.keepassdroid.GroupActivity;
|
||||
import com.keepassdroid.PasswordActivity;
|
||||
import com.keepassdroid.ProgressTask;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.compat.ContentResolverCompat;
|
||||
import com.keepassdroid.compat.StorageAF;
|
||||
import com.keepassdroid.database.edit.CreateDB;
|
||||
import com.keepassdroid.database.edit.FileOnFinish;
|
||||
import com.keepassdroid.database.exception.ContentFileNotFoundException;
|
||||
import com.keepassdroid.intents.Intents;
|
||||
import com.keepassdroid.stylish.StylishActivity;
|
||||
import com.keepassdroid.utils.EmptyUtils;
|
||||
import com.keepassdroid.utils.Interaction;
|
||||
import com.keepassdroid.utils.MenuUtil;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
import com.keepassdroid.utils.Util;
|
||||
import com.keepassdroid.view.AssignPasswordHelper;
|
||||
import com.keepassdroid.view.FileNameView;
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
public class FileSelectActivity extends StylishActivity implements
|
||||
CreateFileDialog.DefinePathDialogListener ,
|
||||
AssignMasterKeyDialog.AssignPasswordDialogListener,
|
||||
FileSelectAdapter.FileSelectClearListener,
|
||||
FileSelectAdapter.FileInformationShowListener {
|
||||
|
||||
private static final String TAG = "FileSelectActivity";
|
||||
|
||||
private static final int MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE = 111;
|
||||
private RecyclerView mListFiles;
|
||||
private FileSelectAdapter mAdapter;
|
||||
private View fileListTitle;
|
||||
|
||||
public static final int FILE_BROWSE = 1;
|
||||
public static final int GET_CONTENT = 2;
|
||||
public static final int OPEN_DOC = 3;
|
||||
|
||||
private RecentFileHistory fileHistory;
|
||||
|
||||
private boolean recentMode = false;
|
||||
|
||||
private EditText openFileNameView;
|
||||
|
||||
private AssignPasswordHelper assignPasswordHelper;
|
||||
private Uri databaseUri;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
fileHistory = App.getFileHistory();
|
||||
|
||||
setContentView(R.layout.file_selection);
|
||||
fileListTitle = findViewById(R.id.file_list_title);
|
||||
if (fileHistory.hasRecentFiles()) {
|
||||
recentMode = true;
|
||||
}
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
toolbar.setTitle(getString(R.string.app_name));
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
mListFiles = (RecyclerView) findViewById(R.id.file_list);
|
||||
mListFiles.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
||||
// Open button
|
||||
View openButton = findViewById(R.id.open_database);
|
||||
openButton.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
String fileName = Util.getEditText(FileSelectActivity.this,
|
||||
R.id.file_filename);
|
||||
try {
|
||||
PasswordActivity.Launch(FileSelectActivity.this, fileName);
|
||||
}
|
||||
catch (ContentFileNotFoundException e) {
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.file_not_found_content, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.file_not_found, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create button
|
||||
View createButton = findViewById(R.id.create_database);
|
||||
createButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
CreateFileDialog createFileDialog = new CreateFileDialog();
|
||||
createFileDialog.show(getSupportFragmentManager(), "createFileDialog");
|
||||
}
|
||||
});
|
||||
|
||||
View browseButton = findViewById(R.id.browse_button);
|
||||
browseButton.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
if (StorageAF.useStorageFramework(FileSelectActivity.this)) {
|
||||
Intent i = new Intent(StorageAF.ACTION_OPEN_DOCUMENT);
|
||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
i.setType("*/*");
|
||||
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION|
|
||||
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
startActivityForResult(i, OPEN_DOC);
|
||||
}
|
||||
else {
|
||||
Intent i;
|
||||
i = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
i.setType("*/*");
|
||||
|
||||
try {
|
||||
startActivityForResult(i, GET_CONTENT);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
lookForOpenIntentsFilePicker();
|
||||
} catch (SecurityException e) {
|
||||
lookForOpenIntentsFilePicker();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void lookForOpenIntentsFilePicker() {
|
||||
if (Interaction.isIntentAvailable(FileSelectActivity.this, Intents.OPEN_INTENTS_FILE_BROWSE)) {
|
||||
Intent i = new Intent(Intents.OPEN_INTENTS_FILE_BROWSE);
|
||||
i.setData(Uri.parse("file://" + Util.getEditText(FileSelectActivity.this, R.id.file_filename)));
|
||||
try {
|
||||
startActivityForResult(i, FILE_BROWSE);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
showBrowserDialog();
|
||||
}
|
||||
} else {
|
||||
showBrowserDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private void showBrowserDialog() {
|
||||
BrowserDialog diag = new BrowserDialog(FileSelectActivity.this);
|
||||
diag.show();
|
||||
}
|
||||
});
|
||||
|
||||
// Set the initial value of the filename
|
||||
openFileNameView = (EditText) findViewById(R.id.file_filename);
|
||||
String defaultPath = Environment.getExternalStorageDirectory().getAbsolutePath()
|
||||
+ getString(R.string.database_file_path_default)
|
||||
+ getString(R.string.database_file_name_default)
|
||||
+ getString(R.string.database_file_extension_default);
|
||||
openFileNameView.setText(defaultPath);
|
||||
|
||||
fillData();
|
||||
|
||||
// Load default database
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
String fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, "");
|
||||
|
||||
if (fileName.length() > 0) {
|
||||
Uri dbUri = UriUtil.parseDefaultFile(fileName);
|
||||
String scheme = null;
|
||||
if (dbUri!=null)
|
||||
scheme = dbUri.getScheme();
|
||||
|
||||
if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
|
||||
String path = dbUri.getPath();
|
||||
File db = new File(path);
|
||||
|
||||
if (db.exists()) {
|
||||
try {
|
||||
PasswordActivity.Launch(FileSelectActivity.this, path);
|
||||
} catch (Exception e) {
|
||||
// Ignore exception
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
PasswordActivity.Launch(FileSelectActivity.this, dbUri.toString());
|
||||
} catch (Exception e) {
|
||||
// Ignore exception
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTitleFileListView() {
|
||||
if(mAdapter.getItemCount() == 0)
|
||||
fileListTitle.setVisibility(View.INVISIBLE);
|
||||
else
|
||||
fileListTitle.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create file for database
|
||||
* @return If not created, return false
|
||||
*/
|
||||
private boolean createDatabaseFile(Uri path) {
|
||||
|
||||
String pathString = URLDecoder.decode(path.getPath());
|
||||
// Make sure file name exists
|
||||
if (pathString.length() == 0) {
|
||||
Log.e(TAG, getString(R.string.error_filename_required));
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.error_filename_required,
|
||||
Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to create the file
|
||||
File file = new File(pathString);
|
||||
try {
|
||||
if (file.exists()) {
|
||||
Log.e(TAG, getString(R.string.error_database_exists) + " " + file);
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.error_database_exists,
|
||||
Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
File parent = file.getParentFile();
|
||||
|
||||
if ( parent == null || (parent.exists() && ! parent.isDirectory()) ) {
|
||||
Log.e(TAG, getString(R.string.error_invalid_path) + " " + file);
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.error_invalid_path,
|
||||
Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! parent.exists() ) {
|
||||
// Create parent directory
|
||||
if ( ! parent.mkdirs() ) {
|
||||
Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + parent);
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.error_could_not_create_parent,
|
||||
Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + e.getLocalizedMessage());
|
||||
e.printStackTrace();
|
||||
Toast.makeText(
|
||||
FileSelectActivity.this,
|
||||
getText(R.string.error_file_not_create) + " "
|
||||
+ e.getLocalizedMessage(),
|
||||
Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDefinePathDialogPositiveClick(Uri pathFile) {
|
||||
databaseUri = pathFile;
|
||||
if(createDatabaseFile(pathFile)) {
|
||||
AssignMasterKeyDialog assignMasterKeyDialog = new AssignMasterKeyDialog();
|
||||
assignMasterKeyDialog.show(getSupportFragmentManager(), "passwordDialog");
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDefinePathDialogNegativeClick(Uri pathFile) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssignKeyDialogPositiveClick(
|
||||
boolean masterPasswordChecked, String masterPassword,
|
||||
boolean keyFileChecked, Uri keyFile) {
|
||||
|
||||
String databaseFilename = databaseUri.getPath();
|
||||
|
||||
// Prep an object to collect a password once the database has
|
||||
// been created
|
||||
FileOnFinish launchActivityOnFinish = new FileOnFinish(
|
||||
new LaunchGroupActivity(databaseFilename));
|
||||
AssignPasswordOnFinish assignPasswordOnFinish =
|
||||
new AssignPasswordOnFinish(launchActivityOnFinish);
|
||||
|
||||
// Create the new database
|
||||
CreateDB create = new CreateDB(FileSelectActivity.this,
|
||||
databaseFilename, assignPasswordOnFinish, true);
|
||||
|
||||
ProgressTask createTask = new ProgressTask(
|
||||
FileSelectActivity.this, create,
|
||||
R.string.progress_create);
|
||||
createTask.run();
|
||||
assignPasswordHelper =
|
||||
new AssignPasswordHelper(this,
|
||||
masterPassword, keyFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssignKeyDialogNegativeClick(
|
||||
boolean masterPasswordChecked, String masterPassword,
|
||||
boolean keyFileChecked, Uri keyFile) {
|
||||
|
||||
}
|
||||
|
||||
private class AssignPasswordOnFinish extends FileOnFinish {
|
||||
|
||||
AssignPasswordOnFinish(FileOnFinish fileOnFinish) {
|
||||
super(fileOnFinish);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mSuccess) {
|
||||
assignPasswordHelper.assignPasswordInDatabase(mOnFinish);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LaunchGroupActivity extends FileOnFinish {
|
||||
private Uri mUri;
|
||||
|
||||
LaunchGroupActivity(String filename) {
|
||||
super(null);
|
||||
mUri = UriUtil.parseDefaultFile(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mSuccess) {
|
||||
// Add to recent files
|
||||
fileHistory.createFile(mUri, getFilename());
|
||||
mAdapter.notifyDataSetChanged();
|
||||
updateTitleFileListView();
|
||||
GroupActivity.Launch(FileSelectActivity.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fillData() {
|
||||
mAdapter = new FileSelectAdapter(FileSelectActivity.this, fileHistory.getDbList());
|
||||
mAdapter.setOnItemClickListener(
|
||||
new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
int itemPosition = mListFiles.getChildLayoutPosition(view);
|
||||
new OpenFileHistoryAsyncTask(new OpenFileHistoryAsyncTask.AfterOpenFileHistoryListener() {
|
||||
@Override
|
||||
public void afterOpenFile(String fileName, String keyFile) {
|
||||
try {
|
||||
PasswordActivity.Launch(FileSelectActivity.this,
|
||||
fileName, keyFile);
|
||||
} catch (ContentFileNotFoundException e) {
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.file_not_found_content, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
} catch (FileNotFoundException e) {
|
||||
Toast.makeText(FileSelectActivity.this,
|
||||
R.string.file_not_found, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
updateTitleFileListView();
|
||||
}
|
||||
}, fileHistory).execute(itemPosition);
|
||||
}
|
||||
}
|
||||
);
|
||||
mAdapter.setFileSelectClearListener(this);
|
||||
mAdapter.setFileInformationShowListener(this);
|
||||
mListFiles.setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickFileInformation(FileSelectBean fileSelectBean) {
|
||||
if (fileSelectBean != null) {
|
||||
FileInformationDialogFragment fileInformationDialogFragment =
|
||||
FileInformationDialogFragment.newInstance(fileSelectBean);
|
||||
fileInformationDialogFragment.show(getSupportFragmentManager(), "fileInformation");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFileSelectClearListener(final FileSelectBean fileSelectBean) {
|
||||
new DeleteFileHistoryAsyncTask(new DeleteFileHistoryAsyncTask.AfterDeleteFileHistoryListener() {
|
||||
@Override
|
||||
public void afterDeleteFile() {
|
||||
fileHistory.deleteFile(fileSelectBean.getFileUri());
|
||||
mAdapter.notifyDataSetChanged();
|
||||
updateTitleFileListView();
|
||||
}
|
||||
}, fileHistory, mAdapter).execute(fileSelectBean);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
fillData();
|
||||
|
||||
String filename = null;
|
||||
if (requestCode == FILE_BROWSE && resultCode == RESULT_OK) {
|
||||
filename = data.getDataString();
|
||||
if (filename != null) {
|
||||
if (filename.startsWith("file://")) {
|
||||
filename = filename.substring(7);
|
||||
}
|
||||
|
||||
filename = URLDecoder.decode(filename);
|
||||
}
|
||||
|
||||
}
|
||||
else if ((requestCode == GET_CONTENT || requestCode == OPEN_DOC) && resultCode == RESULT_OK) {
|
||||
if (data != null) {
|
||||
Uri uri = data.getData();
|
||||
if (uri != null) {
|
||||
if (StorageAF.useStorageFramework(this)) {
|
||||
try {
|
||||
// try to persist read and write permissions
|
||||
ContentResolver resolver = getContentResolver();
|
||||
ContentResolverCompat.takePersistableUriPermission(resolver, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
ContentResolverCompat.takePersistableUriPermission(resolver, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
} catch (Exception e) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
if (requestCode == GET_CONTENT) {
|
||||
uri = UriUtil.translate(this, uri);
|
||||
}
|
||||
filename = uri.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filename != null) {
|
||||
openFileNameView.setText(filename);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// check for storage permission
|
||||
checkStoragePermission();
|
||||
|
||||
// Check to see if we need to change modes
|
||||
if ( fileHistory.hasRecentFiles() != recentMode ) {
|
||||
// Restart the activity
|
||||
Intent intent = getIntent();
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
FileNameView fnv = (FileNameView) findViewById(R.id.file_select);
|
||||
fnv.updateExternalStorageWarning();
|
||||
|
||||
updateTitleFileListView();
|
||||
}
|
||||
|
||||
private void checkStoragePermission() {
|
||||
// Here, thisActivity is the current activity
|
||||
if (ContextCompat.checkSelfPermission(FileSelectActivity.this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
// Should we show an explanation?
|
||||
//if (ActivityCompat.shouldShowRequestPermissionRationale(FileSelectActivity.this,
|
||||
// Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
|
||||
// Show an explanation to the user *asynchronously* -- don't block
|
||||
// this thread waiting for the user's response! After the user
|
||||
// sees the explanation, try again to request the permission.
|
||||
|
||||
//} else {
|
||||
|
||||
// No explanation needed, we can request the permission.
|
||||
|
||||
ActivityCompat.requestPermissions(FileSelectActivity.this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE);
|
||||
|
||||
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
|
||||
// app-defined int constant. The callback method gets the
|
||||
// result of the request.
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
String permissions[], int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
if (grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
// permission was granted, yay! Do the
|
||||
// contacts-related task you need to do.
|
||||
|
||||
} else {
|
||||
|
||||
// permission denied, boo! Disable the
|
||||
// functionality that depends on this permission.
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// other 'case' lines to check for other
|
||||
// permissions this app might request
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
MenuUtil.defaultMenuInflater(getMenuInflater(), menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
return MenuUtil.onDefaultMenuOptionsItemSelected(this, item)
|
||||
&& super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.settings;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v14.preference.SwitchPreference;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.UnavailableFeatureDialog;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwEncryptionAlgorithm;
|
||||
import com.keepassdroid.fingerprint.FingerPrintHelper;
|
||||
import com.keepassdroid.stylish.Stylish;
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
public class NestedSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
public static final int NESTED_SCREEN_APP_KEY = 1;
|
||||
public static final int NESTED_SCREEN_DB_KEY = 2;
|
||||
|
||||
private static final String TAG_KEY = "NESTED_KEY";
|
||||
|
||||
public static NestedSettingsFragment newInstance(int key) {
|
||||
NestedSettingsFragment fragment = new NestedSettingsFragment();
|
||||
// supply arguments to bundle.
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(TAG_KEY, key);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
int key = getArguments().getInt(TAG_KEY);
|
||||
// Load the preferences from an XML resource
|
||||
switch (key) {
|
||||
case NESTED_SCREEN_APP_KEY:
|
||||
setPreferencesFromResource(R.xml.app_preferences, rootKey);
|
||||
|
||||
Preference keyFile = findPreference(getString(R.string.keyfile_key));
|
||||
keyFile.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
Boolean value = (Boolean) newValue;
|
||||
|
||||
if (!value) {
|
||||
App.getFileHistory().deleteAllKeys();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Preference recentHistory = findPreference(getString(R.string.recentfile_key));
|
||||
recentHistory.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
Boolean value = (Boolean) newValue;
|
||||
|
||||
if (value == null) {
|
||||
value = true;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
App.getFileHistory().deleteAll();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Preference stylePreference = findPreference(getString(R.string.setting_style_key));
|
||||
stylePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String styleString = (String) newValue;
|
||||
Stylish.assignStyle(getActivity(), styleString);
|
||||
getActivity().recreate();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
SwitchPreference fingerprintEnablePreference = (SwitchPreference) findPreference(getString(R.string.fingerprint_enable_key));
|
||||
if (!FingerPrintHelper.isFingerprintSupported(FingerprintManagerCompat.from(getContext()))) {
|
||||
// False if under Marshmallow
|
||||
fingerprintEnablePreference.setDefaultValue(false);
|
||||
fingerprintEnablePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
((SwitchPreference) preference).setChecked(false);
|
||||
UnavailableFeatureDialog.getInstance(Build.VERSION_CODES.M)
|
||||
.show(getFragmentManager(), "unavailableFeatureDialog");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Preference deleteKeysFingerprints = findPreference(getString(R.string.fingerprint_delete_all_key));
|
||||
deleteKeysFingerprints.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setMessage(getResources().getString(R.string.fingerprint_delete_all_warning))
|
||||
.setIcon(getResources().getDrawable(
|
||||
android.R.drawable.ic_dialog_alert))
|
||||
.setPositiveButton(
|
||||
getResources().getString(android.R.string.yes),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
FingerPrintHelper.deleteEntryKeyInKeystoreForFingerprints(
|
||||
getContext(),
|
||||
new FingerPrintHelper.FingerPrintErrorCallback() {
|
||||
@Override
|
||||
public void onInvalidKeyException(Exception e) {}
|
||||
|
||||
@Override
|
||||
public void onFingerPrintException(Exception e) {
|
||||
Toast.makeText(getContext(), R.string.fingerprint_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
PrefsUtil.deleteAllValuesFromNoBackupPreferences(getContext());
|
||||
}
|
||||
})
|
||||
.setNegativeButton(
|
||||
getResources().getString(android.R.string.no),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
}
|
||||
}).show();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case NESTED_SCREEN_DB_KEY:
|
||||
setPreferencesFromResource(R.xml.db_preferences, rootKey);
|
||||
|
||||
Database db = App.getDB();
|
||||
Preference algorithmPref = findPreference(getString(R.string.algorithm_key));
|
||||
Preference roundPref = findPreference(getString(R.string.rounds_key));
|
||||
|
||||
if (!(db.Loaded() && db.pm.appSettingsEnabled())) {
|
||||
algorithmPref.setEnabled(false);
|
||||
roundPref.setEnabled(false);
|
||||
}
|
||||
|
||||
if (db.Loaded() && db.pm.appSettingsEnabled()) {
|
||||
roundPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
setRounds(App.getDB(), preference);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
setRounds(db, roundPref);
|
||||
setAlgorithm(db, algorithmPref);
|
||||
} else {
|
||||
Log.e(getClass().getName(), "Database isn't ready");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
// Try if the preference is one of our custom Preferences
|
||||
if (preference instanceof RoundsPreference) {
|
||||
DialogFragment dialogFragment = RoundsPreferenceDialogFragmentCompat.newInstance(preference.getKey());
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(getFragmentManager(), null);
|
||||
}
|
||||
// Could not be handled here. Try with the super method.
|
||||
else {
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
}
|
||||
|
||||
private void setRounds(Database db, Preference rounds) {
|
||||
rounds.setSummary(Long.toString(db.pm.getNumRounds()));
|
||||
}
|
||||
|
||||
private void setAlgorithm(Database db, Preference algorithm) {
|
||||
int resId;
|
||||
if ( db.pm.getEncAlgorithm() == PwEncryptionAlgorithm.Rjindal ) {
|
||||
resId = R.string.rijndael;
|
||||
} else {
|
||||
resId = R.string.twofish;
|
||||
}
|
||||
|
||||
algorithm.setSummary(resId);
|
||||
}
|
||||
|
||||
public static String retrieveTitle(Resources resources, int key) {
|
||||
switch (key) {
|
||||
case NESTED_SCREEN_APP_KEY:
|
||||
return resources.getString(R.string.menu_app_settings);
|
||||
case NESTED_SCREEN_DB_KEY:
|
||||
return resources.getString(R.string.menu_db_settings);
|
||||
default:
|
||||
return resources.getString(R.string.settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package com.keepassdroid.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v7.preference.DialogPreference;
|
||||
import android.support.v7.preference.PreferenceDialogFragmentCompat;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.keepassdroid.Database;
|
||||
import com.keepassdroid.ProgressTask;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.edit.OnFinish;
|
||||
import com.keepassdroid.database.edit.SaveDB;
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
public class RoundsPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
|
||||
|
||||
private PwDatabase mPM;
|
||||
private TextView mRoundsView;
|
||||
|
||||
public static RoundsPreferenceDialogFragmentCompat newInstance(
|
||||
String key) {
|
||||
final RoundsPreferenceDialogFragmentCompat
|
||||
fragment = new RoundsPreferenceDialogFragmentCompat();
|
||||
final Bundle b = new Bundle(1);
|
||||
b.putString(ARG_KEY, key);
|
||||
fragment.setArguments(b);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogClosed(boolean positiveResult) {
|
||||
if ( positiveResult ) {
|
||||
long rounds;
|
||||
|
||||
try {
|
||||
String strRounds = mRoundsView.getText().toString();
|
||||
rounds = Long.parseLong(strRounds);
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(getContext(), R.string.error_rounds_not_number, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( rounds < 1 ) {
|
||||
rounds = 1;
|
||||
}
|
||||
|
||||
long oldRounds = mPM.getNumRounds();
|
||||
try {
|
||||
mPM.setNumRounds(rounds);
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(getContext(), R.string.error_rounds_too_large, Toast.LENGTH_LONG).show();
|
||||
mPM.setNumRounds(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
Handler handler = new Handler();
|
||||
SaveDB save = new SaveDB(getContext(), App.getDB(), new AfterSave(getContext(), handler, oldRounds));
|
||||
ProgressTask pt = new ProgressTask(getContext(), save, R.string.saving_database);
|
||||
pt.run();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindDialogView(View view) {
|
||||
super.onBindDialogView(view);
|
||||
|
||||
mRoundsView = (TextView) view.findViewById(R.id.rounds);
|
||||
|
||||
// Get the time from the related Preference
|
||||
Database db = App.getDB();
|
||||
mPM = db.pm;
|
||||
long numRounds = mPM.getNumRounds();
|
||||
|
||||
DialogPreference preference = getPreference();
|
||||
if (preference instanceof RoundsPreference) {
|
||||
numRounds = ((RoundsPreference) preference).getRounds();
|
||||
}
|
||||
|
||||
mRoundsView.setText(String.valueOf(numRounds));
|
||||
}
|
||||
|
||||
private class AfterSave extends OnFinish {
|
||||
private long mOldRounds;
|
||||
private Context mCtx;
|
||||
|
||||
public AfterSave(Context ctx, Handler handler, long oldRounds) {
|
||||
super(handler);
|
||||
|
||||
mCtx = ctx;
|
||||
mOldRounds = oldRounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if ( mSuccess ) {
|
||||
|
||||
} else {
|
||||
displayMessage(mCtx);
|
||||
mPM.setNumRounds(mOldRounds);
|
||||
}
|
||||
|
||||
super.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.keepassdroid.stylish;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StyleRes;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
|
||||
public abstract class StylishActivity extends AppCompatActivity {
|
||||
|
||||
private @StyleRes int themeId;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
this.themeId = Stylish.getThemeId(this);
|
||||
setTheme(themeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if(Stylish.getThemeId(this) != this.themeId) {
|
||||
Log.d(this.getClass().getName(), "Theme change detected, restarting activity");
|
||||
this.recreate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.utils;
|
||||
|
||||
import com.keepassdroid.database.PwDatabase;
|
||||
import com.keepassdroid.database.PwDatabaseV4;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
|
||||
public class SprEngine {
|
||||
|
||||
private static SprEngineV4 sprV4 = new SprEngineV4();
|
||||
private static SprEngine spr = new SprEngine();
|
||||
|
||||
public static SprEngine getInstance(PwDatabase db) {
|
||||
if (db instanceof PwDatabaseV4) {
|
||||
return sprV4;
|
||||
}
|
||||
else {
|
||||
return spr;
|
||||
}
|
||||
}
|
||||
|
||||
public String compile(String text, PwEntry entry, PwDatabase database) {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.text.ClipboardManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.keepassdroid.database.exception.SamsungClipboardException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class Util {
|
||||
public static String getClipboard(Context context) {
|
||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
CharSequence csText = clipboard.getText();
|
||||
if (csText == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return csText.toString();
|
||||
}
|
||||
|
||||
public static void copyToClipboard(Context context, String text) throws SamsungClipboardException {
|
||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
||||
try {
|
||||
clipboard.setText(text);
|
||||
} catch (NullPointerException e) {
|
||||
throw new SamsungClipboardException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void gotoUrl(Context context, String url) throws ActivityNotFoundException {
|
||||
if ( url != null && url.length() > 0 ) {
|
||||
Uri uri = Uri.parse(url);
|
||||
context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
|
||||
}
|
||||
}
|
||||
|
||||
public static void gotoUrl(Context context, int resId) throws ActivityNotFoundException {
|
||||
gotoUrl(context, context.getString(resId));
|
||||
}
|
||||
|
||||
public static String getEditText(Activity act, int resId) {
|
||||
TextView te = (TextView) act.findViewById(resId);
|
||||
|
||||
if (te != null) {
|
||||
return te.getText().toString();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyStream(InputStream in, OutputStream out) throws IOException {
|
||||
byte[] buf = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, read);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.keepassdroid.app.App;
|
||||
|
||||
public abstract class ClickView extends LinearLayout {
|
||||
protected boolean readOnly = false;
|
||||
|
||||
public ClickView(Context context) {
|
||||
super(context);
|
||||
|
||||
readOnly = App.getDB().readOnly;
|
||||
}
|
||||
|
||||
abstract public void onClick();
|
||||
|
||||
abstract public void onCreateMenu(ContextMenu menu, ContextMenuInfo menuInfo);
|
||||
|
||||
abstract public boolean onContextItemSelected(MenuItem item);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
public class EntryContentsView extends LinearLayout {
|
||||
|
||||
public EntryContentsView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EntryContentsView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
inflate(context);
|
||||
}
|
||||
|
||||
private void inflate(Context context) {
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.entry_view_contents, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LayoutParams generateDefaultLayoutParams() {
|
||||
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.database.security.ProtectedString;
|
||||
|
||||
public class EntryEditSection extends RelativeLayout {
|
||||
|
||||
public EntryEditSection(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public EntryEditSection(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public EntryEditSection(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
public void setData(String title, ProtectedString value) {
|
||||
setText(R.id.title, title);
|
||||
setText(R.id.value, value.toString());
|
||||
|
||||
CheckBox cb = (CheckBox) findViewById(R.id.protection);
|
||||
cb.setChecked(value.isProtected());
|
||||
}
|
||||
|
||||
private void setText(int resId, String str) {
|
||||
if (str != null) {
|
||||
TextView tvTitle = (TextView) findViewById(resId);
|
||||
tvTitle.setText(str);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
public class EntrySection extends LinearLayout {
|
||||
|
||||
public EntrySection(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EntrySection(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, null, null);
|
||||
}
|
||||
|
||||
public EntrySection(Context context, AttributeSet attrs, String title, String value) {
|
||||
super(context, attrs);
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflate(inflater, context, title, value);
|
||||
}
|
||||
|
||||
protected int getLayout() {
|
||||
return R.layout.entry_section;
|
||||
}
|
||||
|
||||
protected void inflate(LayoutInflater inflater, Context context, String title, String value) {
|
||||
inflater.inflate(getLayout(), this);
|
||||
|
||||
setText(R.id.title, title);
|
||||
setText(R.id.value, value);
|
||||
}
|
||||
|
||||
private void setText(int resId, String str) {
|
||||
if (str != null) {
|
||||
TextView tvTitle = (TextView) findViewById(resId);
|
||||
tvTitle.setText(str);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.RotateAnimation;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
|
||||
public class GroupAddEntryView extends RelativeLayout {
|
||||
|
||||
protected View addButton;
|
||||
|
||||
protected View addEntry;
|
||||
protected boolean addEntryActivated;
|
||||
protected View addGroup;
|
||||
protected boolean addGroupActivated;
|
||||
|
||||
|
||||
private boolean animInProgress;
|
||||
private AddButtonAnimation viewButtonMenuAnimation;
|
||||
private ViewMenuAnimation viewMenuAnimationAddEntry;
|
||||
private ViewMenuAnimation viewMenuAnimationAddGroup;
|
||||
|
||||
public GroupAddEntryView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public GroupAddEntryView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
inflate(context);
|
||||
}
|
||||
|
||||
protected void inflate(Context context) {
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.group_add_entry, this);
|
||||
|
||||
addEntryActivated = true;
|
||||
addGroupActivated = true;
|
||||
|
||||
addButton = findViewById(R.id.add_button);
|
||||
addEntry = findViewById(R.id.add_entry);
|
||||
addGroup = findViewById(R.id.add_group);
|
||||
|
||||
viewButtonMenuAnimation = new AddButtonAnimation(addButton);
|
||||
viewMenuAnimationAddEntry = new ViewMenuAnimation(addEntry);
|
||||
viewMenuAnimationAddGroup = new ViewMenuAnimation(addGroup);
|
||||
|
||||
addButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if(!animInProgress) {
|
||||
viewButtonMenuAnimation.startAnimation();
|
||||
if (addEntryActivated) {
|
||||
viewMenuAnimationAddEntry.startAnimation();
|
||||
}
|
||||
if (addGroupActivated) {
|
||||
viewMenuAnimationAddGroup.startAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class AddButtonAnimation implements Animation.AnimationListener {
|
||||
|
||||
private View view;
|
||||
|
||||
private boolean isRotate;
|
||||
|
||||
private Animation rightAnim;
|
||||
private Animation leftAnim;
|
||||
|
||||
AddButtonAnimation(View view) {
|
||||
this.view = view;
|
||||
|
||||
this.isRotate = false;
|
||||
|
||||
rightAnim = new RotateAnimation(0f, 135f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||
rightAnim.setDuration(300);
|
||||
rightAnim.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
rightAnim.setFillAfter(true);
|
||||
leftAnim = new RotateAnimation(135f, 0f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||
leftAnim.setDuration(300);
|
||||
leftAnim.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
leftAnim.setFillAfter(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
animInProgress = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
animInProgress = false;
|
||||
isRotate = !isRotate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
|
||||
void startAnimation() {
|
||||
Animation animation;
|
||||
if(!isRotate)
|
||||
animation = rightAnim;
|
||||
else
|
||||
animation = leftAnim;
|
||||
animation.setAnimationListener(this);
|
||||
view.startAnimation(animation);
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewMenuAnimation implements Animation.AnimationListener {
|
||||
|
||||
private View view;
|
||||
|
||||
private Animation animViewShow;
|
||||
private Animation animViewHide;
|
||||
|
||||
ViewMenuAnimation(View view) {
|
||||
this.view = view;
|
||||
|
||||
animViewShow = new AlphaAnimation(0.0f, 1.0f);
|
||||
animViewShow.setDuration(300);
|
||||
|
||||
animViewHide = new AlphaAnimation(1.0f, 0.0f);
|
||||
animViewHide.setDuration(300);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
if(view.getVisibility() == VISIBLE)
|
||||
view.setVisibility(GONE);
|
||||
else
|
||||
view.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
|
||||
void startAnimation() {
|
||||
Animation animation;
|
||||
if(view.getVisibility() == VISIBLE)
|
||||
animation = animViewHide;
|
||||
else
|
||||
animation = animViewShow;
|
||||
animation.setAnimationListener(this);
|
||||
view.startAnimation(animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class GroupViewOnlyView extends GroupAddEntryView {
|
||||
|
||||
public GroupViewOnlyView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public GroupViewOnlyView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
inflate(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inflate(Context context) {
|
||||
super.inflate(context);
|
||||
|
||||
// Hide the buttons
|
||||
addButton.setVisibility(GONE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.View;
|
||||
|
||||
import com.keepassdroid.compat.StorageAF;
|
||||
import com.keepassdroid.fileselect.BrowserDialog;
|
||||
import com.keepassdroid.intents.Intents;
|
||||
import com.keepassdroid.utils.Interaction;
|
||||
import com.keepassdroid.utils.UriUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
|
||||
|
||||
public class KeyFileHelper {
|
||||
|
||||
public static final int GET_CONTENT = 25745;
|
||||
private static final int OPEN_DOC = 25845;
|
||||
private static final int FILE_BROWSE = 25645;
|
||||
|
||||
private Activity activity;
|
||||
private Fragment fragment;
|
||||
private Uri mDbUri;
|
||||
|
||||
public KeyFileHelper(Activity context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public KeyFileHelper(Activity context, Uri mDbUri) {
|
||||
this.activity = context;
|
||||
this.fragment = null;
|
||||
this.mDbUri = mDbUri;
|
||||
}
|
||||
|
||||
public KeyFileHelper(Fragment context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public KeyFileHelper(Fragment context, Uri mDbUri) {
|
||||
this.activity = context.getActivity();
|
||||
this.fragment = context;
|
||||
this.mDbUri = mDbUri;
|
||||
}
|
||||
|
||||
public View.OnClickListener getOpenFileOnClickViewListener() {
|
||||
return new View.OnClickListener() {
|
||||
|
||||
public void onClick(View v) {
|
||||
if (StorageAF.useStorageFramework(activity)) {
|
||||
Intent i = new Intent(StorageAF.ACTION_OPEN_DOCUMENT);
|
||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
i.setType("*/*");
|
||||
if(fragment != null)
|
||||
fragment.startActivityForResult(i, OPEN_DOC);
|
||||
else
|
||||
activity.startActivityForResult(i, OPEN_DOC);
|
||||
} else {
|
||||
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
i.setType("*/*");
|
||||
|
||||
try {
|
||||
if(fragment != null)
|
||||
fragment.startActivityForResult(i, GET_CONTENT);
|
||||
else
|
||||
activity.startActivityForResult(i, GET_CONTENT);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
lookForOpenIntentsFilePicker();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void lookForOpenIntentsFilePicker() {
|
||||
if (Interaction.isIntentAvailable(activity, Intents.OPEN_INTENTS_FILE_BROWSE)) {
|
||||
Intent i = new Intent(Intents.OPEN_INTENTS_FILE_BROWSE);
|
||||
|
||||
// Get file path parent if possible
|
||||
try {
|
||||
if (mDbUri != null && mDbUri.toString().length() > 0) {
|
||||
if (mDbUri.getScheme().equals("file")) {
|
||||
File keyfile = new File(mDbUri.getPath());
|
||||
File parent = keyfile.getParentFile();
|
||||
if (parent != null) {
|
||||
i.setData(Uri.parse("file://" + parent.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
if(fragment != null)
|
||||
fragment.startActivityForResult(i, FILE_BROWSE);
|
||||
else
|
||||
activity.startActivityForResult(i, FILE_BROWSE);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
showBrowserDialog();
|
||||
}
|
||||
} else {
|
||||
showBrowserDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private void showBrowserDialog() {
|
||||
BrowserDialog browserDialog = new BrowserDialog(activity);
|
||||
browserDialog.show();
|
||||
}
|
||||
|
||||
|
||||
public void onActivityResultCallback(
|
||||
int requestCode,
|
||||
int resultCode,
|
||||
Intent data,
|
||||
KeyFileCallback keyFileCallback) {
|
||||
|
||||
switch (requestCode) {
|
||||
case FILE_BROWSE:
|
||||
if (resultCode == RESULT_OK) {
|
||||
String filename = data.getDataString();
|
||||
Uri keyUri = null;
|
||||
if (filename != null) {
|
||||
keyUri = UriUtil.parseDefaultFile(filename);
|
||||
}
|
||||
if (keyFileCallback != null)
|
||||
keyFileCallback.onKeyFileResultCallback(keyUri);
|
||||
}
|
||||
break;
|
||||
case GET_CONTENT:
|
||||
case OPEN_DOC:
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (data != null) {
|
||||
Uri uri = data.getData();
|
||||
if (uri != null) {
|
||||
if (requestCode == GET_CONTENT) {
|
||||
uri = UriUtil.translate(activity, uri);
|
||||
}
|
||||
if (keyFileCallback != null)
|
||||
keyFileCallback.onKeyFileResultCallback(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public interface KeyFileCallback {
|
||||
void onKeyFileResultCallback(Uri uri);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
public class NoFocusScrollView extends ScrollView {
|
||||
|
||||
public NoFocusScrollView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
public NoFocusScrollView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public NoFocusScrollView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<View> getFocusables(int direction) {
|
||||
return new ArrayList<View>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
|
||||
import android.os.Handler;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.EntryActivity;
|
||||
import com.keepassdroid.GroupBaseActivity;
|
||||
import com.keepassdroid.ProgressTask;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwEntry;
|
||||
import com.keepassdroid.database.edit.DeleteEntry;
|
||||
import com.keepassdroid.settings.PrefsUtil;
|
||||
|
||||
public class PwEntryView extends ClickView {
|
||||
|
||||
protected GroupBaseActivity mAct;
|
||||
protected PwEntry mPw;
|
||||
private TextView mTv;
|
||||
private int mPos;
|
||||
|
||||
protected static final int MENU_OPEN = Menu.FIRST;
|
||||
private static final int MENU_DELETE = MENU_OPEN + 1;
|
||||
|
||||
public static PwEntryView getInstance(GroupBaseActivity act, PwEntry pw, int pos) {
|
||||
return new PwEntryView(act, pw, pos);
|
||||
}
|
||||
|
||||
protected PwEntryView(GroupBaseActivity act, PwEntry pw, int pos) {
|
||||
super(act);
|
||||
mAct = act;
|
||||
|
||||
View ev = View.inflate(mAct, R.layout.list_entries_entry, null);
|
||||
mTv = (TextView) ev.findViewById(R.id.entry_text);
|
||||
mTv.setTextSize(PrefsUtil.getListTextSize(act));
|
||||
|
||||
populateView(ev, pw, pos);
|
||||
|
||||
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
|
||||
addView(ev, lp);
|
||||
|
||||
}
|
||||
|
||||
private void populateView(View ev, PwEntry pw, int pos) {
|
||||
mPw = pw;
|
||||
mPos = pos;
|
||||
|
||||
ImageView iv = (ImageView) ev.findViewById(R.id.entry_icon);
|
||||
App.getDB().drawFactory.assignDrawableTo(iv, getResources(), pw.getIcon());
|
||||
|
||||
mTv.setText(mPw.getDisplayTitle());
|
||||
}
|
||||
|
||||
public void convertView(PwEntry pw, int pos) {
|
||||
populateView(this, pw, pos);
|
||||
}
|
||||
|
||||
public void refreshTitle() {
|
||||
mTv.setText(mPw.getDisplayTitle());
|
||||
}
|
||||
|
||||
public void onClick() {
|
||||
launchEntry();
|
||||
}
|
||||
|
||||
private void launchEntry() {
|
||||
EntryActivity.Launch(mAct, mPw, mPos);
|
||||
|
||||
}
|
||||
|
||||
private void deleteEntry() {
|
||||
Handler handler = new Handler();
|
||||
DeleteEntry task = new DeleteEntry(mAct, App.getDB(), mPw, mAct.new RefreshTask(handler));
|
||||
ProgressTask pt = new ProgressTask(mAct, task, R.string.saving_database);
|
||||
pt.run();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateMenu(ContextMenu menu, ContextMenuInfo menuInfo) {
|
||||
menu.add(0, MENU_OPEN, 0, R.string.menu_open);
|
||||
if (!readOnly) {
|
||||
menu.add(0, MENU_DELETE, 0, R.string.menu_delete);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
switch ( item.getItemId() ) {
|
||||
|
||||
case MENU_OPEN:
|
||||
launchEntry();
|
||||
return true;
|
||||
case MENU_DELETE:
|
||||
deleteEntry();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.GroupActivity;
|
||||
import com.keepassdroid.GroupBaseActivity;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.PwGroupV3;
|
||||
import com.keepassdroid.settings.PrefsUtil;
|
||||
|
||||
|
||||
public class PwGroupView extends ClickView {
|
||||
|
||||
protected PwGroup mPw;
|
||||
protected GroupBaseActivity mAct;
|
||||
protected TextView mTv;
|
||||
|
||||
protected static final int MENU_OPEN = Menu.FIRST;
|
||||
|
||||
public static PwGroupView getInstance(GroupBaseActivity act, PwGroup pw) {
|
||||
if ( pw instanceof PwGroupV3 ) {
|
||||
return new PwGroupViewV3(act, pw);
|
||||
} else {
|
||||
return new PwGroupView(act, pw);
|
||||
}
|
||||
}
|
||||
|
||||
protected PwGroupView(GroupBaseActivity act, PwGroup pw) {
|
||||
super(act);
|
||||
mAct = act;
|
||||
|
||||
View gv = View.inflate(act, R.layout.list_entries_group, null);
|
||||
|
||||
mTv = (TextView) gv.findViewById(R.id.group_text);
|
||||
float size = PrefsUtil.getListTextSize(act);
|
||||
mTv.setTextSize(size);
|
||||
|
||||
TextView label = (TextView) gv.findViewById(R.id.group_label);
|
||||
label.setTextSize(size-8);
|
||||
|
||||
populateView(gv, pw);
|
||||
|
||||
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
|
||||
addView(gv, lp);
|
||||
}
|
||||
|
||||
private void populateView(View gv, PwGroup pw) {
|
||||
mPw = pw;
|
||||
|
||||
ImageView iv = (ImageView) gv.findViewById(R.id.group_icon);
|
||||
App.getDB().drawFactory.assignDrawableTo(iv, getResources(), pw.getIcon());
|
||||
|
||||
mTv.setText(pw.getName());
|
||||
}
|
||||
|
||||
public void convertView(PwGroup pw) {
|
||||
populateView(this, pw);
|
||||
}
|
||||
|
||||
public void onClick() {
|
||||
launchGroup();
|
||||
}
|
||||
|
||||
private void launchGroup() {
|
||||
GroupActivity.Launch(mAct, mPw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateMenu(ContextMenu menu, ContextMenuInfo menuInfo) {
|
||||
menu.add(0, MENU_OPEN, 0, R.string.menu_open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
switch ( item.getItemId() ) {
|
||||
|
||||
case MENU_OPEN:
|
||||
launchGroup();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.keepassdroid.GroupBaseActivity;
|
||||
import com.keepassdroid.ProgressTask;
|
||||
import com.keepassdroid.app.App;
|
||||
import com.keepassdroid.database.PwGroup;
|
||||
import com.keepassdroid.database.edit.DeleteGroup;
|
||||
|
||||
public class PwGroupViewV3 extends PwGroupView {
|
||||
|
||||
private static final int MENU_DELETE = MENU_OPEN + 1;
|
||||
|
||||
protected PwGroupViewV3(GroupBaseActivity act, PwGroup pw) {
|
||||
super(act, pw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateMenu(ContextMenu menu, ContextMenuInfo menuInfo) {
|
||||
super.onCreateMenu(menu, menuInfo);
|
||||
|
||||
if (!readOnly) {
|
||||
menu.add(0, MENU_DELETE, 0, R.string.menu_delete);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
if ( ! super.onContextItemSelected(item) ) {
|
||||
switch ( item.getItemId() ) {
|
||||
case MENU_DELETE:
|
||||
Handler handler = new Handler();
|
||||
DeleteGroup task = new DeleteGroup(App.getDB(), mPw, mAct, mAct.new AfterDeleteGroup(handler));
|
||||
ProgressTask pt = new ProgressTask(mAct, task, R.string.saving_database);
|
||||
pt.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.method.ArrowKeyMovementMethod;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class TextViewSelect extends TextView {
|
||||
|
||||
public TextViewSelect(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TextViewSelect(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.textViewStyle);
|
||||
}
|
||||
|
||||
public TextViewSelect(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected MovementMethod getDefaultMovementMethod() {
|
||||
return ArrowKeyMovementMethod.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getDefaultEditable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(CharSequence text, BufferType type) {
|
||||
super.setText(text, BufferType.EDITABLE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
@@ -21,43 +21,31 @@ package com.kunzisoft.keepass;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.keepassdroid.fileselect.FileSelectActivity;
|
||||
import com.kunzisoft.keepass.fileselect.FileSelectActivity;
|
||||
|
||||
public class KeePass extends Activity {
|
||||
|
||||
public static final int EXIT_NORMAL = 0;
|
||||
public static final int EXIT_LOCK = 1;
|
||||
public static final int EXIT_REFRESH = 2;
|
||||
public static final int EXIT_REFRESH_TITLE = 3;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
startFileSelect();
|
||||
}
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
startFileSelectActivity();
|
||||
// Delete flickering for kitkat <=
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
|
||||
private void startFileSelect() {
|
||||
Intent intent = new Intent(this, FileSelectActivity.class);
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
protected void startFileSelectActivity() {
|
||||
FileSelectActivity.launch(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (resultCode == EXIT_NORMAL) {
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.keepassdroid;
|
||||
package com.kunzisoft.keepass.activities;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
@@ -27,8 +27,8 @@ import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.keepassdroid.stylish.StylishActivity;
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.kunzisoft.keepass.stylish.StylishActivity;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kunzisoft.keepass.R;
|
||||
import com.kunzisoft.keepass.app.App;
|
||||
import com.kunzisoft.keepass.database.Database;
|
||||
import com.kunzisoft.keepass.database.ExtraFields;
|
||||
import com.kunzisoft.keepass.database.PwDatabase;
|
||||
import com.kunzisoft.keepass.database.PwEntry;
|
||||
import com.kunzisoft.keepass.database.security.ProtectedString;
|
||||
import com.kunzisoft.keepass.notifications.NotificationCopyingService;
|
||||
import com.kunzisoft.keepass.notifications.NotificationField;
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil;
|
||||
import com.kunzisoft.keepass.timeout.ClipboardHelper;
|
||||
import com.kunzisoft.keepass.utils.EmptyUtils;
|
||||
import com.kunzisoft.keepass.utils.MenuUtil;
|
||||
import com.kunzisoft.keepass.utils.Types;
|
||||
import com.kunzisoft.keepass.utils.Util;
|
||||
import com.kunzisoft.keepass.view.EntryContentsView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.kunzisoft.keepass.settings.PreferencesUtil.isClipboardNotificationsEnable;
|
||||
|
||||
public class EntryActivity extends LockingHideActivity {
|
||||
private final static String TAG = EntryActivity.class.getName();
|
||||
|
||||
public static final String KEY_ENTRY = "entry";
|
||||
|
||||
private ImageView titleIconView;
|
||||
private TextView titleView;
|
||||
private EntryContentsView entryContentsView;
|
||||
|
||||
protected PwEntry mEntry;
|
||||
private boolean mShowPassword;
|
||||
protected boolean readOnly = false;
|
||||
|
||||
private ClipboardHelper clipboardHelper;
|
||||
private boolean firstLaunchOfActivity;
|
||||
|
||||
public static void launch(Activity act, PwEntry pw) {
|
||||
if (LockingActivity.checkTimeIsAllowedOrFinish(act)) {
|
||||
Intent intent = new Intent(act, EntryActivity.class);
|
||||
intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID()));
|
||||
act.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.entry_view);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
Database db = App.getDB();
|
||||
// Likely the app has been killed exit the activity
|
||||
if ( ! db.getLoaded() ) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
readOnly = db.isReadOnly();
|
||||
|
||||
mShowPassword = !PreferencesUtil.isPasswordMask(this);
|
||||
|
||||
// Get Entry from UUID
|
||||
Intent i = getIntent();
|
||||
UUID uuid = Types.bytestoUUID(i.getByteArrayExtra(KEY_ENTRY));
|
||||
mEntry = db.getPwDatabase().getEntryByUUIDId(uuid);
|
||||
if (mEntry == null) {
|
||||
Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set
|
||||
invalidateOptionsMenu();
|
||||
|
||||
// Update last access time.
|
||||
mEntry.touch(false, false);
|
||||
|
||||
// Get views
|
||||
titleIconView = findViewById(R.id.entry_icon);
|
||||
titleView = findViewById(R.id.entry_title);
|
||||
entryContentsView = findViewById(R.id.entry_contents);
|
||||
entryContentsView.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this));
|
||||
|
||||
// Init the clipboard helper
|
||||
clipboardHelper = new ClipboardHelper(this);
|
||||
firstLaunchOfActivity = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Fill data in resume to update from EntryEditActivity
|
||||
fillData();
|
||||
invalidateOptionsMenu();
|
||||
|
||||
// Start to manage field reference to copy a value from ref
|
||||
mEntry.startToManageFieldReferences(App.getDB().getPwDatabase());
|
||||
|
||||
// If notifications enabled in settings
|
||||
// Don't if application timeout
|
||||
if (firstLaunchOfActivity && !App.isShutdown() && isClipboardNotificationsEnable(getApplicationContext())) {
|
||||
if (mEntry.getUsername().length() > 0
|
||||
|| (mEntry.getPassword().length() > 0 && PreferencesUtil.allowCopyPassword(this))
|
||||
|| mEntry.containsCustomFields()) {
|
||||
// username already copied, waiting for user's action before copy password.
|
||||
Intent intent = new Intent(this, NotificationCopyingService.class);
|
||||
intent.setAction(NotificationCopyingService.ACTION_NEW_NOTIFICATION);
|
||||
if (mEntry.getTitle() != null)
|
||||
intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, mEntry.getTitle());
|
||||
// Construct notification fields
|
||||
ArrayList<NotificationField> notificationFields = new ArrayList<>();
|
||||
// Add username if exists to notifications
|
||||
if (mEntry.getUsername().length() > 0)
|
||||
notificationFields.add(
|
||||
new NotificationField(
|
||||
NotificationField.NotificationFieldId.USERNAME,
|
||||
mEntry.getUsername(),
|
||||
getResources()));
|
||||
// Add password to notifications
|
||||
if (PreferencesUtil.allowCopyPassword(this)) {
|
||||
if (mEntry.getPassword().length() > 0)
|
||||
notificationFields.add(
|
||||
new NotificationField(
|
||||
NotificationField.NotificationFieldId.PASSWORD,
|
||||
mEntry.getPassword(),
|
||||
getResources()));
|
||||
}
|
||||
// Add extra fields
|
||||
if (mEntry.allowExtraFields()) {
|
||||
try {
|
||||
mEntry.getFields().doActionToAllCustomProtectedField(new ExtraFields.ActionProtected() {
|
||||
private int anonymousFieldNumber = 0;
|
||||
@Override
|
||||
public void doAction(String key, ProtectedString value) {
|
||||
notificationFields.add(
|
||||
new NotificationField(
|
||||
NotificationField.NotificationFieldId.getAnonymousFieldId()[anonymousFieldNumber],
|
||||
value.toString(),
|
||||
key,
|
||||
getResources()));
|
||||
anonymousFieldNumber++;
|
||||
}
|
||||
});
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
Log.w(TAG, "Only " + NotificationField.NotificationFieldId.getAnonymousFieldId().length +
|
||||
" anonymous notifications are available");
|
||||
}
|
||||
}
|
||||
// Add notifications
|
||||
intent.putParcelableArrayListExtra(NotificationCopyingService.EXTRA_FIELDS, notificationFields);
|
||||
|
||||
startService(intent);
|
||||
}
|
||||
mEntry.endToManageFieldReferences();
|
||||
}
|
||||
firstLaunchOfActivity = false;
|
||||
}
|
||||
|
||||
private void populateTitle(Drawable drawIcon, String text) {
|
||||
titleIconView.setImageDrawable(drawIcon);
|
||||
titleView.setText(text);
|
||||
}
|
||||
|
||||
protected void fillData() {
|
||||
Database db = App.getDB();
|
||||
PwDatabase pm = db.getPwDatabase();
|
||||
|
||||
mEntry.startToManageFieldReferences(pm);
|
||||
|
||||
// Assign title
|
||||
populateTitle(db.getDrawFactory().getIconDrawable(getResources(), mEntry.getIcon()),
|
||||
mEntry.getTitle());
|
||||
|
||||
// Assign basic fields
|
||||
entryContentsView.assignUserName(mEntry.getUsername());
|
||||
entryContentsView.assignUserNameCopyListener(view ->
|
||||
clipboardHelper.timeoutCopyToClipboard(mEntry.getUsername(),
|
||||
getString(R.string.copy_field, getString(R.string.entry_user_name)))
|
||||
);
|
||||
|
||||
entryContentsView.assignPassword(mEntry.getPassword());
|
||||
if (PreferencesUtil.allowCopyPassword(this)) {
|
||||
entryContentsView.assignPasswordCopyListener(view ->
|
||||
clipboardHelper.timeoutCopyToClipboard(mEntry.getPassword(),
|
||||
getString(R.string.copy_field, getString(R.string.entry_password)))
|
||||
);
|
||||
}
|
||||
|
||||
entryContentsView.assignURL(mEntry.getUrl());
|
||||
|
||||
entryContentsView.setHiddenPasswordStyle(!mShowPassword);
|
||||
entryContentsView.assignComment(mEntry.getNotes());
|
||||
|
||||
// Assign custom fields
|
||||
if (mEntry.allowExtraFields()) {
|
||||
entryContentsView.clearExtraFields();
|
||||
|
||||
mEntry.getFields().doActionToAllCustomProtectedField((label, value) ->
|
||||
|
||||
entryContentsView.addExtraField(label, value, view ->
|
||||
clipboardHelper.timeoutCopyToClipboard(
|
||||
value.toString(),
|
||||
getString(R.string.copy_field, label)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
// Assign dates
|
||||
entryContentsView.assignCreationDate(mEntry.getCreationTime().getDate());
|
||||
entryContentsView.assignModificationDate(mEntry.getLastModificationTime().getDate());
|
||||
entryContentsView.assignLastAccessDate(mEntry.getLastAccessTime().getDate());
|
||||
Date expires = mEntry.getExpiryTime().getDate();
|
||||
if ( mEntry.isExpires() ) {
|
||||
entryContentsView.assignExpiresDate(expires);
|
||||
} else {
|
||||
entryContentsView.assignExpiresDate(getString(R.string.never));
|
||||
}
|
||||
|
||||
mEntry.endToManageFieldReferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE:
|
||||
fillData();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void changeShowPasswordIcon(MenuItem togglePassword) {
|
||||
if ( mShowPassword ) {
|
||||
togglePassword.setTitle(R.string.menu_hide_password);
|
||||
togglePassword.setIcon(R.drawable.ic_visibility_off_white_24dp);
|
||||
} else {
|
||||
togglePassword.setTitle(R.string.menu_showpass);
|
||||
togglePassword.setIcon(R.drawable.ic_visibility_white_24dp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
MenuUtil.donationMenuInflater(inflater, menu);
|
||||
inflater.inflate(R.menu.entry, menu);
|
||||
inflater.inflate(R.menu.database_lock, menu);
|
||||
|
||||
if (readOnly) {
|
||||
MenuItem edit = menu.findItem(R.id.menu_edit);
|
||||
if (edit != null)
|
||||
edit.setVisible(false);
|
||||
}
|
||||
|
||||
MenuItem togglePassword = menu.findItem(R.id.menu_toggle_pass);
|
||||
if (entryContentsView != null && togglePassword != null) {
|
||||
if (entryContentsView.isPasswordPresent() || entryContentsView.atLeastOneFieldProtectedPresent()) {
|
||||
changeShowPasswordIcon(togglePassword);
|
||||
} else {
|
||||
togglePassword.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem gotoUrl = menu.findItem(R.id.menu_goto_url);
|
||||
if (gotoUrl != null) {
|
||||
// In API >= 11 onCreateOptionsMenu may be called before onCreate completes
|
||||
// so mEntry may not be set
|
||||
if (mEntry == null) {
|
||||
gotoUrl.setVisible(false);
|
||||
} else {
|
||||
String url = mEntry.getUrl();
|
||||
if (EmptyUtils.isNullOrEmpty(url)) {
|
||||
// disable button if url is not available
|
||||
gotoUrl.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch ( item.getItemId() ) {
|
||||
case R.id.menu_donate:
|
||||
return MenuUtil.onDonationItemSelected(this);
|
||||
|
||||
case R.id.menu_toggle_pass:
|
||||
mShowPassword = !mShowPassword;
|
||||
changeShowPasswordIcon(item);
|
||||
entryContentsView.setHiddenPasswordStyle(!mShowPassword);
|
||||
return true;
|
||||
|
||||
case R.id.menu_edit:
|
||||
EntryEditActivity.launch(EntryActivity.this, mEntry);
|
||||
return true;
|
||||
|
||||
case R.id.menu_goto_url:
|
||||
String url;
|
||||
url = mEntry.getUrl();
|
||||
|
||||
// Default http:// if no protocol specified
|
||||
if ( ! url.contains("://") ) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
|
||||
try {
|
||||
Util.gotoUrl(this, url);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.menu_lock:
|
||||
lockAndExit();
|
||||
return true;
|
||||
|
||||
case android.R.id.home :
|
||||
finish(); // close this activity and return to preview activity (if there is any)
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
// Transit data in previous Activity after an update
|
||||
/*
|
||||
TODO Slowdown when add entry as result
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntry);
|
||||
setResult(EntryEditActivity.UPDATE_ENTRY_RESULT_CODE, intent);
|
||||
*/
|
||||
super.finish();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user