mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Compare commits
229 Commits
2.5.0.0bet
...
2.5.0.0bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
c24c18d89e | ||
|
|
61043b3acb | ||
|
|
52f8862e71 | ||
|
|
8bd32e6605 | ||
|
|
d430305eb1 | ||
|
|
2cb3972865 | ||
|
|
724bb1fc86 | ||
|
|
ffffeb9e85 | ||
|
|
ebe8c90238 | ||
|
|
25f657e665 | ||
|
|
38def26865 | ||
|
|
a08e65733d | ||
|
|
e5bc9bfd1d | ||
|
|
767f7b06d6 | ||
|
|
a06977cd25 | ||
|
|
60be6f1223 | ||
|
|
e9929ed848 | ||
|
|
21c657c107 | ||
|
|
a93271401d | ||
|
|
a66cd68ae2 | ||
|
|
9ac060ea05 | ||
|
|
c20f453b90 | ||
|
|
27d633a1e9 | ||
|
|
221a851f44 | ||
|
|
c091ffb5e1 | ||
|
|
d8f81b669d | ||
|
|
fb72f37ebb | ||
|
|
6c5936d15d | ||
|
|
e68c682cac | ||
|
|
04da145513 | ||
|
|
a15a039f2a | ||
|
|
818c0a769b | ||
|
|
cc7b8a3fd8 | ||
|
|
43b4d00902 | ||
|
|
06b126469a | ||
|
|
2ca4e817e9 | ||
|
|
42a52b26bf | ||
|
|
5d8a73080b | ||
|
|
0b736ce0b3 | ||
|
|
dea6515e90 | ||
|
|
1bd1b2a224 | ||
|
|
b7a76ed2e7 | ||
|
|
a1237215cc | ||
|
|
4bb869e288 | ||
|
|
1d528488d3 | ||
|
|
90282d9722 | ||
|
|
2c7b19d67d | ||
|
|
981fef8fb1 | ||
|
|
77c8207c73 | ||
|
|
825a5c7e73 | ||
|
|
d921a0ae1a | ||
|
|
ffd40a5419 | ||
|
|
1946844858 | ||
|
|
052641c556 | ||
|
|
7950933d1f | ||
|
|
8f8b265d97 | ||
|
|
d20eef0922 |
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -24,7 +24,7 @@
|
|||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
46
CHANGELOG
46
CHANGELOG
@@ -1,3 +1,45 @@
|
|||||||
|
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
|
||||||
|
* Add information for each database file
|
||||||
|
* Setting to delete fingerprints
|
||||||
|
* Solve bugs when change fingerprint
|
||||||
|
* Delete view assignment for fingerprint opening
|
||||||
|
* Merge KeePassDroid 2.2.1
|
||||||
|
|
||||||
KeepassDX (2.5.0.0beta3)
|
KeepassDX (2.5.0.0beta3)
|
||||||
* New database workflow with new screens and folder selection
|
* New database workflow with new screens and folder selection
|
||||||
* Settings for default password generation
|
* Settings for default password generation
|
||||||
@@ -21,6 +63,10 @@ KeepassDX (2.5.0.0beta1)
|
|||||||
* Update French translation
|
* Update French translation
|
||||||
* Change donation (see KeepassDroid to contribute on both projects)
|
* Change donation (see KeepassDroid to contribute on both projects)
|
||||||
|
|
||||||
|
KeePassDroid (2.2.1)
|
||||||
|
* Fix kdbx4 date corruption
|
||||||
|
* Updated German translations
|
||||||
|
|
||||||
KeePassDroid (2.2.0.9)
|
KeePassDroid (2.2.0.9)
|
||||||
* Update build tools version to workaround CM/Lineage bug (closes: #249)
|
* Update build tools version to workaround CM/Lineage bug (closes: #249)
|
||||||
* Update Russian translations
|
* Update Russian translations
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Diego Pierotto - Italian
|
|||||||
Laurent, Norman Obry, Nam, Bruno Parmentier, Credomo - French
|
Laurent, Norman Obry, Nam, Bruno Parmentier, Credomo - French
|
||||||
Maciej Bieniek, cod3r - Polish
|
Maciej Bieniek, cod3r - Polish
|
||||||
Максим Сёмочкин, i.nedoboy, filimonic, bboa - Russian
|
Максим Сёмочкин, i.nedoboy, filimonic, bboa - Russian
|
||||||
MaWi, rvs2008, meviox, MaDill - German
|
MaWi, rvs2008, meviox, MaDill, EdlerProgrammierer, Jan Thomas - German
|
||||||
yslandro - Norwegian Nynorsk
|
yslandro - Norwegian Nynorsk
|
||||||
王科峰 - Chinese
|
王科峰 - Chinese
|
||||||
Typhoon - Slovak
|
Typhoon - Slovak
|
||||||
|
|||||||
35
ReadMe.md
35
ReadMe.md
@@ -1,18 +1,19 @@
|
|||||||
# Android Keepass DX
|
# 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
|
### Features
|
||||||
|
|
||||||
- Simplified creation of the database file
|
* Create database files / entries and groups
|
||||||
- Create entries and groups
|
* Support for .kdb and .kdbx files (version 1 to 4)
|
||||||
- Open database, copy username / password, open URI / URL
|
* Open database, copy username / password, open URI / URL
|
||||||
- Fingerprint for fast unlocking
|
* Fingerprint for fast unlocking
|
||||||
- Material design with themes
|
* Material design with themes
|
||||||
- Device integration and AutoFill (In progress)
|
* 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/fastlane/metadata/android/en-US/images/phoneScreenshots/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/screen2.jpg" width="220">
|
||||||
|
|
||||||
## What is KeePass?
|
## 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")
|
[](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/fastlane/metadata/android/en-US/images/phoneScreenshots/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/screen5.jpg" width="220">
|
||||||
|
|
||||||
## Download
|
## 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"
|
alt="Get it on Google Play"
|
||||||
height="80">](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.free)
|
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
|
## License
|
||||||
|
|
||||||
Copyright (c) 2017 Jeremy Jamet / Kunzisoft.
|
Copyright (c) 2017 Jeremy Jamet / Kunzisoft.
|
||||||
@@ -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
|
You should have received a copy of the GNU General Public License
|
||||||
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
along with 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,15 +1,16 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion = 25
|
compileSdkVersion = 27
|
||||||
buildToolsVersion = "27.0.1"
|
buildToolsVersion = "27.0.3"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.kunzisoft.keepass"
|
applicationId "com.kunzisoft.keepass"
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 25
|
targetSdkVersion 27
|
||||||
versionCode = 3
|
versionCode = 7
|
||||||
versionName = "2.5.0.0beta3"
|
versionName = "2.5.0.0beta7"
|
||||||
|
multiDexEnabled true
|
||||||
|
|
||||||
testApplicationId = "com.keepassdroid.tests"
|
testApplicationId = "com.keepassdroid.tests"
|
||||||
testInstrumentationRunner = "android.test.InstrumentationTestRunner"
|
testInstrumentationRunner = "android.test.InstrumentationTestRunner"
|
||||||
@@ -58,21 +59,34 @@ android {
|
|||||||
buildConfigField "boolean", "GOOGLE_PLAY_VERSION", "true"
|
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 spongycastleVersion = "1.58.0.0"
|
||||||
|
def permissionDispatcherVersion = "3.1.0"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
androidTestCompile "junit:junit:4.12"
|
implementation "com.android.support:appcompat-v7:$supportVersion"
|
||||||
compile "com.android.support:appcompat-v7:$supportVersion"
|
implementation "com.android.support:design:$supportVersion"
|
||||||
compile "com.android.support:design:$supportVersion"
|
implementation "com.android.support:preference-v7:$supportVersion"
|
||||||
compile "com.android.support:preference-v7:$supportVersion"
|
implementation "com.android.support:preference-v14:$supportVersion"
|
||||||
compile "com.android.support:preference-v14:$supportVersion"
|
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||||
compile "com.android.support:cardview-v7:$supportVersion"
|
implementation "com.madgag.spongycastle:core:$spongycastleVersion"
|
||||||
compile "com.madgag.spongycastle:core:$spongycastleVersion"
|
implementation "com.madgag.spongycastle:prov:$spongycastleVersion"
|
||||||
compile "com.madgag.spongycastle:prov:$spongycastleVersion"
|
// Time
|
||||||
compile "joda-time:joda-time:2.9.9"
|
implementation "joda-time:joda-time:2.9.9"
|
||||||
compile "org.sufficientlysecure:html-textview:3.5"
|
implementation "org.sufficientlysecure:html-textview:3.5"
|
||||||
compile "com.nononsenseapps:filepicker:4.1.0"
|
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"
|
||||||
|
}
|
||||||
|
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'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class PwEntryTestV3 extends AndroidTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testName() {
|
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 {
|
public void testPassword() throws UnsupportedEncodingException {
|
||||||
@@ -54,7 +54,7 @@ public class PwEntryTestV3 extends AndroidTestCase {
|
|||||||
|
|
||||||
public void testCreation() {
|
public void testCreation() {
|
||||||
Calendar cal = Calendar.getInstance();
|
Calendar cal = Calendar.getInstance();
|
||||||
cal.setTime(mPE.tCreation.getJDate());
|
cal.setTime(mPE.getCreationTime().getDate());
|
||||||
|
|
||||||
assertEquals("Incorrect year.", cal.get(Calendar.YEAR), 2009);
|
assertEquals("Incorrect year.", cal.get(Calendar.YEAR), 2009);
|
||||||
assertEquals("Incorrect month.", cal.get(Calendar.MONTH), 3);
|
assertEquals("Incorrect month.", cal.get(Calendar.MONTH), 3);
|
||||||
|
|||||||
@@ -19,10 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.tests;
|
package com.keepassdroid.tests;
|
||||||
|
|
||||||
import java.util.UUID;
|
import com.keepassdroid.database.AutoType;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
import com.keepassdroid.database.PwEntryV4;
|
import com.keepassdroid.database.PwEntryV4;
|
||||||
import com.keepassdroid.database.PwGroupV4;
|
import com.keepassdroid.database.PwGroupV4;
|
||||||
import com.keepassdroid.database.PwIconCustom;
|
import com.keepassdroid.database.PwIconCustom;
|
||||||
@@ -30,29 +27,33 @@ import com.keepassdroid.database.PwIconStandard;
|
|||||||
import com.keepassdroid.database.security.ProtectedBinary;
|
import com.keepassdroid.database.security.ProtectedBinary;
|
||||||
import com.keepassdroid.database.security.ProtectedString;
|
import com.keepassdroid.database.security.ProtectedString;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class PwEntryTestV4 extends TestCase {
|
public class PwEntryTestV4 extends TestCase {
|
||||||
public void testAssign() {
|
public void testAssign() {
|
||||||
PwEntryV4 entry = new PwEntryV4();
|
PwEntryV4 entry = new PwEntryV4();
|
||||||
|
|
||||||
entry.additional = "test223";
|
entry.setAdditional("test223");
|
||||||
|
|
||||||
entry.autoType = entry.new AutoType();
|
entry.setAutoType(new AutoType());
|
||||||
entry.autoType.defaultSequence = "1324";
|
entry.getAutoType().defaultSequence = "1324";
|
||||||
entry.autoType.enabled = true;
|
entry.getAutoType().enabled = true;
|
||||||
entry.autoType.obfuscationOptions = 123412432109L;
|
entry.getAutoType().obfuscationOptions = 123412432109L;
|
||||||
entry.autoType.put("key", "value");
|
entry.getAutoType().put("key", "value");
|
||||||
|
|
||||||
entry.backgroupColor = "blue";
|
entry.setBackgroupColor("blue");
|
||||||
entry.binaries.put("key1", new ProtectedBinary(false, new byte[] {0,1}));
|
entry.putProtectedBinary("key1", new ProtectedBinary(false, new byte[] {0,1}));
|
||||||
entry.customIcon = new PwIconCustom(UUID.randomUUID(), new byte[0]);
|
entry.setCustomIcon(new PwIconCustom(UUID.randomUUID(), new byte[0]));
|
||||||
entry.foregroundColor = "red";
|
entry.setForegroundColor("red");
|
||||||
entry.history.add(new PwEntryV4());
|
entry.addToHistory(new PwEntryV4());
|
||||||
entry.icon = new PwIconStandard(5);
|
entry.setIcon(new PwIconStandard(5));
|
||||||
entry.overrideURL = "override";
|
entry.setOverrideURL("override");
|
||||||
entry.parent = new PwGroupV4();
|
entry.setParent(new PwGroupV4());
|
||||||
entry.strings.put("key2", new ProtectedString(false, "value2"));
|
entry.addField("key2", new ProtectedString(false, "value2"));
|
||||||
entry.url = "http://localhost";
|
entry.setUrl("http://localhost");
|
||||||
entry.uuid = UUID.randomUUID();
|
entry.setUUID(UUID.randomUUID());
|
||||||
|
|
||||||
PwEntryV4 target = new PwEntryV4();
|
PwEntryV4 target = new PwEntryV4();
|
||||||
target.assign(entry);
|
target.assign(entry);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class PwGroupTest extends AndroidTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testGroupName() {
|
public void testGroupName() {
|
||||||
assertTrue("Name was " + mPG.name, mPG.name.equals("Internet"));
|
assertTrue("Name was " + mPG.getName(), mPG.getName().equals("Internet"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import java.util.List;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.database.PwDatabase;
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.PwDatabaseV3;
|
import com.keepassdroid.database.PwDatabaseV3;
|
||||||
import com.keepassdroid.database.PwEntry;
|
import com.keepassdroid.database.PwEntry;
|
||||||
@@ -74,8 +74,8 @@ public class DeleteEntry extends AndroidTestCase {
|
|||||||
PwGroup results1 = dbHelp.search(db, ENTRY1_NAME);
|
PwGroup results1 = dbHelp.search(db, ENTRY1_NAME);
|
||||||
PwGroup results2 = dbHelp.search(db, ENTRY2_NAME);
|
PwGroup results2 = dbHelp.search(db, ENTRY2_NAME);
|
||||||
|
|
||||||
assertEquals("Entry1 was not removed from the search results", 0, results1.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.childEntries.size());
|
assertEquals("Entry2 was not removed from the search results", 0, results2.numbersOfChildEntries());
|
||||||
|
|
||||||
// Verify the group was deleted
|
// Verify the group was deleted
|
||||||
group1 = getGroup(pm, GROUP1_NAME);
|
group1 = getGroup(pm, GROUP1_NAME);
|
||||||
|
|||||||
@@ -32,19 +32,21 @@ public class EntryV4 extends TestCase {
|
|||||||
db.historyMaxItems = 2;
|
db.historyMaxItems = 2;
|
||||||
|
|
||||||
PwEntryV4 entry = new PwEntryV4();
|
PwEntryV4 entry = new PwEntryV4();
|
||||||
entry.setTitle("Title1", db);
|
entry.startToDecodeReference(db);
|
||||||
entry.setUsername("User1", db);
|
entry.setTitle("Title1");
|
||||||
|
entry.setUsername("User1");
|
||||||
entry.createBackup(db);
|
entry.createBackup(db);
|
||||||
|
|
||||||
entry.setTitle("Title2", db);
|
entry.setTitle("Title2");
|
||||||
entry.setUsername("User2", db);
|
entry.setUsername("User2");
|
||||||
entry.createBackup(db);
|
entry.createBackup(db);
|
||||||
|
|
||||||
entry.setTitle("Title3", db);
|
entry.setTitle("Title3");
|
||||||
entry.setUsername("User3", db);
|
entry.setUsername("User3");
|
||||||
entry.createBackup(db);
|
entry.createBackup(db);
|
||||||
|
|
||||||
PwEntryV4 backup = entry.history.get(0);
|
PwEntryV4 backup = entry.getHistory().get(0);
|
||||||
|
entry.endToDecodeReference(db);
|
||||||
assertEquals("Title2", backup.getTitle());
|
assertEquals("Title2", backup.getTitle());
|
||||||
assertEquals("User2", backup.getUsername());
|
assertEquals("User2", backup.getUsername());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,15 +27,16 @@ import android.content.res.AssetManager;
|
|||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
import biz.source_code.base64Coder.Base64Coder;
|
import biz.source_code.base64Coder.Base64Coder;
|
||||||
|
|
||||||
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.PwDatabaseV4;
|
import com.keepassdroid.database.PwDatabaseV4;
|
||||||
import com.keepassdroid.database.PwEntryV4;
|
import com.keepassdroid.database.PwEntryV4;
|
||||||
import com.keepassdroid.database.load.ImporterV4;
|
import com.keepassdroid.database.load.ImporterV4;
|
||||||
import com.keepassdroid.utils.SprEngine;
|
import com.keepassdroid.utils.SprEngineV4;
|
||||||
import com.keepassdroid.utils.Types;
|
import com.keepassdroid.utils.Types;
|
||||||
|
|
||||||
public class SprEngineTest extends AndroidTestCase {
|
public class SprEngineTest extends AndroidTestCase {
|
||||||
private PwDatabaseV4 db;
|
private PwDatabaseV4 db;
|
||||||
private SprEngine spr;
|
private SprEngineV4 spr;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
@@ -51,7 +52,7 @@ public class SprEngineTest extends AndroidTestCase {
|
|||||||
|
|
||||||
is.close();
|
is.close();
|
||||||
|
|
||||||
spr = SprEngine.getInstance(db);
|
spr = new SprEngineV4();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String REF = "{REF:P@I:2B1D56590D961F48A8CE8C392CE6CD35}";
|
private final String REF = "{REF:P@I:2B1D56590D961F48A8CE8C392CE6CD35}";
|
||||||
@@ -69,7 +70,7 @@ public class SprEngineTest extends AndroidTestCase {
|
|||||||
|
|
||||||
private UUID decodeUUID(String encoded) {
|
private UUID decodeUUID(String encoded) {
|
||||||
if (encoded == null || encoded.length() == 0 ) {
|
if (encoded == null || encoded.length() == 0 ) {
|
||||||
return PwDatabaseV4.UUID_ZERO;
|
return PwDatabase.UUID_ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] buf = Base64Coder.decode(encoded);
|
byte[] buf = Base64Coder.decode(encoded);
|
||||||
|
|||||||
@@ -25,12 +25,10 @@ import android.content.Context;
|
|||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.database.PwDatabaseV3Debug;
|
import com.keepassdroid.database.PwDatabaseV3Debug;
|
||||||
import com.keepassdroid.database.load.Importer;
|
import com.keepassdroid.database.load.Importer;
|
||||||
import com.keepassdroid.tests.TestUtil;
|
import com.keepassdroid.tests.TestUtil;
|
||||||
import com.keepassdroid.utils.EmptyUtils;
|
|
||||||
import com.keepassdroid.utils.UriUtil;
|
|
||||||
|
|
||||||
public class TestData {
|
public class TestData {
|
||||||
private static final String TEST1_KEYFILE = "";
|
private static final String TEST1_KEYFILE = "";
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.Database;
|
|
||||||
import com.keepassdroid.database.PwGroup;
|
import com.keepassdroid.database.PwGroup;
|
||||||
import com.keepassdroid.tests.database.TestData;
|
import com.keepassdroid.tests.database.TestData;
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ public class SearchTest extends AndroidTestCase {
|
|||||||
|
|
||||||
public void testSearch() {
|
public void testSearch() {
|
||||||
PwGroup results = mDb.Search("Amazon");
|
PwGroup results = mDb.Search("Amazon");
|
||||||
assertTrue("Search result not found.", results.childEntries.size() > 0);
|
assertTrue("Search result not found.", results.numbersOfChildEntries() > 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,14 +50,14 @@ public class SearchTest extends AndroidTestCase {
|
|||||||
updateOmitSetting(false);
|
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() {
|
public void testBackupExcluded() {
|
||||||
updateOmitSetting(true);
|
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) {
|
private void updateOmitSetting(boolean setting) {
|
||||||
@@ -66,7 +65,7 @@ public class SearchTest extends AndroidTestCase {
|
|||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
|
|
||||||
editor.putBoolean(ctx.getString(R.string.omitbackup_key), setting);
|
editor.putBoolean("settings_omitbackup_key", setting);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,9 @@
|
|||||||
android:theme="@style/KeepassDXStyle.Light"
|
android:theme="@style/KeepassDXStyle.Light"
|
||||||
tools:replace="android:theme">
|
tools:replace="android:theme">
|
||||||
<!-- TODO backup API Key -->
|
<!-- TODO backup API Key -->
|
||||||
<meta-data android:name="com.google.android.backup.api_key"
|
<meta-data
|
||||||
android:value="" />
|
android:name="com.google.android.backup.api_key"
|
||||||
|
android:value="" />
|
||||||
|
|
||||||
<!-- Folder picker -->
|
<!-- Folder picker -->
|
||||||
<provider
|
<provider
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
android:resource="@xml/nnf_provider_paths" />
|
android:resource="@xml/nnf_provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
<activity
|
<activity
|
||||||
android:name="com.keepassdroid.FilePickerStylishActivity"
|
android:name="com.keepassdroid.fileselect.FilePickerStylishActivity"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.GET_CONTENT" />
|
<action android:name="android.intent.action.GET_CONTENT" />
|
||||||
@@ -52,12 +53,19 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="com.keepassdroid.fileselect.FileSelectActivity"
|
<activity
|
||||||
|
android:name="com.keepassdroid.fileselect.FileSelectActivity"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:windowSoftInputMode="stateHidden" />
|
android:windowSoftInputMode="stateHidden" />
|
||||||
<activity android:name="com.keepassdroid.AboutActivity"
|
<activity
|
||||||
|
android:name="com.keepassdroid.activities.AboutActivity"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
android:label="@string/menu_about" />
|
android:label="@string/menu_about" />
|
||||||
<activity android:name="com.keepassdroid.PasswordActivity" android:configChanges="orientation|keyboardHidden">
|
<activity
|
||||||
|
android:name="com.keepassdroid.password.PasswordActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
@@ -87,42 +95,63 @@
|
|||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
<intent-filter tools:ignore="AppLinkUrlError">
|
||||||
<activity android:name="com.keepassdroid.GroupActivityV3" android:configChanges="orientation|keyboardHidden">
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<meta-data android:name="android.app.default_searchable"
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
android:value="com.keepassdroid.search.SearchResults" />
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
</activity>
|
<data android:mimeType="application/octet-stream"/>
|
||||||
<activity android:name="com.keepassdroid.GroupActivityV4" android:configChanges="orientation|keyboardHidden">
|
</intent-filter>
|
||||||
<meta-data android:name="android.app.default_searchable"
|
|
||||||
android:value="com.keepassdroid.search.SearchResults"
|
|
||||||
android:exported="false" />
|
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="com.keepassdroid.EntryActivity"
|
android:name="com.keepassdroid.activities.GroupActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android:windowSoftInputMode="adjustPan">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.default_searchable"
|
||||||
|
android:value="com.keepassdroid.search.SearchResults"
|
||||||
|
android:exported="false"/>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name="com.keepassdroid.activities.EntryActivity"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:windowSoftInputMode="stateHidden" />
|
android:windowSoftInputMode="stateHidden" />
|
||||||
<activity
|
<activity
|
||||||
android:name="com.keepassdroid.EntryActivityV4"
|
android:name="com.keepassdroid.activities.EntryEditActivity"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:windowSoftInputMode="stateHidden" />
|
android:windowSoftInputMode="stateHidden" />
|
||||||
<activity
|
<activity
|
||||||
android:name="com.keepassdroid.EntryEditActivityV3"
|
android:name="com.keepassdroid.search.SearchResultsActivity"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:launchMode="standard">
|
||||||
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">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
<action android:name="android.intent.action.SEARCH" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</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>
|
||||||
<activity android:name="com.keepassdroid.settings.SettingsActivity" />
|
<activity android:name="com.keepassdroid.settings.SettingsActivity" />
|
||||||
|
<activity android:name="com.keepassdroid.autofill.AutoFillAuthActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden" />
|
||||||
|
<activity android:name="com.keepassdroid.settings.SettingsAutofillActivity" />
|
||||||
|
|
||||||
<service android:name="com.keepassdroid.services.TimeoutService" />
|
<service android:name="com.keepassdroid.timeout.TimeoutService" />
|
||||||
|
<service
|
||||||
|
android:name="com.keepassdroid.notifications.NotificationCopyingService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false" />
|
||||||
|
<service
|
||||||
|
android:name="com.keepassdroid.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" />
|
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -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,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;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.keepassdroid.database.PwGroupIdV3;
|
|
||||||
|
|
||||||
public class GroupActivityV3 extends GroupActivity {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected PwGroupIdV3 retrieveGroupId(Intent i) {
|
|
||||||
int id = i.getIntExtra(KEY_ENTRY, -1);
|
|
||||||
|
|
||||||
if ( id == -1 ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PwGroupIdV3(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setupButtons() {
|
|
||||||
super.setupButtons();
|
|
||||||
addEntryEnabled = !isRoot && !readOnly;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,326 +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.SharedPreferences;
|
|
||||||
import android.content.SharedPreferences.Editor;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,711 +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.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.DialogInterface.OnClickListener;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.annotation.RequiresApi;
|
|
||||||
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.keepassdroid.app.App;
|
|
||||||
import com.keepassdroid.compat.BackupManagerCompat;
|
|
||||||
import com.keepassdroid.compat.ClipDataCompat;
|
|
||||||
import com.keepassdroid.compat.EditorCompat;
|
|
||||||
import com.keepassdroid.database.edit.LoadDB;
|
|
||||||
import com.keepassdroid.database.edit.OnFinish;
|
|
||||||
import com.keepassdroid.dialog.PasswordEncodingDialogHelper;
|
|
||||||
import com.keepassdroid.fingerprint.FingerPrintAnimatedVector;
|
|
||||||
import com.keepassdroid.fingerprint.FingerPrintHelper;
|
|
||||||
import com.keepassdroid.settings.PrefsUtil;
|
|
||||||
import com.keepassdroid.utils.EmptyUtils;
|
|
||||||
import com.keepassdroid.utils.MenuUtil;
|
|
||||||
import com.keepassdroid.utils.UriUtil;
|
|
||||||
import com.keepassdroid.utils.Util;
|
|
||||||
import com.keepassdroid.view.FingerPrintDialog;
|
|
||||||
import com.keepassdroid.view.KeyFileHelper;
|
|
||||||
import com.kunzisoft.keepass.KeePass;
|
|
||||||
import com.kunzisoft.keepass.R;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
|
|
||||||
public class PasswordActivity extends LockingActivity implements FingerPrintHelper.FingerPrintCallback {
|
|
||||||
|
|
||||||
public static final String KEY_DEFAULT_FILENAME = "defaultFileName";
|
|
||||||
private static final String KEY_FILENAME = "fileName";
|
|
||||||
private static final String KEY_KEYFILE = "keyFile";
|
|
||||||
private static final String KEY_PASSWORD = "password";
|
|
||||||
private static final String KEY_LAUNCH_IMMEDIATELY = "launchImmediately";
|
|
||||||
private static final String VIEW_INTENT = "android.intent.action.VIEW";
|
|
||||||
|
|
||||||
private Uri mDbUri = null;
|
|
||||||
private Uri mKeyUri = null;
|
|
||||||
private boolean mRememberKeyfile;
|
|
||||||
SharedPreferences prefs;
|
|
||||||
SharedPreferences prefsNoBackup;
|
|
||||||
|
|
||||||
private FingerPrintHelper fingerPrintHelper;
|
|
||||||
private boolean fingerprintMustBeConfigured = true;
|
|
||||||
|
|
||||||
private int mode;
|
|
||||||
private static final String PREF_KEY_VALUE_PREFIX = "valueFor_"; // key is a combination of db file name and this prefix
|
|
||||||
private static final String PREF_KEY_IV_PREFIX = "ivFor_"; // key is a combination of db file name and this prefix
|
|
||||||
|
|
||||||
private View fingerprintContainerView;
|
|
||||||
private View fingerprintImageView;
|
|
||||||
private FingerPrintAnimatedVector fingerPrintAnimatedVector;
|
|
||||||
private TextView fingerprintTextView;
|
|
||||||
private TextView filenameView;
|
|
||||||
private EditText passwordView;
|
|
||||||
private EditText keyFileView;
|
|
||||||
private Button confirmButtonView;
|
|
||||||
private CheckBox checkboxPasswordView;
|
|
||||||
private CheckBox checkboxKeyfileView;
|
|
||||||
|
|
||||||
private KeyFileHelper keyFileHelper;
|
|
||||||
|
|
||||||
public static void Launch(
|
|
||||||
Activity act,
|
|
||||||
String fileName) throws FileNotFoundException {
|
|
||||||
Launch(act, fileName, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Launch(
|
|
||||||
Activity act,
|
|
||||||
String fileName,
|
|
||||||
String keyFile) throws FileNotFoundException {
|
|
||||||
if (EmptyUtils.isNullOrEmpty(fileName)) {
|
|
||||||
throw new FileNotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri uri = UriUtil.parseDefaultFile(fileName);
|
|
||||||
assert uri != null;
|
|
||||||
String scheme = uri.getScheme();
|
|
||||||
|
|
||||||
if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
|
|
||||||
File dbFile = new File(uri.getPath());
|
|
||||||
if (!dbFile.exists()) {
|
|
||||||
throw new FileNotFoundException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent i = new Intent(act, PasswordActivity.class);
|
|
||||||
i.putExtra(KEY_FILENAME, fileName);
|
|
||||||
i.putExtra(KEY_KEYFILE, keyFile);
|
|
||||||
|
|
||||||
act.startActivityForResult(i, 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onActivityResult(
|
|
||||||
int requestCode,
|
|
||||||
int resultCode,
|
|
||||||
Intent data) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
|
|
||||||
keyFileHelper.onActivityResultCallback(requestCode, resultCode, data,
|
|
||||||
new KeyFileHelper.KeyFileCallback() {
|
|
||||||
@Override
|
|
||||||
public void onKeyFileResultCallback(Uri uri) {
|
|
||||||
if(uri != null) {
|
|
||||||
keyFileView.setText(uri.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (requestCode) {
|
|
||||||
case KeePass.EXIT_NORMAL:
|
|
||||||
setEmptyViews();
|
|
||||||
App.getDB().clear();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case KeePass.EXIT_LOCK:
|
|
||||||
setResult(KeePass.EXIT_LOCK);
|
|
||||||
setEmptyViews();
|
|
||||||
finish();
|
|
||||||
App.getDB().clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
Intent i = getIntent();
|
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
prefsNoBackup = getSharedPreferences("nobackup", Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
mRememberKeyfile = prefs.getBoolean(getString(R.string.keyfile_key), getResources().getBoolean(R.bool.keyfile_default));
|
|
||||||
setContentView(R.layout.password);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
confirmButtonView = (Button) findViewById(R.id.pass_ok);
|
|
||||||
fingerprintContainerView = findViewById(R.id.fingerprint_container);
|
|
||||||
fingerprintImageView = findViewById(R.id.fingerprint_image);
|
|
||||||
fingerprintTextView = (TextView) findViewById(R.id.fingerprint_label);
|
|
||||||
filenameView = (TextView) findViewById(R.id.filename);
|
|
||||||
passwordView = (EditText) findViewById(R.id.password);
|
|
||||||
keyFileView = (EditText) findViewById(R.id.pass_keyfile);
|
|
||||||
checkboxPasswordView = (CheckBox) findViewById(R.id.password_checkbox);
|
|
||||||
checkboxKeyfileView = (CheckBox) findViewById(R.id.keyfile_checkox);
|
|
||||||
|
|
||||||
passwordView.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable editable) {
|
|
||||||
checkboxPasswordView.setChecked(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
keyFileView.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable editable) {
|
|
||||||
checkboxKeyfileView.setChecked(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
fingerPrintAnimatedVector = new FingerPrintAnimatedVector(this,
|
|
||||||
(ImageView) fingerprintImageView);
|
|
||||||
}
|
|
||||||
|
|
||||||
new InitTask().execute(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
// If the application was shutdown make sure to clear the password field, if it
|
|
||||||
// was saved in the instance state
|
|
||||||
if (App.isShutdown()) {
|
|
||||||
setEmptyViews();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the shutdown flag
|
|
||||||
App.clearShutdown();
|
|
||||||
|
|
||||||
// checks if fingerprint is available, will also start listening for fingerprints when available
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
initForFingerprint();
|
|
||||||
checkAvailability();
|
|
||||||
if (fingerPrintAnimatedVector != null) {
|
|
||||||
fingerPrintAnimatedVector.startScan();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setEmptyViews() {
|
|
||||||
passwordView.setText("");
|
|
||||||
keyFileView.setText("");
|
|
||||||
checkboxPasswordView.setChecked(false);
|
|
||||||
checkboxKeyfileView.setChecked(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void retrieveSettings() {
|
|
||||||
String defaultFilename = prefs.getString(KEY_DEFAULT_FILENAME, "");
|
|
||||||
if (!EmptyUtils.isNullOrEmpty(mDbUri.getPath()) && UriUtil.equalsDefaultfile(mDbUri, defaultFilename)) {
|
|
||||||
CompoundButton checkbox = (CompoundButton) findViewById(R.id.default_database);
|
|
||||||
checkbox.setChecked(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Uri getKeyFile(Uri dbUri) {
|
|
||||||
if (mRememberKeyfile) {
|
|
||||||
return App.getFileHistory().getFileByName(dbUri);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void populateView() {
|
|
||||||
String db = (mDbUri == null) ? "" : mDbUri.toString();
|
|
||||||
if (!db.isEmpty())
|
|
||||||
filenameView.setText(db);
|
|
||||||
|
|
||||||
String key = (mKeyUri == null) ? "" : mKeyUri.toString();
|
|
||||||
if (!key.isEmpty())
|
|
||||||
keyFileView.setText(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fingerprint related code here
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
private void initForFingerprint() {
|
|
||||||
fingerPrintHelper = new FingerPrintHelper(this, this);
|
|
||||||
|
|
||||||
// when text entered we can enable the logon/purchase button and if required update encryption/decryption mode
|
|
||||||
passwordView.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(
|
|
||||||
final CharSequence s,
|
|
||||||
final int start,
|
|
||||||
final int count,
|
|
||||||
final int after) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(
|
|
||||||
final CharSequence s,
|
|
||||||
final int start,
|
|
||||||
final int before,
|
|
||||||
final int count) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(final Editable s) {
|
|
||||||
if ( !fingerprintMustBeConfigured ) {
|
|
||||||
final boolean validInput = s.length() > 0;
|
|
||||||
// encrypt or decrypt mode based on how much input or not
|
|
||||||
fingerprintTextView.setText(validInput ? R.string.store_with_fingerprint : R.string.scanning_fingerprint);
|
|
||||||
mode = validInput ? toggleMode(Cipher.ENCRYPT_MODE) : toggleMode(Cipher.DECRYPT_MODE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// callback for fingerprint findings
|
|
||||||
fingerPrintHelper.setAuthenticationCallback(new FingerprintManagerCompat.AuthenticationCallback() {
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationError(
|
|
||||||
final int errorCode,
|
|
||||||
final CharSequence errString) {
|
|
||||||
|
|
||||||
// this is triggered on stop/start listening done by helper to switch between modes so don't restart here
|
|
||||||
// errorCode = 5
|
|
||||||
// errString = "Fingerprint operation canceled."
|
|
||||||
//onFingerprintException();
|
|
||||||
//fingerprintTextView.setText(errString);
|
|
||||||
// true false fingerprint readings are handled otherwise with the toast messages, see below in code
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationHelp(
|
|
||||||
final int helpCode,
|
|
||||||
final CharSequence helpString) {
|
|
||||||
|
|
||||||
onFingerprintException(new Exception("onAuthenticationHelp"));
|
|
||||||
fingerprintTextView.setText(helpString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationSucceeded(final FingerprintManagerCompat.AuthenticationResult result) {
|
|
||||||
|
|
||||||
if (mode == Cipher.ENCRYPT_MODE) {
|
|
||||||
|
|
||||||
// newly store the entered password in encrypted way
|
|
||||||
final String password = passwordView.getText().toString();
|
|
||||||
fingerPrintHelper.encryptData(password);
|
|
||||||
|
|
||||||
} else if (mode == Cipher.DECRYPT_MODE) {
|
|
||||||
|
|
||||||
// retrieve the encrypted value from preferences
|
|
||||||
final String encryptedValue = prefsNoBackup.getString(getPreferenceKeyValue(), null);
|
|
||||||
if (encryptedValue != null) {
|
|
||||||
fingerPrintHelper.decryptData(encryptedValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationFailed() {
|
|
||||||
onFingerprintException(new Exception("onAuthenticationFailed"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPreferenceKeyValue() {
|
|
||||||
// makes it possible to store passwords uniqly per database
|
|
||||||
return PREF_KEY_VALUE_PREFIX + (mDbUri != null ? mDbUri.getPath() : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPreferenceKeyIvSpec() {
|
|
||||||
return PREF_KEY_IV_PREFIX + (mDbUri != null ? mDbUri.getPath() : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
private int toggleMode(final int newMode) {
|
|
||||||
mode = newMode;
|
|
||||||
switch (mode) {
|
|
||||||
case Cipher.ENCRYPT_MODE:
|
|
||||||
fingerPrintHelper.initEncryptData();
|
|
||||||
break;
|
|
||||||
case Cipher.DECRYPT_MODE:
|
|
||||||
final String ivSpecValue = prefsNoBackup.getString(getPreferenceKeyIvSpec(), null);
|
|
||||||
fingerPrintHelper.initDecryptData(ivSpecValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return newMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
|
||||||
&& fingerPrintAnimatedVector != null) {
|
|
||||||
fingerPrintAnimatedVector.stopScan();
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop listening when we go in background
|
|
||||||
if (fingerPrintHelper != null) {
|
|
||||||
fingerPrintHelper.stopListening();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setFingerPrintVisibility(int vis) {
|
|
||||||
fingerprintContainerView.setVisibility(vis);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
private void checkAvailability() {
|
|
||||||
|
|
||||||
// fingerprint not supported (by API level or hardware) so keep option hidden
|
|
||||||
// or manually disable
|
|
||||||
if (!PrefsUtil.isFingerprintEnable(getApplicationContext())
|
|
||||||
|| !fingerPrintHelper.isFingerprintSupported(FingerprintManagerCompat.from(this))) {
|
|
||||||
setFingerPrintVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
// fingerprint is available but not configured show icon but in disabled state with some information
|
|
||||||
else {
|
|
||||||
// show explanations
|
|
||||||
fingerprintContainerView.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
FingerPrintDialog fingerPrintDialog = new FingerPrintDialog();
|
|
||||||
fingerPrintDialog.show(getSupportFragmentManager(), "fingerprintDialog");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setFingerPrintVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
if (!fingerPrintHelper.hasEnrolledFingerprints()) {
|
|
||||||
fingerprintImageView.setAlpha(0.3f);
|
|
||||||
// This happens when no fingerprints are registered. Listening won't start
|
|
||||||
fingerprintTextView.setText(R.string.configure_fingerprint);
|
|
||||||
}
|
|
||||||
// finally fingerprint available and configured so we can use it
|
|
||||||
else {
|
|
||||||
fingerprintMustBeConfigured = false;
|
|
||||||
fingerprintImageView.setAlpha(1f);
|
|
||||||
|
|
||||||
// fingerprint available but no stored password found yet for this DB so show info don't listen
|
|
||||||
if (prefsNoBackup.getString(getPreferenceKeyValue(), null) == null) {
|
|
||||||
fingerprintTextView.setText(R.string.no_password_stored);
|
|
||||||
}
|
|
||||||
// all is set here so we can confirm to user and start listening for fingerprints
|
|
||||||
else {
|
|
||||||
fingerprintTextView.setText(R.string.scanning_fingerprint);
|
|
||||||
// listen for decryption by default
|
|
||||||
toggleMode(Cipher.DECRYPT_MODE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleEncryptedResult(
|
|
||||||
final String value,
|
|
||||||
final String ivSpec) {
|
|
||||||
|
|
||||||
prefsNoBackup.edit()
|
|
||||||
.putString(getPreferenceKeyValue(), value)
|
|
||||||
.putString(getPreferenceKeyIvSpec(), ivSpec)
|
|
||||||
.apply();
|
|
||||||
// and remove visual input to reset UI
|
|
||||||
confirmButtonView.performClick();
|
|
||||||
fingerprintTextView.setText(R.string.encrypted_value_stored);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleDecryptedResult(final String value) {
|
|
||||||
// on decrypt enter it for the purchase/login action
|
|
||||||
passwordView.setText(value);
|
|
||||||
confirmButtonView.performClick();
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
@Override
|
|
||||||
public void onInvalidKeyException() {
|
|
||||||
Toast.makeText(this, R.string.fingerprint_invalid_key, Toast.LENGTH_SHORT).show();
|
|
||||||
checkAvailability(); // restarts listening
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
@Override
|
|
||||||
public void onFingerprintException(Exception e) {
|
|
||||||
//Toast.makeText(this, R.string.fingerprint_error, Toast.LENGTH_SHORT).show();
|
|
||||||
checkAvailability();
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DefaultCheckChange implements CompoundButton.OnCheckedChangeListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCheckedChanged(
|
|
||||||
CompoundButton buttonView,
|
|
||||||
boolean isChecked) {
|
|
||||||
|
|
||||||
String newDefaultFileName;
|
|
||||||
|
|
||||||
if (isChecked) {
|
|
||||||
newDefaultFileName = mDbUri.toString();
|
|
||||||
} else {
|
|
||||||
newDefaultFileName = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
|
||||||
editor.putString(KEY_DEFAULT_FILENAME, newDefaultFileName);
|
|
||||||
EditorCompat.apply(editor);
|
|
||||||
|
|
||||||
BackupManagerCompat backupManager = new BackupManagerCompat(PasswordActivity.this);
|
|
||||||
backupManager.dataChanged();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OkClickHandler implements View.OnClickListener {
|
|
||||||
|
|
||||||
public void onClick(View view) {
|
|
||||||
String pass = passwordView.getText().toString();
|
|
||||||
String key = keyFileView.getText().toString();
|
|
||||||
loadDatabase(pass, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadDatabase(
|
|
||||||
String pass,
|
|
||||||
String keyfile) {
|
|
||||||
loadDatabase(pass, UriUtil.parseDefaultFile(keyfile));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadDatabase(
|
|
||||||
String pass,
|
|
||||||
Uri keyfile) {
|
|
||||||
|
|
||||||
// Clear before we load
|
|
||||||
Database db = App.getDB();
|
|
||||||
db.clear();
|
|
||||||
|
|
||||||
// Clear the shutdown flag
|
|
||||||
App.clearShutdown();
|
|
||||||
|
|
||||||
if (!checkboxPasswordView.isChecked()) {
|
|
||||||
pass = "";
|
|
||||||
}
|
|
||||||
if (!checkboxKeyfileView.isChecked()) {
|
|
||||||
keyfile = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handler handler = new Handler();
|
|
||||||
LoadDB task = new LoadDB(db, PasswordActivity.this, mDbUri, pass, keyfile, new AfterLoad(handler, db));
|
|
||||||
ProgressTask pt = new ProgressTask(PasswordActivity.this, task, R.string.loading_database);
|
|
||||||
pt.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getEditText(int resId) {
|
|
||||||
return Util.getEditText(this, resId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
super.onCreateOptionsMenu(menu);
|
|
||||||
MenuUtil.defaultMenuInflater(getMenuInflater(), menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case android.R.id.home:
|
|
||||||
finish();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return MenuUtil.onDefaultMenuOptionsItemSelected(this, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class AfterLoad extends OnFinish {
|
|
||||||
|
|
||||||
private Database db;
|
|
||||||
|
|
||||||
AfterLoad(
|
|
||||||
Handler handler,
|
|
||||||
Database db) {
|
|
||||||
super(handler);
|
|
||||||
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (db.passwordEncodingError) {
|
|
||||||
PasswordEncodingDialogHelper dialog = new PasswordEncodingDialogHelper();
|
|
||||||
dialog.show(PasswordActivity.this, new OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(
|
|
||||||
DialogInterface dialog,
|
|
||||||
int which) {
|
|
||||||
GroupActivity.Launch(PasswordActivity.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
} else if (mSuccess) {
|
|
||||||
GroupActivity.Launch(PasswordActivity.this);
|
|
||||||
} else {
|
|
||||||
displayMessage(PasswordActivity.this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class InitTask extends AsyncTask<Intent, Void, Integer> {
|
|
||||||
|
|
||||||
String password = "";
|
|
||||||
boolean launch_immediately = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Integer doInBackground(Intent... args) {
|
|
||||||
Intent i = args[0];
|
|
||||||
String action = i.getAction();
|
|
||||||
if (action != null && action.equals(VIEW_INTENT)) {
|
|
||||||
Uri incoming = i.getData();
|
|
||||||
mDbUri = incoming;
|
|
||||||
|
|
||||||
mKeyUri = ClipDataCompat.getUriFromIntent(i, KEY_KEYFILE);
|
|
||||||
|
|
||||||
if (incoming == null) {
|
|
||||||
return R.string.error_can_not_handle_uri;
|
|
||||||
} else if (incoming.getScheme().equals("file")) {
|
|
||||||
String fileName = incoming.getPath();
|
|
||||||
|
|
||||||
if (fileName.length() == 0) {
|
|
||||||
// No file name
|
|
||||||
return R.string.FileNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
File dbFile = new File(fileName);
|
|
||||||
if (!dbFile.exists()) {
|
|
||||||
// File does not exist
|
|
||||||
return R.string.FileNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mKeyUri == null) {
|
|
||||||
mKeyUri = getKeyFile(mDbUri);
|
|
||||||
}
|
|
||||||
} else if (incoming.getScheme().equals("content")) {
|
|
||||||
if (mKeyUri == null) {
|
|
||||||
mKeyUri = getKeyFile(mDbUri);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return R.string.error_can_not_handle_uri;
|
|
||||||
}
|
|
||||||
password = i.getStringExtra(KEY_PASSWORD);
|
|
||||||
launch_immediately = i.getBooleanExtra(KEY_LAUNCH_IMMEDIATELY, false);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
mDbUri = UriUtil.parseDefaultFile(i.getStringExtra(KEY_FILENAME));
|
|
||||||
mKeyUri = UriUtil.parseDefaultFile(i.getStringExtra(KEY_KEYFILE));
|
|
||||||
password = i.getStringExtra(KEY_PASSWORD);
|
|
||||||
launch_immediately = i.getBooleanExtra(KEY_LAUNCH_IMMEDIATELY, false);
|
|
||||||
|
|
||||||
if (mKeyUri == null || mKeyUri.toString().length() == 0) {
|
|
||||||
mKeyUri = getKeyFile(mDbUri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPostExecute(Integer result) {
|
|
||||||
if (result != null) {
|
|
||||||
Toast.makeText(PasswordActivity.this, result, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
populateView();
|
|
||||||
|
|
||||||
Button confirmButton = (Button) findViewById(R.id.pass_ok);
|
|
||||||
confirmButton.setOnClickListener(new OkClickHandler());
|
|
||||||
|
|
||||||
if (password != null) {
|
|
||||||
passwordView.setText(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompoundButton defaultCheck = (CompoundButton) findViewById(R.id.default_database);
|
|
||||||
defaultCheck.setOnCheckedChangeListener(new DefaultCheckChange());
|
|
||||||
|
|
||||||
View browseView = findViewById(R.id.browse_button);
|
|
||||||
keyFileHelper = new KeyFileHelper(PasswordActivity.this);
|
|
||||||
browseView.setOnClickListener(keyFileHelper.getOpenFileOnClickViewListener());
|
|
||||||
|
|
||||||
retrieveSettings();
|
|
||||||
|
|
||||||
if (launch_immediately) {
|
|
||||||
loadDatabase(password, mKeyUri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,39 +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());
|
|
||||||
builder.setMessage(getString(R.string.unavailable_feature_text, Build.VERSION.SDK_INT, minVersionRequired))
|
|
||||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) { }
|
|
||||||
});
|
|
||||||
return builder.create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.keepassdroid;
|
package com.keepassdroid.activities;
|
||||||
|
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
@@ -27,8 +27,10 @@ import android.util.Log;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R;
|
|
||||||
import com.keepassdroid.stylish.StylishActivity;
|
import com.keepassdroid.stylish.StylishActivity;
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
public class AboutActivity extends StylishActivity {
|
public class AboutActivity extends StylishActivity {
|
||||||
|
|
||||||
@@ -56,6 +58,9 @@ public class AboutActivity extends StylishActivity {
|
|||||||
version = getString(R.string.version_label) + " " + version;
|
version = getString(R.string.version_label) + " " + version;
|
||||||
TextView versionText = (TextView) findViewById(R.id.activity_about_version);
|
TextView versionText = (TextView) findViewById(R.id.activity_about_version);
|
||||||
versionText.setText(version);
|
versionText.setText(version);
|
||||||
|
|
||||||
|
TextView disclaimerText = (TextView) findViewById(R.id.disclaimer);
|
||||||
|
disclaimerText.setText(getString(R.string.disclaimer_formal, new DateTime().getYear()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
375
app/src/main/java/com/keepassdroid/activities/EntryActivity.java
Normal file
375
app/src/main/java/com/keepassdroid/activities/EntryActivity.java
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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.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.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.keepassdroid.app.App;
|
||||||
|
import com.keepassdroid.database.Database;
|
||||||
|
import com.keepassdroid.database.PwDatabase;
|
||||||
|
import com.keepassdroid.database.PwEntry;
|
||||||
|
import com.keepassdroid.notifications.NotificationCopyingService;
|
||||||
|
import com.keepassdroid.notifications.NotificationField;
|
||||||
|
import com.keepassdroid.settings.PreferencesUtil;
|
||||||
|
import com.keepassdroid.timeout.ClipboardHelper;
|
||||||
|
import com.keepassdroid.utils.EmptyUtils;
|
||||||
|
import com.keepassdroid.utils.MenuUtil;
|
||||||
|
import com.keepassdroid.utils.Types;
|
||||||
|
import com.keepassdroid.utils.Util;
|
||||||
|
import com.keepassdroid.view.EntryContentsView;
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static com.keepassdroid.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.Loaded() ) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
readOnly = db.readOnly;
|
||||||
|
|
||||||
|
mShowPassword = !PreferencesUtil.isPasswordMask(this);
|
||||||
|
|
||||||
|
// Get Entry from UUID
|
||||||
|
Intent i = getIntent();
|
||||||
|
UUID uuid = Types.bytestoUUID(i.getByteArrayExtra(KEY_ENTRY));
|
||||||
|
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
|
||||||
|
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));
|
||||||
|
|
||||||
|
// Setup Edit Buttons
|
||||||
|
View edit = findViewById(R.id.entry_edit);
|
||||||
|
edit.setOnClickListener(v -> EntryEditActivity.Launch(EntryActivity.this, mEntry));
|
||||||
|
if (readOnly) {
|
||||||
|
edit.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// TODO Start decode
|
||||||
|
|
||||||
|
// 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.containsExtraFields()) {
|
||||||
|
// 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 {
|
||||||
|
int anonymousFieldNumber = 0;
|
||||||
|
for (Map.Entry<String, String> entry : mEntry.getExtraFields().entrySet()) {
|
||||||
|
notificationFields.add(
|
||||||
|
new NotificationField(
|
||||||
|
NotificationField.NotificationFieldId.getAnonymousFieldId()[anonymousFieldNumber],
|
||||||
|
entry.getValue(),
|
||||||
|
entry.getKey(),
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
// TODO end decode
|
||||||
|
}
|
||||||
|
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.pm;
|
||||||
|
|
||||||
|
mEntry.startToDecodeReference(pm);
|
||||||
|
|
||||||
|
// Assign title
|
||||||
|
populateTitle(db.drawFactory.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();
|
||||||
|
for (Map.Entry<String, String> field : mEntry.getExtraFields().entrySet()) {
|
||||||
|
final String label = field.getKey();
|
||||||
|
final String value = field.getValue();
|
||||||
|
entryContentsView.addExtraField(label, value, view ->
|
||||||
|
clipboardHelper.timeoutCopyToClipboard(value, 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.expires() ) {
|
||||||
|
entryContentsView.assignExpiresDate(expires);
|
||||||
|
} else {
|
||||||
|
entryContentsView.assignExpiresDate(getString(R.string.never));
|
||||||
|
}
|
||||||
|
|
||||||
|
mEntry.endToDecodeReference(pm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
MenuItem togglePassword = menu.findItem(R.id.menu_toggle_pass);
|
||||||
|
if (entryContentsView != null && togglePassword != null) {
|
||||||
|
if (!entryContentsView.isPasswordPresent()) {
|
||||||
|
togglePassword.setVisible(false);
|
||||||
|
} else {
|
||||||
|
changeShowPasswordIcon(togglePassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,397 @@
|
|||||||
|
/*
|
||||||
|
* 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.activities;
|
||||||
|
|
||||||
|
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.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.keepassdroid.app.App;
|
||||||
|
import com.keepassdroid.database.Database;
|
||||||
|
import com.keepassdroid.database.PwDatabase;
|
||||||
|
import com.keepassdroid.database.PwDate;
|
||||||
|
import com.keepassdroid.database.PwEntry;
|
||||||
|
import com.keepassdroid.database.PwGroup;
|
||||||
|
import com.keepassdroid.database.PwGroupId;
|
||||||
|
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.database.security.ProtectedString;
|
||||||
|
import com.keepassdroid.dialogs.GeneratePasswordDialogFragment;
|
||||||
|
import com.keepassdroid.dialogs.IconPickerDialogFragment;
|
||||||
|
import com.keepassdroid.icons.Icons;
|
||||||
|
import com.keepassdroid.settings.PreferencesUtil;
|
||||||
|
import com.keepassdroid.tasks.ProgressTask;
|
||||||
|
import com.keepassdroid.utils.MenuUtil;
|
||||||
|
import com.keepassdroid.utils.Types;
|
||||||
|
import com.keepassdroid.utils.Util;
|
||||||
|
import com.keepassdroid.view.EntryEditNewField;
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class EntryEditActivity extends LockingHideActivity
|
||||||
|
implements IconPickerDialogFragment.IconPickerListener,
|
||||||
|
GeneratePasswordDialogFragment.GeneratePasswordListener {
|
||||||
|
|
||||||
|
// Keys for current Activity
|
||||||
|
public static final String KEY_ENTRY = "entry";
|
||||||
|
public static final String KEY_PARENT = "parent";
|
||||||
|
|
||||||
|
// Keys for callback
|
||||||
|
public static final int ADD_ENTRY_RESULT_CODE = 31;
|
||||||
|
public static final int UPDATE_ENTRY_RESULT_CODE = 32;
|
||||||
|
public static final int ADD_OR_UPDATE_ENTRY_REQUEST_CODE = 7129;
|
||||||
|
public static final String ADD_OR_UPDATE_ENTRY_KEY = "ADD_OR_UPDATE_ENTRY_KEY";
|
||||||
|
|
||||||
|
protected PwEntry mEntry;
|
||||||
|
protected PwEntry mCallbackNewEntry;
|
||||||
|
protected boolean mIsNew;
|
||||||
|
protected int mSelectedIconID = -1;
|
||||||
|
|
||||||
|
// Views
|
||||||
|
private ScrollView scrollView;
|
||||||
|
private EditText entryTitleView;
|
||||||
|
private EditText entryUserNameView;
|
||||||
|
private EditText entryUrlView;
|
||||||
|
private EditText entryPasswordView;
|
||||||
|
private EditText entryConfirmationPasswordView;
|
||||||
|
private EditText entryCommentView;
|
||||||
|
private ViewGroup entryExtraFieldsContainer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* launch EntryEditActivity to update an existing entry
|
||||||
|
* @param act from activity
|
||||||
|
* @param pw Entry to update
|
||||||
|
*/
|
||||||
|
public static void Launch(Activity act, PwEntry pw) {
|
||||||
|
if (LockingActivity.checkTimeIsAllowedOrFinish(act)) {
|
||||||
|
Intent intent = new Intent(act, EntryEditActivity.class);
|
||||||
|
intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID()));
|
||||||
|
act.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* launch EntryEditActivity to add a new entry
|
||||||
|
* @param act from activity
|
||||||
|
* @param pwGroup Group who will contains new entry
|
||||||
|
*/
|
||||||
|
public static void Launch(Activity act, PwGroup pwGroup) {
|
||||||
|
if (LockingActivity.checkTimeIsAllowedOrFinish(act)) {
|
||||||
|
Intent intent = new Intent(act, EntryEditActivity.class);
|
||||||
|
intent.putExtra(KEY_PARENT, pwGroup.getId());
|
||||||
|
act.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.entry_edit);
|
||||||
|
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
toolbar.setTitle(getString(R.string.app_name));
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
assert getSupportActionBar() != null;
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
|
||||||
|
scrollView = findViewById(R.id.entry_scroll);
|
||||||
|
scrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
|
||||||
|
|
||||||
|
entryTitleView = findViewById(R.id.entry_title);
|
||||||
|
entryUserNameView = findViewById(R.id.entry_user_name);
|
||||||
|
entryUrlView = findViewById(R.id.entry_url);
|
||||||
|
entryPasswordView = findViewById(R.id.entry_password);
|
||||||
|
entryConfirmationPasswordView = findViewById(R.id.entry_confpassword);
|
||||||
|
entryCommentView = findViewById(R.id.entry_comment);
|
||||||
|
entryExtraFieldsContainer = findViewById(R.id.advanced_container);
|
||||||
|
|
||||||
|
// Likely the app has been killed exit the activity
|
||||||
|
Database db = App.getDB();
|
||||||
|
if ( ! db.Loaded() ) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
byte[] uuidBytes = intent.getByteArrayExtra(KEY_ENTRY);
|
||||||
|
|
||||||
|
PwDatabase pm = db.pm;
|
||||||
|
if ( uuidBytes == null ) {
|
||||||
|
PwGroupId parentId = (PwGroupId) intent.getSerializableExtra(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 iconButton = findViewById(R.id.icon_button);
|
||||||
|
iconButton.setOnClickListener(v ->
|
||||||
|
IconPickerDialogFragment.launch(EntryEditActivity.this));
|
||||||
|
|
||||||
|
// Generate password button
|
||||||
|
View generatePassword = findViewById(R.id.generate_button);
|
||||||
|
generatePassword.setOnClickListener(v -> {
|
||||||
|
GeneratePasswordDialogFragment generatePasswordDialogFragment = new GeneratePasswordDialogFragment();
|
||||||
|
generatePasswordDialogFragment.show(getSupportFragmentManager(), "PasswordGeneratorFragment");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save button
|
||||||
|
View save = findViewById(R.id.entry_save);
|
||||||
|
save.setOnClickListener(v -> {
|
||||||
|
if (!validateBeforeSaving()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mCallbackNewEntry = populateNewEntry();
|
||||||
|
|
||||||
|
OnFinish onFinish = new AfterSave();
|
||||||
|
EntryEditActivity act = EntryEditActivity.this;
|
||||||
|
RunnableOnFinish task;
|
||||||
|
if ( mIsNew ) {
|
||||||
|
task = new AddEntry(act, App.getDB(), mCallbackNewEntry, onFinish);
|
||||||
|
} else {
|
||||||
|
task = new UpdateEntry(act, App.getDB(), mEntry, mCallbackNewEntry, onFinish);
|
||||||
|
}
|
||||||
|
ProgressTask pt = new ProgressTask(act, task, R.string.saving_database);
|
||||||
|
pt.run();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (mEntry.allowExtraFields()) {
|
||||||
|
View add = findViewById(R.id.add_new_field);
|
||||||
|
add.setVisibility(View.VISIBLE);
|
||||||
|
add.setOnClickListener(v -> {
|
||||||
|
EntryEditNewField ees = new EntryEditNewField(EntryEditActivity.this);
|
||||||
|
ees.setData("", new ProtectedString(false, ""));
|
||||||
|
entryExtraFieldsContainer.addView(ees);
|
||||||
|
|
||||||
|
// Scroll bottom
|
||||||
|
scrollView.post(() -> scrollView.fullScroll(ScrollView.FOCUS_DOWN));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean validateBeforeSaving() {
|
||||||
|
// Require title
|
||||||
|
String title = entryTitleView.getText().toString();
|
||||||
|
if ( title.length() == 0 ) {
|
||||||
|
Toast.makeText(this, R.string.error_title_required, Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate password
|
||||||
|
String pass = entryPasswordView.getText().toString();
|
||||||
|
String conf = entryConfirmationPasswordView.getText().toString();
|
||||||
|
if ( ! pass.equals(conf) ) {
|
||||||
|
Toast.makeText(this, R.string.error_pass_match, Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate extra fields
|
||||||
|
if (mEntry.allowExtraFields()) {
|
||||||
|
for (int i = 0; i < entryExtraFieldsContainer.getChildCount(); i++) {
|
||||||
|
EntryEditNewField entryEditNewField = (EntryEditNewField) entryExtraFieldsContainer.getChildAt(i);
|
||||||
|
String key = entryEditNewField.getLabel();
|
||||||
|
if (key == null || key.length() == 0) {
|
||||||
|
Toast.makeText(this, R.string.error_string_key, Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PwEntry populateNewEntry() {
|
||||||
|
PwDatabase db = App.getDB().pm;
|
||||||
|
|
||||||
|
PwEntry newEntry = mEntry.clone();
|
||||||
|
|
||||||
|
newEntry.startToDecodeReference(db);
|
||||||
|
|
||||||
|
newEntry.createBackup(db);
|
||||||
|
|
||||||
|
newEntry.setLastAccessTime(new PwDate());
|
||||||
|
newEntry.setLastModificationTime(new PwDate());
|
||||||
|
|
||||||
|
newEntry.setTitle(entryTitleView.getText().toString());
|
||||||
|
if(mSelectedIconID != -1)
|
||||||
|
// or TODO icon factory newEntry.setIcon(App.getDB().pm.iconFactory.getIcon(mSelectedIconID));
|
||||||
|
newEntry.setIcon(new PwIconStandard(mSelectedIconID));
|
||||||
|
else {
|
||||||
|
if (mIsNew) {
|
||||||
|
newEntry.setIcon(App.getDB().pm.iconFactory.getIcon(0));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Keep previous icon, if no new one was selected
|
||||||
|
newEntry.setIcon(mEntry.getIconStandard());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newEntry.setUrl(entryUrlView.getText().toString());
|
||||||
|
newEntry.setUsername(entryUserNameView.getText().toString());
|
||||||
|
newEntry.setNotes(entryCommentView.getText().toString());
|
||||||
|
newEntry.setPassword(entryPasswordView.getText().toString());
|
||||||
|
|
||||||
|
if (newEntry.allowExtraFields()) {
|
||||||
|
// Delete all new standard strings
|
||||||
|
newEntry.removeExtraFields();
|
||||||
|
// Add extra fields from views
|
||||||
|
for (int i = 0; i < entryExtraFieldsContainer.getChildCount(); i++) {
|
||||||
|
EntryEditNewField view = (EntryEditNewField) entryExtraFieldsContainer.getChildAt(i);
|
||||||
|
String key = view.getLabel();
|
||||||
|
String value = view.getValue();
|
||||||
|
boolean protect = view.isProtected();
|
||||||
|
newEntry.addField(key, new ProtectedString(protect, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newEntry.endToDecodeReference(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 = findViewById(R.id.icon_button);
|
||||||
|
App.getDB().drawFactory.assignDrawableTo(currIconButton, getResources(), mEntry.getIcon());
|
||||||
|
|
||||||
|
entryTitleView.setText(mEntry.getTitle());
|
||||||
|
entryUserNameView.setText(mEntry.getUsername());
|
||||||
|
entryUrlView.setText(mEntry.getUrl());
|
||||||
|
String password = mEntry.getPassword();
|
||||||
|
entryPasswordView.setText(password);
|
||||||
|
entryConfirmationPasswordView.setText(password);
|
||||||
|
entryCommentView.setText(mEntry.getNotes());
|
||||||
|
|
||||||
|
boolean visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this);
|
||||||
|
if (visibilityFontActivated) {
|
||||||
|
Util.applyFontVisibilityTo(entryUserNameView);
|
||||||
|
Util.applyFontVisibilityTo(entryPasswordView);
|
||||||
|
Util.applyFontVisibilityTo(entryConfirmationPasswordView);
|
||||||
|
Util.applyFontVisibilityTo(entryCommentView);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mEntry.allowExtraFields()) {
|
||||||
|
LinearLayout container = findViewById(R.id.advanced_container);
|
||||||
|
for (Map.Entry<String, ProtectedString> pair : mEntry.getExtraProtectedFields().entrySet()) {
|
||||||
|
EntryEditNewField entryEditNewField = new EntryEditNewField(EntryEditActivity.this);
|
||||||
|
entryEditNewField.setData(pair.getKey(), pair.getValue());
|
||||||
|
entryEditNewField.setFontVisibility(visibilityFontActivated);
|
||||||
|
container.addView(entryEditNewField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void iconPicked(Bundle bundle) {
|
||||||
|
mSelectedIconID = bundle.getInt(IconPickerDialogFragment.KEY_ICON_ID);
|
||||||
|
ImageButton currIconButton = findViewById(R.id.icon_button);
|
||||||
|
currIconButton.setImageResource(Icons.iconToResId(mSelectedIconID));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptPassword(Bundle bundle) {
|
||||||
|
String generatedPassword = bundle.getString(GeneratePasswordDialogFragment.KEY_PASSWORD_ID);
|
||||||
|
entryPasswordView.setText(generatedPassword);
|
||||||
|
entryConfirmationPasswordView.setText(generatedPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelPassword(Bundle bundle) {
|
||||||
|
// Do nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
// Assign entry callback as a result in all case
|
||||||
|
if (mCallbackNewEntry != null) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
Intent intentEntry = new Intent();
|
||||||
|
bundle.putSerializable(ADD_OR_UPDATE_ENTRY_KEY, mCallbackNewEntry);
|
||||||
|
intentEntry.putExtras(bundle);
|
||||||
|
if (mIsNew) {
|
||||||
|
setResult(ADD_ENTRY_RESULT_CODE, intentEntry);
|
||||||
|
} else {
|
||||||
|
setResult(UPDATE_ENTRY_RESULT_CODE, intentEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class AfterSave extends OnFinish {
|
||||||
|
|
||||||
|
AfterSave() {
|
||||||
|
super(new Handler());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if ( mSuccess ) {
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
displayMessage(EntryEditActivity.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
442
app/src/main/java/com/keepassdroid/activities/GroupActivity.java
Normal file
442
app/src/main/java/com/keepassdroid/activities/GroupActivity.java
Normal file
@@ -0,0 +1,442 @@
|
|||||||
|
/*
|
||||||
|
* 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.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.app.SearchManager;
|
||||||
|
import android.app.assist.AssistStructure;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.SearchView;
|
||||||
|
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 com.keepassdroid.adapters.NodeAdapter;
|
||||||
|
import com.keepassdroid.app.App;
|
||||||
|
import com.keepassdroid.autofill.AutofillHelper;
|
||||||
|
import com.keepassdroid.database.Database;
|
||||||
|
import com.keepassdroid.database.PwEntry;
|
||||||
|
import com.keepassdroid.database.PwGroup;
|
||||||
|
import com.keepassdroid.database.PwGroupId;
|
||||||
|
import com.keepassdroid.database.PwNode;
|
||||||
|
import com.keepassdroid.database.SortNodeEnum;
|
||||||
|
import com.keepassdroid.database.edit.AddGroup;
|
||||||
|
import com.keepassdroid.database.edit.DeleteEntry;
|
||||||
|
import com.keepassdroid.database.edit.DeleteGroup;
|
||||||
|
import com.keepassdroid.dialogs.AssignMasterKeyDialogFragment;
|
||||||
|
import com.keepassdroid.dialogs.GroupEditDialogFragment;
|
||||||
|
import com.keepassdroid.dialogs.IconPickerDialogFragment;
|
||||||
|
import com.keepassdroid.dialogs.ReadOnlyDialog;
|
||||||
|
import com.keepassdroid.search.SearchResultsActivity;
|
||||||
|
import com.keepassdroid.tasks.ProgressTask;
|
||||||
|
import com.keepassdroid.view.AddNodeButtonView;
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
public class GroupActivity extends ListNodesActivity
|
||||||
|
implements GroupEditDialogFragment.EditGroupListener, IconPickerDialogFragment.IconPickerListener {
|
||||||
|
|
||||||
|
private static final String GROUP_ID_KEY = "GROUP_ID_KEY";
|
||||||
|
|
||||||
|
private AddNodeButtonView addNodeButtonView;
|
||||||
|
|
||||||
|
protected boolean addGroupEnabled = false;
|
||||||
|
protected boolean addEntryEnabled = false;
|
||||||
|
protected boolean isRoot = false;
|
||||||
|
protected boolean readOnly = false;
|
||||||
|
protected EditGroupDialogAction editGroupDialogAction = EditGroupDialogAction.NONE;
|
||||||
|
|
||||||
|
private AutofillHelper autofillHelper;
|
||||||
|
|
||||||
|
private enum EditGroupDialogAction {
|
||||||
|
CREATION, UPDATE, NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TAG = "Group Activity:";
|
||||||
|
|
||||||
|
public static void launch(Activity act) {
|
||||||
|
LockingActivity.recordFirstTimeBeforeLaunch(act);
|
||||||
|
launch(act, (PwGroup) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void launch(Activity act, PwGroup group) {
|
||||||
|
if (LockingActivity.checkTimeIsAllowedOrFinish(act)) {
|
||||||
|
Intent intent = new Intent(act, GroupActivity.class);
|
||||||
|
if (group != null) {
|
||||||
|
intent.putExtra(GROUP_ID_KEY, group.getId());
|
||||||
|
}
|
||||||
|
act.startActivityForResult(intent, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
public static void launch(Activity act, AssistStructure assistStructure) {
|
||||||
|
if ( assistStructure != null ) {
|
||||||
|
LockingActivity.recordFirstTimeBeforeLaunch(act);
|
||||||
|
launch(act, null, assistStructure);
|
||||||
|
} else {
|
||||||
|
launch(act);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
public static void launch(Activity act, PwGroup group, AssistStructure assistStructure) {
|
||||||
|
if ( assistStructure != null ) {
|
||||||
|
if (LockingActivity.checkTimeIsAllowedOrFinish(act)) {
|
||||||
|
Intent intent = new Intent(act, GroupActivity.class);
|
||||||
|
if (group != null) {
|
||||||
|
intent.putExtra(GROUP_ID_KEY, group.getId());
|
||||||
|
}
|
||||||
|
AutofillHelper.addAssistStructureExtraInIntent(intent, assistStructure);
|
||||||
|
act.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
launch(act, group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Log.w(TAG, "Retrieved tree");
|
||||||
|
if ( mCurrentGroup == null ) {
|
||||||
|
Log.w(TAG, "Group was null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct main view
|
||||||
|
setContentView(getLayoutInflater().inflate(R.layout.list_nodes_with_add_button, null));
|
||||||
|
|
||||||
|
addNodeButtonView = findViewById(R.id.add_node_button);
|
||||||
|
addNodeButtonView.enableAddGroup(addGroupEnabled);
|
||||||
|
addNodeButtonView.enableAddEntry(addEntryEnabled);
|
||||||
|
// Hide when scroll
|
||||||
|
RecyclerView recyclerView = findViewById(R.id.nodes_list);
|
||||||
|
recyclerView.addOnScrollListener(addNodeButtonView.hideButtonOnScrollListener());
|
||||||
|
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
toolbar.setTitle("");
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
if ( mCurrentGroup.getParent() != null )
|
||||||
|
toolbar.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp);
|
||||||
|
|
||||||
|
addNodeButtonView.setAddGroupClickListener(v -> {
|
||||||
|
editGroupDialogAction = EditGroupDialogAction.CREATION;
|
||||||
|
GroupEditDialogFragment groupEditDialogFragment = new GroupEditDialogFragment();
|
||||||
|
groupEditDialogFragment.show(getSupportFragmentManager(),
|
||||||
|
GroupEditDialogFragment.TAG_CREATE_GROUP);
|
||||||
|
});
|
||||||
|
addNodeButtonView.setAddEntryClickListener(v ->
|
||||||
|
EntryEditActivity.Launch(GroupActivity.this, mCurrentGroup));
|
||||||
|
|
||||||
|
setGroupTitle();
|
||||||
|
setGroupIcon();
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
autofillHelper = new AutofillHelper();
|
||||||
|
autofillHelper.retrieveAssistStructure(getIntent());
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.w(TAG, "Finished creating tree");
|
||||||
|
|
||||||
|
if (isRoot) {
|
||||||
|
showWarnings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PwGroup initCurrentGroup() {
|
||||||
|
PwGroup currentGroup;
|
||||||
|
Database db = App.getDB();
|
||||||
|
readOnly = db.readOnly;
|
||||||
|
PwGroup root = db.pm.rootGroup;
|
||||||
|
|
||||||
|
Log.w(TAG, "Creating tree view");
|
||||||
|
PwGroupId pwGroupId = (PwGroupId) getIntent().getSerializableExtra(GROUP_ID_KEY);
|
||||||
|
if ( pwGroupId == null ) {
|
||||||
|
currentGroup = root;
|
||||||
|
} else {
|
||||||
|
currentGroup = db.pm.groups.get(pwGroupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentGroup != null) {
|
||||||
|
addGroupEnabled = !readOnly;
|
||||||
|
addEntryEnabled = !readOnly; // TODO ReadOnly
|
||||||
|
isRoot = (currentGroup == root);
|
||||||
|
if (!currentGroup.allowAddEntryIfIsRoot())
|
||||||
|
addEntryEnabled = !isRoot && addEntryEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView defineNodeList() {
|
||||||
|
return (RecyclerView) findViewById(R.id.nodes_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNodeClick(PwNode node) {
|
||||||
|
// Add event when we have Autofill
|
||||||
|
AssistStructure assistStructure = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
assistStructure = autofillHelper.getAssistStructure();
|
||||||
|
if (assistStructure != null) {
|
||||||
|
mAdapter.registerANodeToUpdate(node);
|
||||||
|
switch (node.getType()) {
|
||||||
|
case GROUP:
|
||||||
|
GroupActivity.launch(this, (PwGroup) node, assistStructure);
|
||||||
|
break;
|
||||||
|
case ENTRY:
|
||||||
|
// Build response with the entry selected
|
||||||
|
autofillHelper.buildResponseWhenEntrySelected(this, (PwEntry) node);
|
||||||
|
finish();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( assistStructure == null ){
|
||||||
|
super.onNodeClick(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addOptionsToAdapter(NodeAdapter nodeAdapter) {
|
||||||
|
super.addOptionsToAdapter(nodeAdapter);
|
||||||
|
|
||||||
|
nodeAdapter.setActivateContextMenu(true);
|
||||||
|
nodeAdapter.setNodeMenuListener(new NodeAdapter.NodeMenuListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onOpenMenuClick(PwNode node) {
|
||||||
|
mAdapter.registerANodeToUpdate(node);
|
||||||
|
switch (node.getType()) {
|
||||||
|
case GROUP:
|
||||||
|
GroupActivity.launch(GroupActivity.this, (PwGroup) node);
|
||||||
|
break;
|
||||||
|
case ENTRY:
|
||||||
|
EntryActivity.launch(GroupActivity.this, (PwEntry) node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onEditMenuClick(PwNode node) {
|
||||||
|
mAdapter.registerANodeToUpdate(node);
|
||||||
|
switch (node.getType()) {
|
||||||
|
case GROUP:
|
||||||
|
editGroupDialogAction = EditGroupDialogAction.UPDATE;
|
||||||
|
GroupEditDialogFragment groupEditDialogFragment =
|
||||||
|
GroupEditDialogFragment.build(node);
|
||||||
|
groupEditDialogFragment.show(getSupportFragmentManager(),
|
||||||
|
GroupEditDialogFragment.TAG_CREATE_GROUP);
|
||||||
|
break;
|
||||||
|
case ENTRY:
|
||||||
|
EntryEditActivity.Launch(GroupActivity.this, (PwEntry) node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onDeleteMenuClick(PwNode node) {
|
||||||
|
switch (node.getType()) {
|
||||||
|
case GROUP:
|
||||||
|
deleteGroup((PwGroup) node);
|
||||||
|
break;
|
||||||
|
case ENTRY:
|
||||||
|
deleteEntry((PwEntry) node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
// Show button on resume
|
||||||
|
addNodeButtonView.showButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
// Hide button
|
||||||
|
addNodeButtonView.hideButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) {
|
||||||
|
super.onSortSelected(sortNodeEnum, ascending, groupsBefore, recycleBinBottom);
|
||||||
|
// Show button if hide after sort
|
||||||
|
addNodeButtonView.showButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setGroupIcon() {
|
||||||
|
if (mCurrentGroup != null) {
|
||||||
|
ImageView iv = (ImageView) findViewById(R.id.icon);
|
||||||
|
App.getDB().drawFactory.assignDrawableTo(iv, getResources(), mCurrentGroup.getIcon());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteEntry(PwEntry entry) {
|
||||||
|
Handler handler = new Handler();
|
||||||
|
DeleteEntry task = new DeleteEntry(this, App.getDB(), entry,
|
||||||
|
new AfterDeleteNode(handler, entry));
|
||||||
|
ProgressTask pt = new ProgressTask(this, task, R.string.saving_database);
|
||||||
|
pt.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteGroup(PwGroup group) {
|
||||||
|
//TODO Verify trash recycle bin
|
||||||
|
Handler handler = new Handler();
|
||||||
|
DeleteGroup task = new DeleteGroup(this, App.getDB(), group,
|
||||||
|
new AfterDeleteNode(handler, group));
|
||||||
|
ProgressTask pt = new ProgressTask(this, task, R.string.saving_database);
|
||||||
|
pt.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
super.onCreateOptionsMenu(menu);
|
||||||
|
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.search, menu);
|
||||||
|
inflater.inflate(R.menu.database_master_key, menu);
|
||||||
|
inflater.inflate(R.menu.database_lock, 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) {
|
||||||
|
// TODO Flickering when locking, will be better with content provider
|
||||||
|
searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, SearchResultsActivity.class)));
|
||||||
|
searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
|
||||||
|
case android.R.id.home:
|
||||||
|
onBackPressed();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.menu_search:
|
||||||
|
onSearchRequested();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.menu_lock:
|
||||||
|
lockAndExit();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.menu_change_master_key:
|
||||||
|
setPassword();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPassword() {
|
||||||
|
AssignMasterKeyDialogFragment dialog = new AssignMasterKeyDialogFragment();
|
||||||
|
dialog.show(getSupportFragmentManager(), "passwordDialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void approveEditGroup(Bundle bundle) {
|
||||||
|
String GroupName = bundle.getString(GroupEditDialogFragment.KEY_NAME);
|
||||||
|
int GroupIconID = bundle.getInt(GroupEditDialogFragment.KEY_ICON_ID);
|
||||||
|
switch (editGroupDialogAction) {
|
||||||
|
case CREATION:
|
||||||
|
// If edit group creation
|
||||||
|
Handler handler = new Handler();
|
||||||
|
AddGroup task = new AddGroup(this, App.getDB(), GroupName, GroupIconID, mCurrentGroup,
|
||||||
|
new AfterAddNode(handler), false);
|
||||||
|
ProgressTask pt = new ProgressTask(this, task, R.string.saving_database);
|
||||||
|
pt.run();
|
||||||
|
break;
|
||||||
|
case UPDATE:
|
||||||
|
// If edit group update
|
||||||
|
// TODO UpdateGroup
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
editGroupDialogAction = EditGroupDialogAction.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelEditGroup(Bundle bundle) {
|
||||||
|
// Do nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// For icon in create tree dialog
|
||||||
|
public void iconPicked(Bundle bundle) {
|
||||||
|
GroupEditDialogFragment groupEditDialogFragment =
|
||||||
|
(GroupEditDialogFragment) getSupportFragmentManager()
|
||||||
|
.findFragmentByTag(GroupEditDialogFragment.TAG_CREATE_GROUP);
|
||||||
|
if (groupEditDialogFragment != null) {
|
||||||
|
groupEditDialogFragment.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
/*
|
||||||
|
* 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.activities;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.Editor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.keepassdroid.adapters.NodeAdapter;
|
||||||
|
import com.keepassdroid.app.App;
|
||||||
|
import com.keepassdroid.compat.EditorCompat;
|
||||||
|
import com.keepassdroid.database.PwDatabase;
|
||||||
|
import com.keepassdroid.database.PwEntry;
|
||||||
|
import com.keepassdroid.database.PwGroup;
|
||||||
|
import com.keepassdroid.database.PwNode;
|
||||||
|
import com.keepassdroid.database.edit.AfterAddNodeOnFinish;
|
||||||
|
import com.keepassdroid.database.edit.OnFinish;
|
||||||
|
import com.keepassdroid.dialogs.AssignMasterKeyDialogFragment;
|
||||||
|
import com.keepassdroid.dialogs.SortDialogFragment;
|
||||||
|
import com.keepassdroid.settings.PreferencesUtil;
|
||||||
|
import com.keepassdroid.tasks.UIToastTask;
|
||||||
|
import com.keepassdroid.utils.MenuUtil;
|
||||||
|
import com.keepassdroid.database.SortNodeEnum;
|
||||||
|
import com.keepassdroid.password.AssignPasswordHelper;
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
public abstract class ListNodesActivity extends LockingActivity
|
||||||
|
implements AssignMasterKeyDialogFragment.AssignPasswordDialogListener,
|
||||||
|
NodeAdapter.OnNodeClickCallback,
|
||||||
|
SortDialogFragment.SortSelectionListener {
|
||||||
|
|
||||||
|
protected PwGroup mCurrentGroup;
|
||||||
|
protected NodeAdapter mAdapter;
|
||||||
|
|
||||||
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if ( isFinishing() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Likely the app has been killed exit the activity
|
||||||
|
if ( ! App.getDB().Loaded() ) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
|
invalidateOptionsMenu();
|
||||||
|
|
||||||
|
// TODO Move in search
|
||||||
|
setContentView(R.layout.list_nodes);
|
||||||
|
|
||||||
|
mCurrentGroup = initCurrentGroup();
|
||||||
|
|
||||||
|
mAdapter = new NodeAdapter(this);
|
||||||
|
addOptionsToAdapter(mAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract PwGroup initCurrentGroup();
|
||||||
|
|
||||||
|
protected abstract RecyclerView defineNodeList();
|
||||||
|
|
||||||
|
protected void addOptionsToAdapter(NodeAdapter nodeAdapter) {
|
||||||
|
mAdapter.setOnNodeClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
// Add elements to the list
|
||||||
|
mAdapter.rebuildList(mCurrentGroup);
|
||||||
|
assignListToNodeAdapter(defineNodeList());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setGroupTitle() {
|
||||||
|
if ( mCurrentGroup != null ) {
|
||||||
|
String name = mCurrentGroup.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 assignListToNodeAdapter(RecyclerView recyclerView) {
|
||||||
|
recyclerView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
|
||||||
|
// TODO mList.setTextFilterEnabled(true);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||||
|
recyclerView.setAdapter(mAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNodeClick(PwNode node) {
|
||||||
|
mAdapter.registerANodeToUpdate(node);
|
||||||
|
switch (node.getType()) {
|
||||||
|
case GROUP:
|
||||||
|
GroupActivity.launch(this, (PwGroup) node);
|
||||||
|
break;
|
||||||
|
case ENTRY:
|
||||||
|
EntryActivity.launch(this, (PwEntry) node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
super.onCreateOptionsMenu(menu);
|
||||||
|
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
MenuUtil.donationMenuInflater(inflater, menu);
|
||||||
|
inflater.inflate(R.menu.tree, menu);
|
||||||
|
inflater.inflate(R.menu.default_menu, menu);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) {
|
||||||
|
// Toggle setting
|
||||||
|
Editor editor = prefs.edit();
|
||||||
|
editor.putString(getString(R.string.sort_node_key), sortNodeEnum.name());
|
||||||
|
editor.putBoolean(getString(R.string.sort_ascending_key), ascending);
|
||||||
|
editor.putBoolean(getString(R.string.sort_group_before_key), groupsBefore);
|
||||||
|
editor.putBoolean(getString(R.string.sort_recycle_bin_bottom_key), recycleBinBottom);
|
||||||
|
EditorCompat.apply(editor);
|
||||||
|
|
||||||
|
// Tell the adapter to refresh it's list
|
||||||
|
mAdapter.notifyChangeSort(sortNodeEnum, ascending, groupsBefore);
|
||||||
|
mAdapter.rebuildList(mCurrentGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch ( item.getItemId() ) {
|
||||||
|
|
||||||
|
case R.id.menu_sort:
|
||||||
|
SortDialogFragment sortDialogFragment;
|
||||||
|
|
||||||
|
PwDatabase database = App.getDB().pm;
|
||||||
|
/*
|
||||||
|
// TODO Recycle bin bottom
|
||||||
|
if (database.isRecycleBinAvailable() && database.isRecycleBinEnable()) {
|
||||||
|
sortDialogFragment =
|
||||||
|
SortDialogFragment.getInstance(
|
||||||
|
PrefsUtil.getListSort(this),
|
||||||
|
PrefsUtil.getAscendingSort(this),
|
||||||
|
PrefsUtil.getGroupsBeforeSort(this),
|
||||||
|
PrefsUtil.getRecycleBinBottomSort(this));
|
||||||
|
} else {
|
||||||
|
*/
|
||||||
|
sortDialogFragment =
|
||||||
|
SortDialogFragment.getInstance(
|
||||||
|
PreferencesUtil.getListSort(this),
|
||||||
|
PreferencesUtil.getAscendingSort(this),
|
||||||
|
PreferencesUtil.getGroupsBeforeSort(this));
|
||||||
|
//}
|
||||||
|
|
||||||
|
sortDialogFragment.show(getSupportFragmentManager(), "sortDialog");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Check the time lock before launching settings
|
||||||
|
MenuUtil.onDefaultMenuOptionsItemSelected(this, item, true);
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@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:
|
||||||
|
if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE ||
|
||||||
|
resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) {
|
||||||
|
PwNode newNode = (PwNode) data.getSerializableExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY);
|
||||||
|
if (newNode != null) {
|
||||||
|
if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE)
|
||||||
|
mAdapter.addNode(newNode);
|
||||||
|
if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) {
|
||||||
|
//mAdapter.updateLastNodeRegister(newNode);
|
||||||
|
mAdapter.rebuildList(mCurrentGroup);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(this.getClass().getName(), "New node can be retrieve in Activity Result");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
// TODO BUG HERE
|
||||||
|
super.startActivityForResult(intent, requestCode, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AfterAddNode extends AfterAddNodeOnFinish {
|
||||||
|
AfterAddNode(Handler handler) {
|
||||||
|
super(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(PwNode pwNode) {
|
||||||
|
super.run();
|
||||||
|
if (mSuccess) {
|
||||||
|
mAdapter.addNode(pwNode);
|
||||||
|
} else {
|
||||||
|
displayMessage(ListNodesActivity.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AfterDeleteNode extends OnFinish {
|
||||||
|
private PwNode pwNode;
|
||||||
|
|
||||||
|
AfterDeleteNode(Handler handler, PwNode pwNode) {
|
||||||
|
super(handler);
|
||||||
|
this.pwNode = pwNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if ( mSuccess) {
|
||||||
|
mAdapter.removeNode(pwNode);
|
||||||
|
PwGroup parent = pwNode.getParent();
|
||||||
|
PwDatabase database = App.getDB().pm;
|
||||||
|
if (database.isRecycleBinAvailable() && database.isRecycleBinEnable()) {
|
||||||
|
PwGroup recycleBin = database.getRecycleBin();
|
||||||
|
// Add trash if it doesn't exists
|
||||||
|
if (parent.equals(recycleBin)
|
||||||
|
&& mCurrentGroup != null
|
||||||
|
&& mCurrentGroup.getParent() == null
|
||||||
|
&& !mCurrentGroup.equals(recycleBin)) {
|
||||||
|
mAdapter.addNode(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mHandler.post(new UIToastTask(ListNodesActivity.this, "Unrecoverable error: " + mMessage));
|
||||||
|
App.setShutdown();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* 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.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
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 android.util.Log;
|
||||||
|
|
||||||
|
import com.keepassdroid.app.App;
|
||||||
|
import com.keepassdroid.settings.PreferencesUtil;
|
||||||
|
import com.keepassdroid.stylish.StylishActivity;
|
||||||
|
import com.keepassdroid.timeout.TimeoutHelper;
|
||||||
|
|
||||||
|
public abstract class LockingActivity extends StylishActivity {
|
||||||
|
|
||||||
|
private static final String TAG = LockingActivity.class.getName();
|
||||||
|
|
||||||
|
public static final int RESULT_EXIT_LOCK = 1450;
|
||||||
|
|
||||||
|
private static final String AT_LEAST_SECOND_SHOWN_KEY = "AT_LEAST_SECOND_SHOWN_KEY";
|
||||||
|
|
||||||
|
private ScreenReceiver screenReceiver;
|
||||||
|
private boolean exitLock;
|
||||||
|
|
||||||
|
protected static void recordFirstTimeBeforeLaunch(Activity activity) {
|
||||||
|
TimeoutHelper.recordTime(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean checkTimeIsAllowedOrFinish(Activity activity) {
|
||||||
|
return TimeoutHelper.checkTime(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this)) {
|
||||||
|
screenReceiver = new ScreenReceiver();
|
||||||
|
registerReceiver(screenReceiver, new IntentFilter((Intent.ACTION_SCREEN_OFF)));
|
||||||
|
} else
|
||||||
|
screenReceiver = null;
|
||||||
|
|
||||||
|
exitLock = false;
|
||||||
|
|
||||||
|
// WARNING TODO recordTime is not called after a back if was in backstack
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkShutdown(Activity act) {
|
||||||
|
if (App.isShutdown() && App.getDB().Loaded()) {
|
||||||
|
Log.i(TAG, "Shutdown " + act.getLocalClassName() +
|
||||||
|
" after inactivity or manual lock");
|
||||||
|
act.setResult(RESULT_EXIT_LOCK);
|
||||||
|
act.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void lockAndExit() {
|
||||||
|
App.setShutdown();
|
||||||
|
setResult(LockingActivity.RESULT_EXIT_LOCK);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if (resultCode == RESULT_EXIT_LOCK) {
|
||||||
|
exitLock = true;
|
||||||
|
checkShutdown(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
// After the first creation
|
||||||
|
// or If simply swipe with another application
|
||||||
|
// If the time is out -> close the Activity
|
||||||
|
TimeoutHelper.checkTime(this);
|
||||||
|
// If onCreate already record time
|
||||||
|
if (!exitLock)
|
||||||
|
TimeoutHelper.recordTime(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
// If the time is out during our navigation in activity -> close the Activity
|
||||||
|
TimeoutHelper.checkTime(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if(screenReceiver != null)
|
||||||
|
unregisterReceiver(screenReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putBoolean(AT_LEAST_SECOND_SHOWN_KEY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(LockingActivity.this)) {
|
||||||
|
App.setShutdown();
|
||||||
|
checkShutdown(LockingActivity.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,12 +17,31 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.keepassdroid;
|
package com.keepassdroid.activities;
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
public abstract class LockCloseActivity extends LockingActivity {
|
import com.keepassdroid.compat.BuildCompat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locking Hide Activity that sets FLAG_SECURE to prevent screenshots, and from
|
||||||
|
* appearing in the recent app preview
|
||||||
|
*/
|
||||||
|
public abstract class LockingHideActivity extends LockingActivity {
|
||||||
|
|
||||||
|
@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(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc) Workaround for HTC Linkify issues
|
/* (non-Javadoc) Workaround for HTC Linkify issues
|
||||||
* @see android.app.Activity#startActivity(android.content.Intent)
|
* @see android.app.Activity#startActivity(android.content.Intent)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.keepassdroid.adapters;
|
||||||
|
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
abstract class BasicViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
View container;
|
||||||
|
ImageView icon;
|
||||||
|
TextView text;
|
||||||
|
|
||||||
|
BasicViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.keepassdroid.adapters;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
class EntryViewHolder extends BasicViewHolder {
|
||||||
|
|
||||||
|
EntryViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
container = itemView.findViewById(R.id.entry_container);
|
||||||
|
icon = (ImageView) itemView.findViewById(R.id.entry_icon);
|
||||||
|
text = (TextView) itemView.findViewById(R.id.entry_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.keepassdroid.adapters;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
class GroupViewHolder extends BasicViewHolder {
|
||||||
|
|
||||||
|
GroupViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
container = itemView.findViewById(R.id.group_container);
|
||||||
|
icon = (ImageView) itemView.findViewById(R.id.group_icon);
|
||||||
|
text = (TextView) itemView.findViewById(R.id.group_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
295
app/src/main/java/com/keepassdroid/adapters/NodeAdapter.java
Normal file
295
app/src/main/java/com/keepassdroid/adapters/NodeAdapter.java
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 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.adapters;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v7.util.SortedList;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.util.SortedListAdapterCallback;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ContextMenu;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.keepassdroid.app.App;
|
||||||
|
import com.keepassdroid.database.PwGroup;
|
||||||
|
import com.keepassdroid.database.PwNode;
|
||||||
|
import com.keepassdroid.settings.PreferencesUtil;
|
||||||
|
import com.keepassdroid.database.SortNodeEnum;
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
|
||||||
|
|
||||||
|
private SortedList<PwNode> nodeSortedList;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private LayoutInflater inflater;
|
||||||
|
private float textSize;
|
||||||
|
private SortNodeEnum listSort;
|
||||||
|
private boolean groupsBeforeSort;
|
||||||
|
private boolean ascendingSort;
|
||||||
|
|
||||||
|
private OnNodeClickCallback onNodeClickCallback;
|
||||||
|
private int nodePositionToUpdate;
|
||||||
|
private NodeMenuListener nodeMenuListener;
|
||||||
|
private boolean activateContextMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create node list adapter with contextMenu or not
|
||||||
|
* @param context Context to use
|
||||||
|
*/
|
||||||
|
public NodeAdapter(final Context context) {
|
||||||
|
this.inflater = LayoutInflater.from(context);
|
||||||
|
this.context = context;
|
||||||
|
this.textSize = PreferencesUtil.getListTextSize(context);
|
||||||
|
this.listSort = PreferencesUtil.getListSort(context);
|
||||||
|
this.groupsBeforeSort = PreferencesUtil.getGroupsBeforeSort(context);
|
||||||
|
this.ascendingSort = PreferencesUtil.getAscendingSort(context);
|
||||||
|
this.activateContextMenu = false;
|
||||||
|
this.nodePositionToUpdate = -1;
|
||||||
|
|
||||||
|
this.nodeSortedList = new SortedList<>(PwNode.class, new SortedListAdapterCallback<PwNode>(this) {
|
||||||
|
@Override public int compare(PwNode item1, PwNode item2) {
|
||||||
|
return listSort.getNodeComparator(ascendingSort, groupsBeforeSort).compare(item1, item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean areContentsTheSame(PwNode oldItem, PwNode newItem) {
|
||||||
|
return oldItem.isContentVisuallyTheSame(newItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean areItemsTheSame(PwNode item1, PwNode item2) {
|
||||||
|
return item1.equals(item2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivateContextMenu(boolean activate) {
|
||||||
|
this.activateContextMenu = activate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuild the list by clear and build children from the group
|
||||||
|
*/
|
||||||
|
public void rebuildList(PwGroup group) {
|
||||||
|
this.nodeSortedList.clear();
|
||||||
|
if (group != null) {
|
||||||
|
this.nodeSortedList.addAll(group.getDirectChildren());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a node to the list
|
||||||
|
* @param node Node to add
|
||||||
|
*/
|
||||||
|
public void addNode(PwNode node) {
|
||||||
|
nodeSortedList.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a node to update before an action
|
||||||
|
* Call updateLastNodeRegister() after the action to update the node
|
||||||
|
* @param node Node to register
|
||||||
|
*/
|
||||||
|
public void registerANodeToUpdate(PwNode node) {
|
||||||
|
nodePositionToUpdate = nodeSortedList.indexOf(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the last Node register in the list
|
||||||
|
* Work if only registerANodeToUpdate(PwNode node) is called before
|
||||||
|
*/
|
||||||
|
public void updateLastNodeRegister(PwNode node) {
|
||||||
|
// Don't really update here, sorted list knows each original ref, so we just notify a change
|
||||||
|
try {
|
||||||
|
if (nodePositionToUpdate != -1) {
|
||||||
|
// Don't know why but there is a bug to remove a node after this update
|
||||||
|
nodeSortedList.updateItemAt(nodePositionToUpdate, node);
|
||||||
|
nodeSortedList.recalculatePositionOfItemAt(nodePositionToUpdate);
|
||||||
|
nodePositionToUpdate = -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.e(NodeAdapter.class.getName(), "registerANodeToUpdate must be called before updateLastNodeRegister");
|
||||||
|
}
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
Log.e(NodeAdapter.class.getName(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove node in the list
|
||||||
|
* @param node Node to delete
|
||||||
|
*/
|
||||||
|
public void removeNode(PwNode node) {
|
||||||
|
nodeSortedList.remove(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify a change sort of the list
|
||||||
|
*/
|
||||||
|
public void notifyChangeSort(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore) {
|
||||||
|
this.listSort = sortNodeEnum;
|
||||||
|
this.ascendingSort = ascending;
|
||||||
|
this.groupsBeforeSort = groupsBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
return nodeSortedList.get(position).getType().ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
BasicViewHolder basicViewHolder;
|
||||||
|
View view;
|
||||||
|
if (viewType == PwNode.Type.GROUP.ordinal()) {
|
||||||
|
view = inflater.inflate(R.layout.list_nodes_group, parent, false);
|
||||||
|
basicViewHolder = new GroupViewHolder(view);
|
||||||
|
} else {
|
||||||
|
view = inflater.inflate(R.layout.list_nodes_entry, parent, false);
|
||||||
|
basicViewHolder = new EntryViewHolder(view);
|
||||||
|
}
|
||||||
|
return basicViewHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(BasicViewHolder holder, int position) {
|
||||||
|
PwNode subNode = nodeSortedList.get(position);
|
||||||
|
// Assign image
|
||||||
|
App.getDB().drawFactory.assignDrawableTo(holder.icon,
|
||||||
|
context.getResources(), subNode.getIcon());
|
||||||
|
// Assign text
|
||||||
|
holder.text.setText(subNode.getDisplayTitle());
|
||||||
|
// Assign click
|
||||||
|
holder.container.setOnClickListener(
|
||||||
|
new OnNodeClickListener(subNode));
|
||||||
|
// Context menu
|
||||||
|
if (activateContextMenu) {
|
||||||
|
holder.container.setOnCreateContextMenuListener(
|
||||||
|
new ContextMenuBuilder(subNode, nodeMenuListener));
|
||||||
|
}
|
||||||
|
// Assign text size
|
||||||
|
holder.text.setTextSize(textSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return nodeSortedList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign a listener when a node is clicked
|
||||||
|
*/
|
||||||
|
public void setOnNodeClickListener(OnNodeClickCallback onNodeClickCallback) {
|
||||||
|
this.onNodeClickCallback = onNodeClickCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign a listener when an element of menu is clicked
|
||||||
|
*/
|
||||||
|
public void setNodeMenuListener(NodeMenuListener nodeMenuListener) {
|
||||||
|
this.nodeMenuListener = nodeMenuListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback listener to redefine to do an action when a node is click
|
||||||
|
*/
|
||||||
|
public interface OnNodeClickCallback {
|
||||||
|
void onNodeClick(PwNode node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu listener to redefine to do an action in menu
|
||||||
|
*/
|
||||||
|
public interface NodeMenuListener {
|
||||||
|
boolean onOpenMenuClick(PwNode node);
|
||||||
|
boolean onEditMenuClick(PwNode node);
|
||||||
|
boolean onDeleteMenuClick(PwNode node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for node listener
|
||||||
|
*/
|
||||||
|
private class OnNodeClickListener implements View.OnClickListener {
|
||||||
|
private PwNode node;
|
||||||
|
|
||||||
|
OnNodeClickListener(PwNode node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (onNodeClickCallback != null)
|
||||||
|
onNodeClickCallback.onNodeClick(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for menu listener
|
||||||
|
*/
|
||||||
|
private class ContextMenuBuilder implements View.OnCreateContextMenuListener {
|
||||||
|
|
||||||
|
private static final int MENU_OPEN = Menu.FIRST;
|
||||||
|
private static final int MENU_EDIT = MENU_OPEN + 1;
|
||||||
|
private static final int MENU_DELETE = MENU_EDIT + 1;
|
||||||
|
|
||||||
|
private PwNode node;
|
||||||
|
private NodeMenuListener menuListener;
|
||||||
|
|
||||||
|
ContextMenuBuilder(PwNode node, NodeMenuListener menuListener) {
|
||||||
|
this.menuListener = menuListener;
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
|
||||||
|
MenuItem clearMenu = contextMenu.add(Menu.NONE, MENU_OPEN, Menu.NONE, R.string.menu_open);
|
||||||
|
clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener);
|
||||||
|
if (!App.getDB().readOnly && !node.equals(App.getDB().pm.getRecycleBin())) {
|
||||||
|
// TODO make edit for group
|
||||||
|
// clearMenu = contextMenu.add(Menu.NONE, MENU_EDIT, Menu.NONE, R.string.menu_edit);
|
||||||
|
// clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener);
|
||||||
|
clearMenu = contextMenu.add(Menu.NONE, MENU_DELETE, Menu.NONE, R.string.menu_delete);
|
||||||
|
clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuItem.OnMenuItemClickListener mOnMyActionClickListener = new MenuItem.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
if (menuListener == null)
|
||||||
|
return false;
|
||||||
|
switch ( item.getItemId() ) {
|
||||||
|
case MENU_OPEN:
|
||||||
|
return menuListener.onOpenMenuClick(node);
|
||||||
|
case MENU_EDIT:
|
||||||
|
return menuListener.onEditMenuClick(node);
|
||||||
|
case MENU_DELETE:
|
||||||
|
return menuListener.onDeleteMenuClick(node);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,18 +19,19 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.app;
|
package com.keepassdroid.app;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.support.multidex.MultiDexApplication;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
|
||||||
import com.keepassdroid.compat.PRNGFixes;
|
import com.keepassdroid.compat.PRNGFixes;
|
||||||
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.fileselect.RecentFileHistory;
|
import com.keepassdroid.fileselect.RecentFileHistory;
|
||||||
import com.keepassdroid.stylish.Stylish;
|
import com.keepassdroid.stylish.Stylish;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends MultiDexApplication {
|
||||||
private static Database db = null;
|
private static Database db = null;
|
||||||
private static boolean shutdown = false;
|
private static boolean shutdown = false;
|
||||||
|
private static CharSequence mMessage = "";
|
||||||
private static Calendar calendar = null;
|
private static Calendar calendar = null;
|
||||||
private static RecentFileHistory fileHistory = null;
|
private static RecentFileHistory fileHistory = null;
|
||||||
|
|
||||||
@@ -38,7 +39,6 @@ public class App extends Application {
|
|||||||
if ( db == null ) {
|
if ( db == null ) {
|
||||||
db = new Database();
|
db = new Database();
|
||||||
}
|
}
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,17 +56,27 @@ public class App extends Application {
|
|||||||
|
|
||||||
public static void setShutdown() {
|
public static void setShutdown() {
|
||||||
shutdown = true;
|
shutdown = true;
|
||||||
|
mMessage = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setShutdown(CharSequence message) {
|
||||||
|
shutdown = true;
|
||||||
|
mMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CharSequence getMessage() {
|
||||||
|
return mMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearShutdown() {
|
public static void clearShutdown() {
|
||||||
shutdown = false;
|
shutdown = false;
|
||||||
|
mMessage = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Calendar getCalendar() {
|
public static Calendar getCalendar() {
|
||||||
if ( calendar == null ) {
|
if ( calendar == null ) {
|
||||||
calendar = Calendar.getInstance();
|
calendar = Calendar.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
return calendar;
|
return calendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,9 +85,7 @@ public class App extends Application {
|
|||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
Stylish.init(this);
|
Stylish.init(this);
|
||||||
|
|
||||||
fileHistory = new RecentFileHistory(this);
|
fileHistory = new RecentFileHistory(this);
|
||||||
|
|
||||||
PRNGFixes.apply();
|
PRNGFixes.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +94,6 @@ public class App extends Application {
|
|||||||
if ( db != null ) {
|
if ( db != null ) {
|
||||||
db.clear();
|
db.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onTerminate();
|
super.onTerminate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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.autofill;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.assist.AssistStructure;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import com.keepassdroid.fileselect.FileSelectActivity;
|
||||||
|
import com.kunzisoft.keepass.KeePass;
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
public class AutoFillAuthActivity extends KeePass {
|
||||||
|
|
||||||
|
private AutofillHelper autofillHelper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
autofillHelper = new AutofillHelper();
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntentSender getAuthIntentSenderForResponse(Context context) {
|
||||||
|
final Intent intent = new Intent(context, AutoFillAuthActivity.class);
|
||||||
|
return PendingIntent.getActivity(context, 0,
|
||||||
|
intent, PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startFileSelectActivity() {
|
||||||
|
// Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE)
|
||||||
|
AssistStructure assistStructure = autofillHelper.retrieveAssistStructure(getIntent());
|
||||||
|
if (assistStructure != null) {
|
||||||
|
FileSelectActivity.launch(this, assistStructure);
|
||||||
|
} else {
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
174
app/src/main/java/com/keepassdroid/autofill/AutofillHelper.java
Normal file
174
app/src/main/java/com/keepassdroid/autofill/AutofillHelper.java
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* 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.autofill;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.assist.AssistStructure;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.service.autofill.Dataset;
|
||||||
|
import android.service.autofill.FillResponse;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.autofill.AutofillId;
|
||||||
|
import android.view.autofill.AutofillManager;
|
||||||
|
import android.view.autofill.AutofillValue;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import com.keepassdroid.database.PwEntry;
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
public class AutofillHelper {
|
||||||
|
|
||||||
|
public static final int AUTOFILL_RESPONSE_REQUEST_CODE = 8165;
|
||||||
|
|
||||||
|
private AssistStructure assistStructure = null;
|
||||||
|
|
||||||
|
public AssistStructure retrieveAssistStructure(Intent intent) {
|
||||||
|
if (intent != null && intent.getExtras() != null) {
|
||||||
|
assistStructure = intent.getParcelableExtra(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE);
|
||||||
|
}
|
||||||
|
return assistStructure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call retrieveAssistStructure before
|
||||||
|
*/
|
||||||
|
public AssistStructure getAssistStructure() {
|
||||||
|
return assistStructure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addAssistStructureExtraInIntent(Intent intent, AssistStructure assistStructure) {
|
||||||
|
if (assistStructure != null) {
|
||||||
|
intent.putExtra(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE, assistStructure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define if android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE is an extra bundle key present in the Intent
|
||||||
|
*/
|
||||||
|
public static boolean isIntentContainsExtraAssistStructureKey(Intent intent) {
|
||||||
|
return (intent != null
|
||||||
|
&& intent.getExtras() != null
|
||||||
|
&& intent.getExtras().containsKey(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE));
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Dataset buildDataset(Context context, PwEntry entry,
|
||||||
|
StructureParser.Result struct) {
|
||||||
|
String title = makeEntryTitle(entry);
|
||||||
|
RemoteViews views = newRemoteViews(context.getPackageName(), title);
|
||||||
|
Dataset.Builder builder = new Dataset.Builder(views);
|
||||||
|
builder.setId(entry.getUUID().toString());
|
||||||
|
|
||||||
|
if (entry.getPassword() != null) {
|
||||||
|
AutofillValue value = AutofillValue.forText(entry.getPassword());
|
||||||
|
struct.password.forEach(id -> builder.setValue(id, value));
|
||||||
|
}
|
||||||
|
if (entry.getUsername() != null) {
|
||||||
|
AutofillValue value = AutofillValue.forText(entry.getUsername());
|
||||||
|
List<AutofillId> ids = new ArrayList<>(struct.username);
|
||||||
|
if (entry.getUsername().contains("@") || struct.username.isEmpty())
|
||||||
|
ids.addAll(struct.email);
|
||||||
|
ids.forEach(id -> builder.setValue(id, value));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return builder.build();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// if not value be set
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private String makeEntryTitle(PwEntry entry) {
|
||||||
|
if (!entry.getTitle().isEmpty() && !entry.getUsername().isEmpty())
|
||||||
|
return String.format("%s (%s)", entry.getTitle(), entry.getUsername());
|
||||||
|
if (!entry.getTitle().isEmpty())
|
||||||
|
return entry.getTitle();
|
||||||
|
if (!entry.getUsername().isEmpty())
|
||||||
|
return entry.getUsername();
|
||||||
|
if (!entry.getNotes().isEmpty())
|
||||||
|
return entry.getNotes().trim();
|
||||||
|
return ""; // TODO No title
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to hit when right key is selected
|
||||||
|
*/
|
||||||
|
public void buildResponseWhenEntrySelected(Activity activity, PwEntry entry) {
|
||||||
|
Intent mReplyIntent;
|
||||||
|
Intent intent = activity.getIntent();
|
||||||
|
if (isIntentContainsExtraAssistStructureKey(intent)) {
|
||||||
|
AssistStructure structure = intent.getParcelableExtra(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE);
|
||||||
|
StructureParser.Result result = new StructureParser(structure).parse();
|
||||||
|
|
||||||
|
// New Response
|
||||||
|
FillResponse.Builder responseBuilder = new FillResponse.Builder();
|
||||||
|
Dataset dataset = buildDataset(activity, entry, result);
|
||||||
|
responseBuilder.addDataset(dataset);
|
||||||
|
mReplyIntent = new Intent();
|
||||||
|
Log.d(activity.getClass().getName(), "Successed Autofill auth.");
|
||||||
|
mReplyIntent.putExtra(
|
||||||
|
AutofillManager.EXTRA_AUTHENTICATION_RESULT,
|
||||||
|
responseBuilder.build());
|
||||||
|
activity.setResult(Activity.RESULT_OK, mReplyIntent);
|
||||||
|
} else {
|
||||||
|
Log.w(activity.getClass().getName(), "Failed Autofill auth.");
|
||||||
|
activity.setResult(Activity.RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method to loop and close each activity with return data
|
||||||
|
*/
|
||||||
|
public static void onActivityResultSetResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == AUTOFILL_RESPONSE_REQUEST_CODE) {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
activity.setResult(resultCode, data);
|
||||||
|
}
|
||||||
|
if (resultCode == Activity.RESULT_CANCELED) {
|
||||||
|
activity.setResult(Activity.RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method to loop and close each activity with return data
|
||||||
|
*/
|
||||||
|
public static void onActivityResultSetResultAndFinish(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == AUTOFILL_RESPONSE_REQUEST_CODE) {
|
||||||
|
onActivityResultSetResult(activity, requestCode, resultCode, data);
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RemoteViews newRemoteViews(String packageName, String remoteViewsText) {
|
||||||
|
RemoteViews presentation =
|
||||||
|
new RemoteViews(packageName, R.layout.autofill_service_list_item);
|
||||||
|
presentation.setTextViewText(R.id.text, remoteViewsText);
|
||||||
|
return presentation;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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.autofill;
|
||||||
|
|
||||||
|
import android.app.assist.AssistStructure;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.service.autofill.AutofillService;
|
||||||
|
import android.service.autofill.FillCallback;
|
||||||
|
import android.service.autofill.FillContext;
|
||||||
|
import android.service.autofill.FillRequest;
|
||||||
|
import android.service.autofill.FillResponse;
|
||||||
|
import android.service.autofill.SaveCallback;
|
||||||
|
import android.service.autofill.SaveRequest;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.autofill.AutofillId;
|
||||||
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
public class KeeAutofillService extends AutofillService {
|
||||||
|
private static final String TAG = "KeeAutofillService";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFillRequest(@NonNull FillRequest request, @NonNull CancellationSignal cancellationSignal,
|
||||||
|
@NonNull FillCallback callback) {
|
||||||
|
List<FillContext> fillContexts = request.getFillContexts();
|
||||||
|
AssistStructure latestStructure = fillContexts.get(fillContexts.size() - 1).getStructure();
|
||||||
|
|
||||||
|
cancellationSignal.setOnCancelListener(() ->
|
||||||
|
Log.e(TAG, "Cancel autofill not implemented in this sample.")
|
||||||
|
);
|
||||||
|
|
||||||
|
FillResponse.Builder responseBuilder = new FillResponse.Builder();
|
||||||
|
// Check user's settings for authenticating Responses and Datasets.
|
||||||
|
StructureParser.Result parseResult = new StructureParser(latestStructure).parse();
|
||||||
|
AutofillId[] autofillIds = parseResult.allAutofillIds();
|
||||||
|
if (!Arrays.asList(autofillIds).isEmpty()) {
|
||||||
|
// If the entire Autofill Response is authenticated, AuthActivity is used
|
||||||
|
// to generate Response.
|
||||||
|
IntentSender sender = AutoFillAuthActivity.getAuthIntentSenderForResponse(this);
|
||||||
|
RemoteViews presentation = new RemoteViews(getPackageName(), R.layout.autofill_service_unlock);
|
||||||
|
responseBuilder.setAuthentication(autofillIds, sender, presentation);
|
||||||
|
callback.onSuccess(responseBuilder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
|
||||||
|
// TODO Save autofill
|
||||||
|
//callback.onFailure(getString(R.string.autofill_not_support_save));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnected() {
|
||||||
|
Log.d(TAG, "onConnected");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnected() {
|
||||||
|
Log.d(TAG, "onDisconnected");
|
||||||
|
}
|
||||||
|
}
|
||||||
115
app/src/main/java/com/keepassdroid/autofill/StructureParser.java
Normal file
115
app/src/main/java/com/keepassdroid/autofill/StructureParser.java
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 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.autofill;
|
||||||
|
|
||||||
|
import android.app.assist.AssistStructure;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.autofill.AutofillId;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse AssistStructure and guess username and password fields.
|
||||||
|
*/
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
class StructureParser {
|
||||||
|
static private final String TAG = StructureParser.class.getName();
|
||||||
|
|
||||||
|
final private AssistStructure structure;
|
||||||
|
private Result result;
|
||||||
|
private AutofillId usernameCandidate;
|
||||||
|
|
||||||
|
StructureParser(AssistStructure structure) {
|
||||||
|
this.structure = structure;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result parse() {
|
||||||
|
result = new Result();
|
||||||
|
usernameCandidate = null;
|
||||||
|
for (int i=0; i<structure.getWindowNodeCount(); ++i) {
|
||||||
|
AssistStructure.WindowNode windowNode = structure.getWindowNodeAt(i);
|
||||||
|
result.title.add(windowNode.getTitle());
|
||||||
|
result.webDomain.add(windowNode.getRootViewNode().getWebDomain());
|
||||||
|
parseViewNode(windowNode.getRootViewNode());
|
||||||
|
}
|
||||||
|
// If not explicit username field found, add the field just before password field.
|
||||||
|
if (result.username.isEmpty() && result.email.isEmpty()
|
||||||
|
&& !result.password.isEmpty() && usernameCandidate != null)
|
||||||
|
result.username.add(usernameCandidate);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseViewNode(AssistStructure.ViewNode node) {
|
||||||
|
String[] hints = node.getAutofillHints();
|
||||||
|
if (hints != null && hints.length > 0) {
|
||||||
|
if (Arrays.stream(hints).anyMatch(View.AUTOFILL_HINT_USERNAME::equals))
|
||||||
|
result.username.add(node.getAutofillId());
|
||||||
|
else if (Arrays.stream(hints).anyMatch(View.AUTOFILL_HINT_EMAIL_ADDRESS::equals))
|
||||||
|
result.email.add(node.getAutofillId());
|
||||||
|
else if (Arrays.stream(hints).anyMatch(View.AUTOFILL_HINT_PASSWORD::equals))
|
||||||
|
result.password.add(node.getAutofillId());
|
||||||
|
else
|
||||||
|
Log.d(TAG, "unsupported hints");
|
||||||
|
} else if (node.getAutofillType() == View.AUTOFILL_TYPE_TEXT) {
|
||||||
|
int inputType = node.getInputType();
|
||||||
|
if ((inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) > 0)
|
||||||
|
result.email.add(node.getAutofillId());
|
||||||
|
else if ((inputType & InputType.TYPE_TEXT_VARIATION_PASSWORD) > 0)
|
||||||
|
result.password.add(node.getAutofillId());
|
||||||
|
else if (result.password.isEmpty())
|
||||||
|
usernameCandidate = node.getAutofillId();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<node.getChildCount(); ++i)
|
||||||
|
parseViewNode(node.getChildAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
static class Result {
|
||||||
|
final List<CharSequence> title;
|
||||||
|
final List<String> webDomain;
|
||||||
|
final List<AutofillId> username;
|
||||||
|
final List<AutofillId> email;
|
||||||
|
final List<AutofillId> password;
|
||||||
|
|
||||||
|
private Result() {
|
||||||
|
title = new ArrayList<>();
|
||||||
|
webDomain = new ArrayList<>();
|
||||||
|
username = new ArrayList<>();
|
||||||
|
email = new ArrayList<>();
|
||||||
|
password = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
AutofillId[] allAutofillIds() {
|
||||||
|
ArrayList<AutofillId> all = new ArrayList<>();
|
||||||
|
all.addAll(username);
|
||||||
|
all.addAll(email);
|
||||||
|
all.addAll(password);
|
||||||
|
return all.toArray(new AutofillId[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -51,6 +51,6 @@ public class StorageAF {
|
|||||||
if (!supportsStorageFramework()) { return false; }
|
if (!supportsStorageFramework()) { return false; }
|
||||||
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||||
return prefs.getBoolean(ctx.getString(R.string.saf_key), ctx.getResources().getBoolean(R.bool.saf_default));
|
return prefs.getBoolean(ctx.getString(R.string.saf_key), ctx.getResources().getBoolean(R.bool.settings_saf_default));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
app/src/main/java/com/keepassdroid/database/AutoType.java
Normal file
37
app/src/main/java/com/keepassdroid/database/AutoType.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class AutoType implements Cloneable, Serializable {
|
||||||
|
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<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public AutoType clone() {
|
||||||
|
AutoType auto;
|
||||||
|
try {
|
||||||
|
auto = (AutoType) super.clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
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<Map.Entry<String, String>> entrySet() {
|
||||||
|
return windowSeqPairs.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ import java.util.Set;
|
|||||||
import com.keepassdroid.database.security.ProtectedBinary;
|
import com.keepassdroid.database.security.ProtectedBinary;
|
||||||
|
|
||||||
public class BinaryPool {
|
public class BinaryPool {
|
||||||
private HashMap<Integer, ProtectedBinary> pool = new HashMap<Integer, ProtectedBinary>();
|
private HashMap<Integer, ProtectedBinary> pool = new HashMap<>();
|
||||||
|
|
||||||
public BinaryPool() {
|
public BinaryPool() {
|
||||||
|
|
||||||
@@ -63,12 +63,10 @@ public class BinaryPool {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean operate(PwEntryV4 entry) {
|
public boolean operate(PwEntryV4 entry) {
|
||||||
for (PwEntryV4 histEntry : entry.history) {
|
for (PwEntryV4 histEntry : entry.getHistory()) {
|
||||||
poolAdd(histEntry.binaries);
|
poolAdd(histEntry.getBinaries());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
poolAdd(entry.getBinaries());
|
||||||
poolAdd(entry.binaries);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.keepassdroid;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
@@ -25,9 +25,6 @@ import android.net.Uri;
|
|||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
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.ContentFileNotFoundException;
|
||||||
import com.keepassdroid.database.exception.InvalidDBException;
|
import com.keepassdroid.database.exception.InvalidDBException;
|
||||||
import com.keepassdroid.database.exception.InvalidPasswordException;
|
import com.keepassdroid.database.exception.InvalidPasswordException;
|
||||||
@@ -37,6 +34,7 @@ import com.keepassdroid.database.load.ImporterFactory;
|
|||||||
import com.keepassdroid.database.save.PwDbOutput;
|
import com.keepassdroid.database.save.PwDbOutput;
|
||||||
import com.keepassdroid.icons.DrawableFactory;
|
import com.keepassdroid.icons.DrawableFactory;
|
||||||
import com.keepassdroid.search.SearchDbHelper;
|
import com.keepassdroid.search.SearchDbHelper;
|
||||||
|
import com.keepassdroid.tasks.UpdateStatus;
|
||||||
import com.keepassdroid.utils.UriUtil;
|
import com.keepassdroid.utils.UriUtil;
|
||||||
import com.kunzisoft.keepass.R;
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
@@ -48,14 +46,11 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.SyncFailedException;
|
import java.io.SyncFailedException;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author bpellin
|
* @author bpellin
|
||||||
*/
|
*/
|
||||||
public class Database {
|
public class Database {
|
||||||
public Set<PwGroup> dirty = new HashSet<PwGroup>();
|
|
||||||
public PwDatabase pm;
|
public PwDatabase pm;
|
||||||
public Uri mUri;
|
public Uri mUri;
|
||||||
public SearchDbHelper searchHelper;
|
public SearchDbHelper searchHelper;
|
||||||
@@ -222,7 +217,6 @@ public class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
dirty.clear();
|
|
||||||
drawFactory.clear();
|
drawFactory.clear();
|
||||||
|
|
||||||
pm = null;
|
pm = null;
|
||||||
@@ -230,16 +224,4 @@ public class Database {
|
|||||||
loaded = false;
|
loaded = false;
|
||||||
passwordEncodingError = 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ public abstract class EntrySearchHandler extends EntryHandler<PwEntry> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sp.excludeExpired && entry.expires() && now.after(entry.getExpiryTime())) {
|
if (sp.excludeExpired && entry.expires() && now.after(entry.getExpiryTime().getDate())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class EntrySearchHandlerAll extends EntryHandler<PwEntry> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sp.excludeExpired && entry.expires() && now.after(entry.getExpiryTime())) {
|
if (sp.excludeExpired && entry.expires() && now.after(entry.getExpiryTime().getDate())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class EntrySearchHandlerV4 extends EntrySearchHandler {
|
|||||||
protected boolean searchID(PwEntry e) {
|
protected boolean searchID(PwEntry e) {
|
||||||
PwEntryV4 entry = (PwEntryV4) e;
|
PwEntryV4 entry = (PwEntryV4) e;
|
||||||
if (sp.searchInUUIDs) {
|
if (sp.searchInUUIDs) {
|
||||||
String hex = UuidUtil.toHexString(entry.uuid);
|
String hex = UuidUtil.toHexString(entry.getUUID());
|
||||||
return StrUtil.indexOfIgnoreCase(hex, sp.searchString, Locale.ENGLISH) >= 0;
|
return StrUtil.indexOfIgnoreCase(hex, sp.searchString, Locale.ENGLISH) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
public interface ISmallTimeLogger {
|
||||||
|
|
||||||
|
PwDate getLastModificationTime();
|
||||||
|
void setLastModificationTime(PwDate date);
|
||||||
|
|
||||||
|
PwDate getCreationTime();
|
||||||
|
void setCreationTime(PwDate date);
|
||||||
|
|
||||||
|
PwDate getLastAccessTime();
|
||||||
|
void setLastAccessTime(PwDate date);
|
||||||
|
|
||||||
|
PwDate getExpiryTime();
|
||||||
|
void setExpiryTime(PwDate date);
|
||||||
|
|
||||||
|
boolean expires();
|
||||||
|
void setExpires(boolean exp);
|
||||||
|
}
|
||||||
@@ -19,28 +19,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
import java.util.Date;
|
public interface ITimeLogger extends ISmallTimeLogger {
|
||||||
|
|
||||||
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();
|
long getUsageCount();
|
||||||
void setUsageCount(long count);
|
void setUsageCount(long count);
|
||||||
|
|
||||||
Date getLocationChanged();
|
PwDate getLocationChanged();
|
||||||
void setLocationChanged(Date date);
|
void setLocationChanged(PwDate date);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@@ -36,8 +32,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import android.os.DropBoxManager.Entry;
|
|
||||||
|
|
||||||
import com.keepassdroid.crypto.finalkey.FinalKey;
|
import com.keepassdroid.crypto.finalkey.FinalKey;
|
||||||
import com.keepassdroid.crypto.finalkey.FinalKeyFactory;
|
import com.keepassdroid.crypto.finalkey.FinalKeyFactory;
|
||||||
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
||||||
@@ -47,13 +41,14 @@ import com.keepassdroid.utils.Util;
|
|||||||
|
|
||||||
public abstract class PwDatabase {
|
public abstract class PwDatabase {
|
||||||
|
|
||||||
|
public static final UUID UUID_ZERO = new UUID(0,0);
|
||||||
public byte masterKey[] = new byte[32];
|
public byte masterKey[] = new byte[32];
|
||||||
public byte[] finalKey;
|
public byte[] finalKey;
|
||||||
public String name = "KeePass database";
|
public String name = "KeePass database";
|
||||||
public PwGroup rootGroup;
|
public PwGroup rootGroup;
|
||||||
public PwIconFactory iconFactory = new PwIconFactory();
|
public PwIconFactory iconFactory = new PwIconFactory();
|
||||||
public Map<PwGroupId, PwGroup> groups = new HashMap<PwGroupId, PwGroup>();
|
public Map<PwGroupId, PwGroup> groups = new HashMap<>();
|
||||||
public Map<UUID, PwEntry> entries = new HashMap<UUID, PwEntry>();
|
public Map<UUID, PwEntry> entries = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
private static boolean isKDBExtension(String filename) {
|
private static boolean isKDBExtension(String filename) {
|
||||||
@@ -252,7 +247,7 @@ public abstract class PwDatabase {
|
|||||||
|
|
||||||
public abstract void setNumRounds(long rounds) throws NumberFormatException;
|
public abstract void setNumRounds(long rounds) throws NumberFormatException;
|
||||||
|
|
||||||
public abstract boolean appSettingsEnabled();
|
public abstract boolean algorithmSettingsEnabled();
|
||||||
|
|
||||||
public abstract PwEncryptionAlgorithm getEncAlgorithm();
|
public abstract PwEncryptionAlgorithm getEncAlgorithm();
|
||||||
|
|
||||||
@@ -262,7 +257,7 @@ public abstract class PwDatabase {
|
|||||||
parent = rootGroup;
|
parent = rootGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.childGroups.add(newGroup);
|
parent.addChildGroup(newGroup);
|
||||||
newGroup.setParent(parent);
|
newGroup.setParent(parent);
|
||||||
groups.put(newGroup.getId(), newGroup);
|
groups.put(newGroup.getId(), newGroup);
|
||||||
|
|
||||||
@@ -271,15 +266,16 @@ public abstract class PwDatabase {
|
|||||||
|
|
||||||
public void removeGroupFrom(PwGroup remove, PwGroup parent) {
|
public void removeGroupFrom(PwGroup remove, PwGroup parent) {
|
||||||
// Remove tree from parent tree
|
// Remove tree from parent tree
|
||||||
parent.childGroups.remove(remove);
|
if (parent != null) {
|
||||||
|
parent.removeChildGroup(remove);
|
||||||
|
}
|
||||||
groups.remove(remove.getId());
|
groups.remove(remove.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addEntryTo(PwEntry newEntry, PwGroup parent) {
|
public void addEntryTo(PwEntry newEntry, PwGroup parent) {
|
||||||
// Add entry to parent
|
// Add entry to parent
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent.childEntries.add(newEntry);
|
parent.addChildEntry(newEntry);
|
||||||
}
|
}
|
||||||
newEntry.setParent(parent);
|
newEntry.setParent(parent);
|
||||||
|
|
||||||
@@ -289,7 +285,7 @@ public abstract class PwDatabase {
|
|||||||
public void removeEntryFrom(PwEntry remove, PwGroup parent) {
|
public void removeEntryFrom(PwEntry remove, PwGroup parent) {
|
||||||
// Remove entry for parent
|
// Remove entry for parent
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent.childEntries.remove(remove);
|
parent.removeChildEntry(remove);
|
||||||
}
|
}
|
||||||
entries.remove(remove.getUUID());
|
entries.remove(remove.getUUID());
|
||||||
}
|
}
|
||||||
@@ -322,8 +318,8 @@ public abstract class PwDatabase {
|
|||||||
|
|
||||||
public void populateGlobals(PwGroup currentGroup) {
|
public void populateGlobals(PwGroup currentGroup) {
|
||||||
|
|
||||||
List<PwGroup> childGroups = currentGroup.childGroups;
|
List<PwGroup> childGroups = currentGroup.getChildGroups();
|
||||||
List<PwEntry> childEntries = currentGroup.childEntries;
|
List<PwEntry> childEntries = currentGroup.getChildEntries();
|
||||||
|
|
||||||
for (int i = 0; i < childEntries.size(); i++ ) {
|
for (int i = 0; i < childEntries.size(); i++ ) {
|
||||||
PwEntry cur = childEntries.get(i);
|
PwEntry cur = childEntries.get(i);
|
||||||
@@ -337,28 +333,73 @@ public abstract class PwDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if RecycleBin is available or not for this version of database
|
||||||
|
* @return true if RecycleBin enable
|
||||||
|
*/
|
||||||
|
public boolean isRecycleBinAvailable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if RecycleBin is enable or not
|
||||||
|
* @return true if RecycleBin enable, false if is not available or not enable
|
||||||
|
*/
|
||||||
|
public boolean isRecycleBinEnable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define if a Group must be delete or recycle
|
||||||
|
* @param group Group to remove
|
||||||
|
* @return true if group can be recycle, false elsewhere
|
||||||
|
*/
|
||||||
public boolean canRecycle(PwGroup group) {
|
public boolean canRecycle(PwGroup group) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define if an Entry must be delete or recycle
|
||||||
|
* @param entry Entry to remove
|
||||||
|
* @return true if entry can be recycle, false elsewhere
|
||||||
|
*/
|
||||||
public boolean canRecycle(PwEntry entry) {
|
public boolean canRecycle(PwEntry entry) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void recycle(PwGroup group) {
|
||||||
|
// Assume calls to this are protected by calling inRecyleBin
|
||||||
|
throw new RuntimeException("Call not valid for .kdb databases.");
|
||||||
|
}
|
||||||
|
|
||||||
public void recycle(PwEntry entry) {
|
public void recycle(PwEntry entry) {
|
||||||
// Assume calls to this are protected by calling inRecyleBin
|
// Assume calls to this are protected by calling inRecyleBin
|
||||||
throw new RuntimeException("Call not valid for .kdb databases.");
|
throw new RuntimeException("Call not valid for .kdb databases.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void undoRecycle(PwGroup group, PwGroup origParent) {
|
||||||
|
throw new RuntimeException("Call not valid for .kdb databases.");
|
||||||
|
}
|
||||||
|
|
||||||
public void undoRecycle(PwEntry entry, PwGroup origParent) {
|
public void undoRecycle(PwEntry entry, PwGroup origParent) {
|
||||||
throw new RuntimeException("Call not valid for .kdb databases.");
|
throw new RuntimeException("Call not valid for .kdb databases.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteGroup(PwGroup group) {
|
||||||
|
PwGroup parent = group.getParent();
|
||||||
|
removeGroupFrom(group, parent);
|
||||||
|
parent.touch(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteEntry(PwEntry entry) {
|
public void deleteEntry(PwEntry entry) {
|
||||||
PwGroup parent = entry.getParent();
|
PwGroup parent = entry.getParent();
|
||||||
removeEntryFrom(entry, parent);
|
removeEntryFrom(entry, parent);
|
||||||
parent.touch(false, true);
|
parent.touch(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Delete group
|
||||||
|
public void undoDeleteGroup(PwGroup group, PwGroup origParent) {
|
||||||
|
addGroupTo(group, origParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void undoDeleteEntry(PwEntry entry, PwGroup origParent) {
|
public void undoDeleteEntry(PwEntry entry, PwGroup origParent) {
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ import java.io.InputStream;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
||||||
|
|
||||||
@@ -70,13 +69,13 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
public PwEntry metaInfo;
|
public PwEntry metaInfo;
|
||||||
|
|
||||||
// all entries
|
// all entries
|
||||||
public List<PwEntry> entries = new ArrayList<PwEntry>();
|
public List<PwEntry> entries = new ArrayList<>();
|
||||||
// all groups
|
// all groups
|
||||||
public List<PwGroup> groups = new ArrayList<PwGroup>();
|
public List<PwGroup> groups = new ArrayList<>();
|
||||||
// Algorithm used to encrypt the database
|
// Algorithm used to encrypt the database
|
||||||
public PwEncryptionAlgorithm algorithm;
|
public PwEncryptionAlgorithm algorithm;
|
||||||
public int numKeyEncRounds;
|
public int numKeyEncRounds;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwEncryptionAlgorithm getEncAlgorithm() {
|
public PwEncryptionAlgorithm getEncAlgorithm() {
|
||||||
return algorithm;
|
return algorithm;
|
||||||
@@ -106,7 +105,7 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
List<PwGroup> kids = new ArrayList<PwGroup>();
|
List<PwGroup> kids = new ArrayList<PwGroup>();
|
||||||
for (int i = 0; i < groups.size(); i++) {
|
for (int i = 0; i < groups.size(); i++) {
|
||||||
PwGroupV3 grp = (PwGroupV3) groups.get(i);
|
PwGroupV3 grp = (PwGroupV3) groups.get(i);
|
||||||
if (grp.level == target)
|
if (grp.getLevel() == target)
|
||||||
kids.add(grp);
|
kids.add(grp);
|
||||||
}
|
}
|
||||||
return kids;
|
return kids;
|
||||||
@@ -115,8 +114,8 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
public int getRootGroupId() {
|
public int getRootGroupId() {
|
||||||
for (int i = 0; i < groups.size(); i++) {
|
for (int i = 0; i < groups.size(); i++) {
|
||||||
PwGroupV3 grp = (PwGroupV3) groups.get(i);
|
PwGroupV3 grp = (PwGroupV3) groups.get(i);
|
||||||
if (grp.level == 0) {
|
if (grp.getLevel() == 0) {
|
||||||
return grp.groupId;
|
return grp.getGroupId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,13 +124,13 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
|
|
||||||
public List<PwGroup> getGrpChildren(PwGroupV3 parent) {
|
public List<PwGroup> getGrpChildren(PwGroupV3 parent) {
|
||||||
int idx = groups.indexOf(parent);
|
int idx = groups.indexOf(parent);
|
||||||
int target = parent.level + 1;
|
int target = parent.getLevel() + 1;
|
||||||
List<PwGroup> kids = new ArrayList<PwGroup>();
|
List<PwGroup> kids = new ArrayList<PwGroup>();
|
||||||
while (++idx < groups.size()) {
|
while (++idx < groups.size()) {
|
||||||
PwGroupV3 grp = (PwGroupV3) groups.get(idx);
|
PwGroupV3 grp = (PwGroupV3) groups.get(idx);
|
||||||
if (grp.level < target)
|
if (grp.getLevel() < target)
|
||||||
break;
|
break;
|
||||||
else if (grp.level == target)
|
else if (grp.getLevel() == target)
|
||||||
kids.add(grp);
|
kids.add(grp);
|
||||||
}
|
}
|
||||||
return kids;
|
return kids;
|
||||||
@@ -146,7 +145,7 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
*/
|
*/
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
PwEntryV3 ent = (PwEntryV3) entries.get(i);
|
PwEntryV3 ent = (PwEntryV3) entries.get(i);
|
||||||
if (ent.groupId == parent.groupId)
|
if (ent.getGroupId() == parent.getGroupId())
|
||||||
kids.add(ent);
|
kids.add(ent);
|
||||||
}
|
}
|
||||||
return kids;
|
return kids;
|
||||||
@@ -164,11 +163,11 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
|
|
||||||
List<PwGroup> rootChildGroups = getGrpRoots();
|
List<PwGroup> rootChildGroups = getGrpRoots();
|
||||||
root.setGroups(rootChildGroups);
|
root.setGroups(rootChildGroups);
|
||||||
root.childEntries = new ArrayList<PwEntry>();
|
root.setEntries(new ArrayList<>());
|
||||||
root.level = -1;
|
root.setLevel(-1);
|
||||||
for (int i = 0; i < rootChildGroups.size(); i++) {
|
for (int i = 0; i < rootChildGroups.size(); i++) {
|
||||||
PwGroupV3 grp = (PwGroupV3) rootChildGroups.get(i);
|
PwGroupV3 grp = (PwGroupV3) rootChildGroups.get(i);
|
||||||
grp.parent = root;
|
grp.setParent(root);
|
||||||
constructTree(grp);
|
constructTree(grp);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -177,18 +176,18 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
// I'm in non-root
|
// I'm in non-root
|
||||||
// get child groups
|
// get child groups
|
||||||
currentGroup.setGroups(getGrpChildren(currentGroup));
|
currentGroup.setGroups(getGrpChildren(currentGroup));
|
||||||
currentGroup.childEntries = getEntries(currentGroup);
|
currentGroup.setEntries(getEntries(currentGroup));
|
||||||
|
|
||||||
// set parent in child entries
|
// set parent in child entries
|
||||||
for (int i = 0; i < currentGroup.childEntries.size(); i++) {
|
for (int i = 0; i < currentGroup.numbersOfChildEntries(); i++) {
|
||||||
PwEntryV3 entry = (PwEntryV3) currentGroup.childEntries.get(i);
|
PwEntryV3 entry = (PwEntryV3) currentGroup.getChildEntryAt(i);
|
||||||
entry.parent = currentGroup;
|
entry.setParent(currentGroup);
|
||||||
}
|
}
|
||||||
// recursively construct child groups
|
// recursively construct child groups
|
||||||
for (int i = 0; i < currentGroup.childGroups.size(); i++) {
|
for (int i = 0; i < currentGroup.numbersOfChildGroups(); i++) {
|
||||||
PwGroupV3 grp = (PwGroupV3) currentGroup.childGroups.get(i);
|
PwGroupV3 grp = (PwGroupV3) currentGroup.getChildGroupAt(i);
|
||||||
grp.parent = currentGroup;
|
grp.setParent(currentGroup);
|
||||||
constructTree((PwGroupV3) currentGroup.childGroups.get(i));
|
constructTree((PwGroupV3) currentGroup.getChildGroupAt(i));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -222,18 +221,16 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
|
|
||||||
public byte[] getMasterKey(String key, InputStream keyInputStream)
|
public byte[] getMasterKey(String key, InputStream keyInputStream)
|
||||||
throws InvalidKeyFileException, IOException {
|
throws InvalidKeyFileException, IOException {
|
||||||
assert (key != null);
|
|
||||||
|
|
||||||
if (key.length() > 0 && keyInputStream != null) {
|
if (key != null && key.length() > 0 && keyInputStream != null) {
|
||||||
return getCompositeKey(key, keyInputStream);
|
return getCompositeKey(key, keyInputStream);
|
||||||
} else if (key.length() > 0) {
|
} else if (key != null && key.length() > 0) {
|
||||||
return getPasswordKey(key);
|
return getPasswordKey(key);
|
||||||
} else if (keyInputStream != null) {
|
} else if (keyInputStream != null) {
|
||||||
return getFileKey(keyInputStream);
|
return getFileKey(keyInputStream);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Key cannot be empty.");
|
throw new IllegalArgumentException("Key cannot be empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -263,7 +260,7 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean appSettingsEnabled() {
|
public boolean algorithmSettingsEnabled() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,11 +316,11 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
public boolean isBackup(PwGroup group) {
|
public boolean isBackup(PwGroup group) {
|
||||||
PwGroupV3 g = (PwGroupV3) group;
|
PwGroupV3 g = (PwGroupV3) group;
|
||||||
while (g != null) {
|
while (g != null) {
|
||||||
if (g.level == 0 && g.name.equalsIgnoreCase("Backup")) {
|
if (g.getLevel() == 0 && g.getName().equalsIgnoreCase("Backup")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
g = g.parent;
|
g = (PwGroupV3) g.getParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -341,7 +338,7 @@ public class PwDatabaseV3 extends PwDatabase {
|
|||||||
private void initAndAddGroup(String name, int iconId, PwGroup parent) {
|
private void initAndAddGroup(String name, int iconId, PwGroup parent) {
|
||||||
PwGroup group = createGroup();
|
PwGroup group = createGroup();
|
||||||
group.initNewGroup(name, newGroupId());
|
group.initNewGroup(name, newGroupId());
|
||||||
group.icon = iconFactory.getIcon(iconId);
|
group.setIcon(iconFactory.getIcon(iconId));
|
||||||
addGroupTo(group, parent);
|
addGroupTo(group, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,6 @@ import biz.source_code.base64Coder.Base64Coder;
|
|||||||
|
|
||||||
public class PwDatabaseV4 extends PwDatabase {
|
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;
|
public static final int DEFAULT_ROUNDS = 6000;
|
||||||
private static final int DEFAULT_HISTORY_MAX_ITEMS = 10; // -1 unlimited
|
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 long DEFAULT_HISTORY_MAX_SIZE = 6 * 1024 * 1024; // -1 unlimited
|
||||||
@@ -71,14 +69,14 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
public PwCompressionAlgorithm compressionAlgorithm = PwCompressionAlgorithm.Gzip;
|
public PwCompressionAlgorithm compressionAlgorithm = PwCompressionAlgorithm.Gzip;
|
||||||
// TODO: Refactor me away to get directly from kdfParameters
|
// TODO: Refactor me away to get directly from kdfParameters
|
||||||
public long numKeyEncRounds = 6000;
|
public long numKeyEncRounds = 6000;
|
||||||
public Date nameChanged = DEFAULT_NOW;
|
public Date nameChanged = new Date();
|
||||||
public Date settingsChanged = DEFAULT_NOW;
|
public Date settingsChanged = new Date();
|
||||||
public String description = "";
|
public String description = "";
|
||||||
public Date descriptionChanged = DEFAULT_NOW;
|
public Date descriptionChanged = new Date();
|
||||||
public String defaultUserName = "";
|
public String defaultUserName = "";
|
||||||
public Date defaultUserNameChanged = DEFAULT_NOW;
|
public Date defaultUserNameChanged = new Date();
|
||||||
|
|
||||||
public Date keyLastChanged = DEFAULT_NOW;
|
public Date keyLastChanged = new Date();
|
||||||
public long keyChangeRecDays = -1;
|
public long keyChangeRecDays = -1;
|
||||||
public long keyChangeForceDays = 1;
|
public long keyChangeForceDays = 1;
|
||||||
public boolean keyChangeForceOnce = false;
|
public boolean keyChangeForceOnce = false;
|
||||||
@@ -87,17 +85,17 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
public String color = "";
|
public String color = "";
|
||||||
public boolean recycleBinEnabled = true;
|
public boolean recycleBinEnabled = true;
|
||||||
public UUID recycleBinUUID = UUID_ZERO;
|
public UUID recycleBinUUID = UUID_ZERO;
|
||||||
public Date recycleBinChanged = DEFAULT_NOW;
|
public Date recycleBinChanged = new Date();
|
||||||
public UUID entryTemplatesGroup = UUID_ZERO;
|
public UUID entryTemplatesGroup = UUID_ZERO;
|
||||||
public Date entryTemplatesGroupChanged = DEFAULT_NOW;
|
public Date entryTemplatesGroupChanged = new Date();
|
||||||
public int historyMaxItems = DEFAULT_HISTORY_MAX_ITEMS;
|
public int historyMaxItems = DEFAULT_HISTORY_MAX_ITEMS;
|
||||||
public long historyMaxSize = DEFAULT_HISTORY_MAX_SIZE;
|
public long historyMaxSize = DEFAULT_HISTORY_MAX_SIZE;
|
||||||
public UUID lastSelectedGroup = UUID_ZERO;
|
public UUID lastSelectedGroup = UUID_ZERO;
|
||||||
public UUID lastTopVisibleGroup = UUID_ZERO;
|
public UUID lastTopVisibleGroup = UUID_ZERO;
|
||||||
public MemoryProtectionConfig memoryProtection = new MemoryProtectionConfig();
|
public MemoryProtectionConfig memoryProtection = new MemoryProtectionConfig();
|
||||||
public List<PwDeletedObject> deletedObjects = new ArrayList<PwDeletedObject>();
|
public List<PwDeletedObject> deletedObjects = new ArrayList<>();
|
||||||
public List<PwIconCustom> customIcons = new ArrayList<PwIconCustom>();
|
public List<PwIconCustom> customIcons = new ArrayList<>();
|
||||||
public Map<String, String> customData = new HashMap<String, String>();
|
public Map<String, String> customData = new HashMap<>();
|
||||||
public KdfParameters kdfParameters = KdfFactory.getDefaultParameters();
|
public KdfParameters kdfParameters = KdfFactory.getDefaultParameters();
|
||||||
public VariantDictionary publicCustomData = new VariantDictionary();
|
public VariantDictionary publicCustomData = new VariantDictionary();
|
||||||
public BinaryPool binPool = new BinaryPool();
|
public BinaryPool binPool = new BinaryPool();
|
||||||
@@ -269,25 +267,42 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
public List<PwGroup> getGroups() {
|
public List<PwGroup> getGroups() {
|
||||||
List<PwGroup> list = new ArrayList<PwGroup>();
|
List<PwGroup> list = new ArrayList<PwGroup>();
|
||||||
PwGroupV4 root = (PwGroupV4) rootGroup;
|
PwGroupV4 root = (PwGroupV4) rootGroup;
|
||||||
root.buildChildGroupsRecursive(list);
|
buildChildGroupsRecursive(root, list);
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void buildChildGroupsRecursive(PwGroupV4 root, List<PwGroup> list) {
|
||||||
|
list.add(root);
|
||||||
|
for ( int i = 0; i < root.numbersOfChildGroups(); i++) {
|
||||||
|
PwGroupV4 child = (PwGroupV4) root.getChildGroupAt(i);
|
||||||
|
buildChildGroupsRecursive(child, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PwGroup> getGrpRoots() {
|
public List<PwGroup> getGrpRoots() {
|
||||||
return rootGroup.childGroups;
|
return rootGroup.getChildGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PwEntry> getEntries() {
|
public List<PwEntry> getEntries() {
|
||||||
List<PwEntry> list = new ArrayList<PwEntry>();
|
List<PwEntry> list = new ArrayList<PwEntry>();
|
||||||
PwGroupV4 root = (PwGroupV4) rootGroup;
|
PwGroupV4 root = (PwGroupV4) rootGroup;
|
||||||
root.buildChildEntriesRecursive(list);
|
buildChildEntriesRecursive(root, list);
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void buildChildEntriesRecursive(PwGroupV4 root, List<PwEntry> list) {
|
||||||
|
for ( int i = 0; i < root.numbersOfChildEntries(); i++ ) {
|
||||||
|
list.add(root.getChildEntryAt(i));
|
||||||
|
}
|
||||||
|
for ( int i = 0; i < root.numbersOfChildGroups(); i++ ) {
|
||||||
|
PwGroupV4 child = (PwGroupV4) root.getChildGroupAt(i);
|
||||||
|
buildChildEntriesRecursive(child, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getNumRounds() {
|
public long getNumRounds() {
|
||||||
return numKeyEncRounds;
|
return numKeyEncRounds;
|
||||||
@@ -300,7 +315,7 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean appSettingsEnabled() {
|
public boolean algorithmSettingsEnabled() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,24 +366,32 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
if (getRecycleBin() == null) {
|
if (getRecycleBin() == null) {
|
||||||
// Create recycle bin
|
// Create recycle bin
|
||||||
|
|
||||||
PwGroupV4 recycleBin = new PwGroupV4(true, true, RECYCLEBIN_NAME, iconFactory.getIcon(PwIconStandard.TRASH_BIN));
|
PwGroupV4 recycleBin = new PwGroupV4(RECYCLEBIN_NAME, iconFactory.getIcon(PwIconStandard.TRASH_BIN));
|
||||||
recycleBin.enableAutoType = false;
|
recycleBin.setEnableAutoType(false);
|
||||||
recycleBin.enableSearching = false;
|
recycleBin.setEnableSearching(false);
|
||||||
recycleBin.isExpanded = false;
|
recycleBin.setExpanded(false);
|
||||||
addGroupTo(recycleBin, rootGroup);
|
addGroupTo(recycleBin, rootGroup);
|
||||||
|
|
||||||
recycleBinUUID = recycleBin.uuid;
|
recycleBinUUID = recycleBin.getUUID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecycleBinAvailable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecycleBinEnable() {
|
||||||
|
return recycleBinEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canRecycle(PwGroup group) {
|
public boolean canRecycle(PwGroup group) {
|
||||||
if (!recycleBinEnabled) {
|
if (!recycleBinEnabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PwGroup recycle = getRecycleBin();
|
PwGroup recycle = getRecycleBin();
|
||||||
|
|
||||||
return (recycle == null) || (!group.isContainedIn(recycle));
|
return (recycle == null) || (!group.isContainedIn(recycle));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,11 +400,25 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
if (!recycleBinEnabled) {
|
if (!recycleBinEnabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PwGroup parent = entry.getParent();
|
PwGroup parent = entry.getParent();
|
||||||
return (parent != null) && canRecycle(parent);
|
return (parent != null) && canRecycle(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recycle(PwGroup group) {
|
||||||
|
ensureRecycleBin();
|
||||||
|
|
||||||
|
PwGroup parent = group.getParent();
|
||||||
|
removeGroupFrom(group, parent);
|
||||||
|
parent.touch(false, true);
|
||||||
|
|
||||||
|
PwGroup recycleBin = getRecycleBin();
|
||||||
|
addGroupTo(group, recycleBin);
|
||||||
|
|
||||||
|
group.touch(false, true);
|
||||||
|
// TODO ? group.touchLocation();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recycle(PwEntry entry) {
|
public void recycle(PwEntry entry) {
|
||||||
ensureRecycleBin();
|
ensureRecycleBin();
|
||||||
@@ -397,6 +434,15 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
entry.touchLocation();
|
entry.touchLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undoRecycle(PwGroup group, PwGroup origParent) {
|
||||||
|
|
||||||
|
PwGroup recycleBin = getRecycleBin();
|
||||||
|
removeGroupFrom(group, recycleBin);
|
||||||
|
|
||||||
|
addGroupTo(group, origParent);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undoRecycle(PwEntry entry, PwGroup origParent) {
|
public void undoRecycle(PwEntry entry, PwGroup origParent) {
|
||||||
|
|
||||||
@@ -409,15 +455,14 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
@Override
|
@Override
|
||||||
public void deleteEntry(PwEntry entry) {
|
public void deleteEntry(PwEntry entry) {
|
||||||
super.deleteEntry(entry);
|
super.deleteEntry(entry);
|
||||||
|
|
||||||
deletedObjects.add(new PwDeletedObject(entry.getUUID()));
|
deletedObjects.add(new PwDeletedObject(entry.getUUID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undoDeleteEntry(PwEntry entry, PwGroup origParent) {
|
public void undoDeleteEntry(PwEntry entry, PwGroup origParent) {
|
||||||
super.undoDeleteEntry(entry, origParent);
|
super.undoDeleteEntry(entry, origParent);
|
||||||
|
// TODO undo delete entry
|
||||||
deletedObjects.remove(entry);
|
deletedObjects.remove(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -450,7 +495,7 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
public void initNew(String dbPath) {
|
public void initNew(String dbPath) {
|
||||||
String filename = URLUtil.guessFileName(dbPath, null, null);
|
String filename = URLUtil.guessFileName(dbPath, null, null);
|
||||||
|
|
||||||
rootGroup = new PwGroupV4(true, true, dbNameFromPath(dbPath), iconFactory.getIcon(PwIconStandard.FOLDER));
|
rootGroup = new PwGroupV4(dbNameFromPath(dbPath), iconFactory.getIcon(PwIconStandard.FOLDER));
|
||||||
groups.put(rootGroup.getId(), rootGroup);
|
groups.put(rootGroup.getId(), rootGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,7 +523,7 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
PwGroupV4 g4 = (PwGroupV4) group;
|
PwGroupV4 g4 = (PwGroupV4) group;
|
||||||
if (g4.customData.size() > 0) {
|
if (g4.containsCustomData()) {
|
||||||
hasCustomData = true;
|
hasCustomData = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -498,7 +543,7 @@ public class PwDatabaseV4 extends PwDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PwEntryV4 e4 = (PwEntryV4)entry;
|
PwEntryV4 e4 = (PwEntryV4)entry;
|
||||||
if (e4.customData.size() > 0) {
|
if (e4.containsCustomData()) {
|
||||||
hasCustomData = true;
|
hasCustomData = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,21 +19,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
import com.keepassdroid.app.App;
|
||||||
|
import com.keepassdroid.utils.Types;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import com.keepassdroid.app.App;
|
|
||||||
import com.keepassdroid.utils.Types;
|
|
||||||
|
|
||||||
/** Converting from the C Date format to the Java data format is
|
/** Converting from the C Date format to the Java data format is
|
||||||
* expensive when done for every record at once. I use this class to
|
* expensive when done for every record at once. I use this class to
|
||||||
* allow lazy conversions between the formats.
|
* allow lazy conversions between the formats.
|
||||||
* @author bpellin
|
* @author bpellin
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PwDate implements Cloneable {
|
public class PwDate implements Cloneable, Serializable {
|
||||||
|
|
||||||
private static final int DATE_SIZE = 5;
|
private static final int DATE_SIZE = 5;
|
||||||
|
|
||||||
private boolean cDateBuilt = false;
|
private boolean cDateBuilt = false;
|
||||||
@@ -41,6 +42,56 @@ public class PwDate implements Cloneable {
|
|||||||
|
|
||||||
private Date jDate;
|
private Date jDate;
|
||||||
private byte[] cDate;
|
private byte[] cDate;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 PwDate(byte[] buf, int offset) {
|
public PwDate(byte[] buf, int offset) {
|
||||||
cDate = new byte[DATE_SIZE];
|
cDate = new byte[DATE_SIZE];
|
||||||
@@ -58,12 +109,13 @@ public class PwDate implements Cloneable {
|
|||||||
jDateBuilt = true;
|
jDateBuilt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PwDate() {
|
public PwDate() {
|
||||||
|
jDate = new Date();
|
||||||
|
jDateBuilt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object clone() {
|
public PwDate clone() {
|
||||||
PwDate copy = new PwDate();
|
PwDate copy = new PwDate();
|
||||||
|
|
||||||
if ( cDateBuilt ) {
|
if ( cDateBuilt ) {
|
||||||
@@ -81,9 +133,7 @@ public class PwDate implements Cloneable {
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
|
||||||
public Date getJDate() {
|
|
||||||
if ( ! jDateBuilt ) {
|
if ( ! jDateBuilt ) {
|
||||||
jDate = readTime(cDate, 0, App.getCalendar());
|
jDate = readTime(cDate, 0, App.getCalendar());
|
||||||
jDateBuilt = true;
|
jDateBuilt = true;
|
||||||
@@ -101,7 +151,6 @@ public class PwDate implements Cloneable {
|
|||||||
return cDate;
|
return cDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unpack date from 5 byte format. The five bytes at 'offset' are unpacked
|
* Unpack date from 5 byte format. The five bytes at 'offset' are unpacked
|
||||||
* to a java.util.Date instance.
|
* to a java.util.Date instance.
|
||||||
@@ -188,7 +237,7 @@ public class PwDate implements Cloneable {
|
|||||||
} else if ( cDateBuilt && date.jDateBuilt ) {
|
} else if ( cDateBuilt && date.jDateBuilt ) {
|
||||||
return Arrays.equals(date.getCDate(), cDate);
|
return Arrays.equals(date.getCDate(), cDate);
|
||||||
} else {
|
} else {
|
||||||
return IsSameDate(date.getJDate(), jDate);
|
return IsSameDate(date.getDate(), jDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,36 +19,26 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import com.keepassdroid.database.iterator.EntrySearchStringIterator;
|
||||||
import java.util.Date;
|
import com.keepassdroid.database.security.ProtectedString;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import com.keepassdroid.database.iterator.EntrySearchStringIterator;
|
public abstract class PwEntry extends PwNode implements Cloneable {
|
||||||
import com.keepassdroid.utils.SprEngine;
|
|
||||||
|
|
||||||
public abstract class PwEntry implements Cloneable {
|
private static final String PMS_TAN_ENTRY = "<TAN>";
|
||||||
|
|
||||||
protected static final String PMS_TAN_ENTRY = "<TAN>";
|
protected UUID uuid = PwDatabase.UUID_ZERO;
|
||||||
|
|
||||||
public static class EntryNameComparator implements Comparator<PwEntry> {
|
|
||||||
|
|
||||||
public int compare(PwEntry object1, PwEntry object2) {
|
@Override
|
||||||
return object1.getTitle().compareToIgnoreCase(object2.getTitle());
|
protected void construct() {
|
||||||
}
|
super.construct();
|
||||||
|
uuid = UUID.randomUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PwIconStandard icon = PwIconStandard.FIRST;
|
|
||||||
|
|
||||||
public PwEntry() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PwEntry getInstance(PwGroup parent) {
|
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) {
|
if (parent instanceof PwGroupV3) {
|
||||||
return new PwEntryV3((PwGroupV3)parent);
|
return new PwEntryV3((PwGroupV3)parent);
|
||||||
}
|
}
|
||||||
@@ -59,86 +49,65 @@ public abstract class PwEntry implements Cloneable {
|
|||||||
throw new RuntimeException("Unknown PwGroup instance.");
|
throw new RuntimeException("Unknown PwGroup instance.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object clone() {
|
public PwEntry clone() {
|
||||||
PwEntry newEntry;
|
PwEntry newEntry;
|
||||||
try {
|
try {
|
||||||
newEntry = (PwEntry) super.clone();
|
newEntry = (PwEntry) super.clone();
|
||||||
} catch (CloneNotSupportedException e) {
|
} catch (CloneNotSupportedException e) {
|
||||||
assert(false);
|
|
||||||
throw new RuntimeException("Clone should be supported");
|
throw new RuntimeException("Clone should be supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
return newEntry;
|
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() {
|
@Override
|
||||||
return getPassword(false, null);
|
protected void addCloneAttributesToNewEntry(PwEntry newEntry) {
|
||||||
}
|
super.addCloneAttributesToNewEntry(newEntry);
|
||||||
|
// uuid is clone automatically (IMMUTABLE)
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assign(PwEntry source) {
|
||||||
|
super.assign(source);
|
||||||
|
uuid = source.uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getUUID() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUUID(UUID uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startToDecodeReference(PwDatabase db) {}
|
||||||
|
public void endToDecodeReference(PwDatabase db) {}
|
||||||
|
|
||||||
|
public abstract String getTitle();
|
||||||
|
public abstract void setTitle(String title);
|
||||||
|
|
||||||
|
public abstract String getUsername();
|
||||||
|
public abstract void setUsername(String user);
|
||||||
|
|
||||||
|
public abstract String getPassword();
|
||||||
|
public abstract void setPassword(String pass);
|
||||||
|
|
||||||
|
public abstract String getUrl();
|
||||||
|
public abstract void setUrl(String url);
|
||||||
|
|
||||||
|
public abstract String getNotes();
|
||||||
|
public abstract void setNotes(String notes);
|
||||||
|
|
||||||
public boolean isTan() {
|
private boolean isTan() {
|
||||||
return getTitle().equals(PMS_TAN_ENTRY) && (getUsername().length() > 0);
|
return getTitle().equals(PMS_TAN_ENTRY) && (getUsername().length() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getDisplayTitle() {
|
public String getDisplayTitle() {
|
||||||
if ( isTan() ) {
|
if ( isTan() ) {
|
||||||
return PMS_TAN_ENTRY + " " + getUsername();
|
return PMS_TAN_ENTRY + " " + getUsername();
|
||||||
@@ -147,18 +116,72 @@ public abstract class PwEntry implements Cloneable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO encapsulate extra fields
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To redefine if version of entry allow extra field,
|
||||||
|
* @return true if entry allows extra field
|
||||||
|
*/
|
||||||
|
public boolean allowExtraFields() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve extra fields to show, key is the label, value is the value of field
|
||||||
|
* @return Map of label/value
|
||||||
|
*/
|
||||||
|
public Map<String, String> getExtraFields() {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve extra protected fields to show, key is the label, value is the value protected of field
|
||||||
|
* @return Map of label/value
|
||||||
|
*/
|
||||||
|
public Map<String, ProtectedString> getExtraProtectedFields() {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If entry contains extra fields
|
||||||
|
* @return true if there is extra fields
|
||||||
|
*/
|
||||||
|
public boolean containsExtraFields() {
|
||||||
|
return !getExtraProtectedFields().keySet().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an extra field to the list
|
||||||
|
* @param label Label of field, must be unique
|
||||||
|
* @param value Value of field
|
||||||
|
*/
|
||||||
|
public void addField(String label, ProtectedString value) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all extra fields
|
||||||
|
*/
|
||||||
|
public void removeExtraFields() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If it's a node with only meta information like Meta-info SYSTEM Database Color
|
||||||
|
* @return false by default, true if it's a meta stream
|
||||||
|
*/
|
||||||
public boolean isMetaStream() {
|
public boolean isMetaStream() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a backup of entry
|
||||||
|
*/
|
||||||
|
public void createBackup(PwDatabase db) {}
|
||||||
|
|
||||||
public EntrySearchStringIterator stringIterator() {
|
public EntrySearchStringIterator stringIterator() {
|
||||||
return EntrySearchStringIterator.getInstance(this);
|
return EntrySearchStringIterator.getInstance(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void touch(boolean modified, boolean touchParents) {
|
public void touch(boolean modified, boolean touchParents) {
|
||||||
Date now = new Date();
|
PwDate now = new PwDate();
|
||||||
|
|
||||||
setLastAccessTime(now);
|
setLastAccessTime(now);
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
@@ -173,10 +196,22 @@ public abstract class PwEntry implements Cloneable {
|
|||||||
|
|
||||||
public void touchLocation() { }
|
public void touchLocation() { }
|
||||||
|
|
||||||
public abstract void setParent(PwGroup parent);
|
|
||||||
|
|
||||||
public boolean isSearchingEnabled() {
|
public boolean isSearchingEnabled() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
PwEntry pwEntry = (PwEntry) o;
|
||||||
|
return isSameType(pwEntry)
|
||||||
|
&& (getUUID() != null ? getUUID().equals(pwEntry.getUUID()) : pwEntry.getUUID() == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getUUID() != null ? getUUID().hashCode() : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,15 +42,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
|
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
// PhoneID
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import com.keepassdroid.utils.Types;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure containing information about one entry.
|
* Structure containing information about one entry.
|
||||||
@@ -71,138 +65,204 @@ import com.keepassdroid.utils.Types;
|
|||||||
* @author Naomaru Itoi <nao@phoneid.org>
|
* @author Naomaru Itoi <nao@phoneid.org>
|
||||||
* @author Bill Zwicky <wrzwicky@pobox.com>
|
* @author Bill Zwicky <wrzwicky@pobox.com>
|
||||||
* @author Dominik Reichl <dominik.reichl@t-online.de>
|
* @author Dominik Reichl <dominik.reichl@t-online.de>
|
||||||
|
* @author Jeremy Jamet <jeremy.jamet@kunzisoft.com>
|
||||||
*/
|
*/
|
||||||
public class PwEntryV3 extends PwEntry {
|
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. */
|
/** Size of byte buffer needed to hold this struct. */
|
||||||
public static final String PMS_ID_BINDESC = "bin-stream";
|
private static final String PMS_ID_BINDESC = "bin-stream";
|
||||||
public static final String PMS_ID_TITLE = "Meta-Info";
|
private static final String PMS_ID_TITLE = "Meta-Info";
|
||||||
public static final String PMS_ID_USER = "SYSTEM";
|
private static final String PMS_ID_USER = "SYSTEM";
|
||||||
public static final String PMS_ID_URL = "$";
|
private static final String PMS_ID_URL = "$";
|
||||||
|
|
||||||
|
// for tree traversing
|
||||||
|
private PwGroupV3 parent = null;
|
||||||
|
private int groupId;
|
||||||
|
|
||||||
|
private String title;
|
||||||
public int groupId;
|
private String username;
|
||||||
public String username;
|
private byte[] password;
|
||||||
private byte[] password;
|
private String url;
|
||||||
private byte[] uuid;
|
private String additional;
|
||||||
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 */
|
/** A string describing what is in pBinaryData */
|
||||||
public String binaryDesc;
|
private String binaryDesc;
|
||||||
private byte[] binaryData;
|
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() {
|
public PwEntryV3() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
public PwEntryV3(PwEntryV3 source) {
|
|
||||||
assign(source);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public PwEntryV3(PwGroupV3 p) {
|
public PwEntryV3(PwGroupV3 p) {
|
||||||
this(p, true, true);
|
construct();
|
||||||
}
|
|
||||||
|
|
||||||
public PwEntryV3(PwGroupV3 p, boolean initId, boolean initDates) {
|
|
||||||
|
|
||||||
parent = p;
|
parent = p;
|
||||||
groupId = ((PwGroupIdV3)parent.getId()).getId();
|
groupId = ((PwGroupIdV3)parent.getId()).getId(); // TODO remove
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void assign(PwEntry source) {
|
||||||
|
if ( ! (source instanceof PwEntryV3) ) {
|
||||||
|
throw new RuntimeException("DB version mix");
|
||||||
|
}
|
||||||
|
super.assign(source);
|
||||||
|
PwEntryV3 src = (PwEntryV3) source;
|
||||||
|
parent = src.parent;
|
||||||
|
groupId = src.groupId;
|
||||||
|
|
||||||
|
title = src.title;
|
||||||
|
username = src.username;
|
||||||
|
int passLen = src.password.length;
|
||||||
|
password = new byte[passLen];
|
||||||
|
System.arraycopy(src.password, 0, password, 0, passLen);
|
||||||
|
url = src.url;
|
||||||
|
additional = src.additional;
|
||||||
|
|
||||||
|
binaryDesc = src.binaryDesc;
|
||||||
|
if ( src.binaryData != null ) {
|
||||||
|
int descLen = src.binaryData.length;
|
||||||
|
binaryData = new byte[descLen];
|
||||||
|
System.arraycopy(src.binaryData, 0, binaryData, 0, descLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PwEntryV3 clone() {
|
||||||
|
PwEntryV3 newEntry = (PwEntryV3) super.clone();
|
||||||
|
|
||||||
|
// Attributes in parent
|
||||||
|
addCloneAttributesToNewEntry(newEntry);
|
||||||
|
|
||||||
|
// Attributes here
|
||||||
|
// newEntry.parent stay the same in copy
|
||||||
|
// newEntry.groupId stay the same in copy
|
||||||
|
// newEntry.title stay the same in copy
|
||||||
|
// newEntry.username stay the same in copy
|
||||||
|
if (password != null) {
|
||||||
|
int passLen = password.length;
|
||||||
|
password = new byte[passLen];
|
||||||
|
System.arraycopy(password, 0, newEntry.password, 0, passLen);
|
||||||
|
}
|
||||||
|
// newEntry.url stay the same in copy
|
||||||
|
// newEntry.additional stay the same in copy
|
||||||
|
|
||||||
|
// newEntry.binaryDesc stay the same in copy
|
||||||
|
if ( binaryData != null ) {
|
||||||
|
int descLen = binaryData.length;
|
||||||
|
newEntry.binaryData = new byte[descLen];
|
||||||
|
System.arraycopy(binaryData, 0, newEntry.binaryData, 0, descLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PwGroupV3 getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParent(PwGroup parent) {
|
||||||
|
this.parent = (PwGroupV3) parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupId(int groupId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
if (username == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsername(String user) {
|
||||||
|
username = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNotes() {
|
||||||
|
return additional;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNotes(String notes) {
|
||||||
|
additional = notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void populateBlankFields(PwDatabaseV3 db) {
|
||||||
|
// TODO verify and remove
|
||||||
|
if (icon == null) {
|
||||||
|
icon = db.iconFactory.getIcon(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username == null) {
|
||||||
|
username = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password == null) {
|
||||||
|
password = new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuid == null) {
|
||||||
|
uuid = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (title == null) {
|
||||||
|
title = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url == null) {
|
||||||
|
url = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additional == null) {
|
||||||
|
additional = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binaryDesc == null) {
|
||||||
|
binaryDesc = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binaryData == null) {
|
||||||
|
binaryData = new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the actual password byte array.
|
* @return the actual password byte array.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getPassword(boolean decodeRef, PwDatabase db) {
|
public String getPassword() {
|
||||||
if (password == null) {
|
if (password == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return new String(password);
|
return new String(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +270,6 @@ public class PwEntryV3 extends PwEntry {
|
|||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fill byte array
|
* fill byte array
|
||||||
*/
|
*/
|
||||||
@@ -231,10 +290,8 @@ public class PwEntryV3 extends PwEntry {
|
|||||||
System.arraycopy( buf, offset, password, 0, len );
|
System.arraycopy( buf, offset, password, 0, len );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPassword(String pass, PwDatabase db) {
|
public void setPassword(String pass) {
|
||||||
byte[] password;
|
byte[] password;
|
||||||
try {
|
try {
|
||||||
password = pass.getBytes("UTF-8");
|
password = pass.getBytes("UTF-8");
|
||||||
@@ -253,8 +310,6 @@ public class PwEntryV3 extends PwEntry {
|
|||||||
return binaryData;
|
return binaryData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Securely erase old data before copying new. */
|
/** Securely erase old data before copying new. */
|
||||||
public void setBinaryData( byte[] buf, int offset, int len ) {
|
public void setBinaryData( byte[] buf, int offset, int len ) {
|
||||||
if( binaryData != null ) {
|
if( binaryData != null ) {
|
||||||
@@ -265,6 +320,14 @@ public class PwEntryV3 extends PwEntry {
|
|||||||
System.arraycopy( buf, offset, binaryData, 0, len );
|
System.arraycopy( buf, offset, binaryData, 0, len );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getBinaryDesc() {
|
||||||
|
return binaryDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBinaryDesc(String binaryDesc) {
|
||||||
|
this.binaryDesc = binaryDesc;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine if this is a MetaStream entry
|
// Determine if this is a MetaStream entry
|
||||||
@Override
|
@Override
|
||||||
public boolean isMetaStream() {
|
public boolean isMetaStream() {
|
||||||
@@ -281,250 +344,4 @@ public class PwEntryV3 extends PwEntry {
|
|||||||
|
|
||||||
return true;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,313 +19,225 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
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.ProtectedBinary;
|
||||||
import com.keepassdroid.database.security.ProtectedString;
|
import com.keepassdroid.database.security.ProtectedString;
|
||||||
import com.keepassdroid.utils.SprEngine;
|
import com.keepassdroid.utils.SprEngineV4;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
public class PwEntryV4 extends PwEntry implements ITimeLogger {
|
public class PwEntryV4 extends PwEntry implements ITimeLogger {
|
||||||
|
|
||||||
public static final String STR_TITLE = "Title";
|
public static final String STR_TITLE = "Title";
|
||||||
public static final String STR_USERNAME = "UserName";
|
public static final String STR_USERNAME = "UserName";
|
||||||
public static final String STR_PASSWORD = "Password";
|
public static final String STR_PASSWORD = "Password";
|
||||||
public static final String STR_URL = "URL";
|
public static final String STR_URL = "URL";
|
||||||
public static final String STR_NOTES = "Notes";
|
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 {
|
// To decode each field not serializable
|
||||||
private static final long OBF_OPT_NONE = 0;
|
private transient PwDatabase mDatabase = null;
|
||||||
|
private transient boolean mDecodeRef = false;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private PwGroupV4 parent;
|
||||||
|
private PwIconCustom customIcon = PwIconCustom.ZERO;
|
||||||
|
private long usageCount = 0;
|
||||||
|
private PwDate parentGroupLastMod = new PwDate();
|
||||||
|
private Map<String, String> customData = new HashMap<>();
|
||||||
|
|
||||||
|
private HashMap<String, ProtectedString> fields = new HashMap<>();
|
||||||
|
private HashMap<String, ProtectedBinary> binaries = new HashMap<>();
|
||||||
|
private String foregroundColor = "";
|
||||||
|
private String backgroupColor = "";
|
||||||
|
private String overrideURL = "";
|
||||||
|
private AutoType autoType = new AutoType();
|
||||||
|
private ArrayList<PwEntryV4> history = new ArrayList<>();
|
||||||
|
|
||||||
|
private String url = "";
|
||||||
|
private String additional = "";
|
||||||
|
private String tags = "";
|
||||||
|
|
||||||
public PwEntryV4() {
|
public PwEntryV4() {
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PwEntryV4(PwGroupV4 p) {
|
public PwEntryV4(PwGroupV4 p) {
|
||||||
this(p, true, true);
|
construct();
|
||||||
}
|
|
||||||
|
|
||||||
public PwEntryV4(PwGroupV4 p, boolean initId, boolean initDates) {
|
|
||||||
parent = p;
|
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 void assign(PwEntry source) {
|
||||||
|
if ( ! (source instanceof PwEntryV4) ) {
|
||||||
|
throw new RuntimeException("DB version mix.");
|
||||||
|
}
|
||||||
|
super.assign(source);
|
||||||
|
PwEntryV4 src = (PwEntryV4) source;
|
||||||
|
parent = src.parent;
|
||||||
|
customIcon = src.customIcon;
|
||||||
|
usageCount = src.usageCount;
|
||||||
|
parentGroupLastMod = src.parentGroupLastMod;
|
||||||
|
// TODO customData
|
||||||
|
|
||||||
|
fields = src.fields;
|
||||||
|
binaries = src.binaries;
|
||||||
|
foregroundColor = src.foregroundColor;
|
||||||
|
backgroupColor = src.backgroupColor;
|
||||||
|
overrideURL = src.overrideURL;
|
||||||
|
autoType = src.autoType;
|
||||||
|
history = src.history;
|
||||||
|
|
||||||
|
url = src.url;
|
||||||
|
additional = src.additional;
|
||||||
|
tags = src.tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public PwEntryV4 clone() {
|
||||||
|
PwEntryV4 newEntry = (PwEntryV4) super.clone();
|
||||||
|
|
||||||
|
// Attributes in parent
|
||||||
|
addCloneAttributesToNewEntry(newEntry);
|
||||||
|
|
||||||
|
// Attributes here
|
||||||
|
// newEntry.parent stay the same in copy
|
||||||
|
newEntry.customIcon = new PwIconCustom(this.customIcon);
|
||||||
|
// newEntry.usageCount stay the same in copy
|
||||||
|
newEntry.parentGroupLastMod = this.parentGroupLastMod.clone();
|
||||||
|
// TODO customData make copy from hashmap
|
||||||
|
|
||||||
|
newEntry.fields = (HashMap<String, ProtectedString>) this.fields.clone();
|
||||||
|
newEntry.binaries = (HashMap<String, ProtectedBinary>) this.binaries.clone();
|
||||||
|
// newEntry.foregroundColor stay the same in copy
|
||||||
|
// newEntry.backgroupColor stay the same in copy
|
||||||
|
// newEntry.overrideURL stay the same in copy
|
||||||
|
newEntry.autoType = autoType.clone();
|
||||||
|
newEntry.history = (ArrayList<PwEntryV4>) this.history.clone();
|
||||||
|
|
||||||
|
// newEntry.url stay the same in copy
|
||||||
|
// newEntry.additional stay the same in copy
|
||||||
|
// newEntry.tags stay the same in copy
|
||||||
|
|
||||||
|
return newEntry;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwEntry clone(boolean deepStrings) {
|
public void startToDecodeReference(PwDatabase db) {
|
||||||
PwEntryV4 entry = (PwEntryV4) super.clone(deepStrings);
|
this.mDatabase = db;
|
||||||
|
this.mDecodeRef = true;
|
||||||
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
|
@Override
|
||||||
public void assign(PwEntry source) {
|
public void endToDecodeReference(PwDatabase db) {
|
||||||
|
this.mDatabase = null;
|
||||||
if ( ! (source instanceof PwEntryV4) ) {
|
this.mDecodeRef = false;
|
||||||
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
|
private String decodeRefKey(boolean decodeRef, String key) {
|
||||||
public Object clone() {
|
|
||||||
PwEntryV4 newEntry = (PwEntryV4) super.clone();
|
|
||||||
|
|
||||||
return newEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String decodeRefKey(boolean decodeRef, String key, PwDatabase db) {
|
|
||||||
String text = getString(key);
|
String text = getString(key);
|
||||||
if (decodeRef) {
|
if (decodeRef) {
|
||||||
text = decodeRef(text, db);
|
text = decodeRef(text, mDatabase);
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String decodeRef(String text, PwDatabase db) {
|
private String decodeRef(String text, PwDatabase db) {
|
||||||
if (db == null) { return text; }
|
if (db == null) { return text; }
|
||||||
|
SprEngineV4 spr = new SprEngineV4();
|
||||||
SprEngine spr = SprEngine.getInstance(db);
|
|
||||||
return spr.compile(text, this, db);
|
return spr.compile(text, this, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername(boolean decodeRef, PwDatabase db) {
|
public String getUsername() {
|
||||||
return decodeRefKey(decodeRef, STR_USERNAME, db);
|
return decodeRefKey(mDecodeRef, STR_USERNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTitle(boolean decodeRef, PwDatabase db) {
|
public String getTitle() {
|
||||||
return decodeRefKey(decodeRef, STR_TITLE, db);
|
return decodeRefKey(mDecodeRef, STR_TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPassword(boolean decodeRef, PwDatabase db) {
|
public String getPassword() {
|
||||||
return decodeRefKey(decodeRef, STR_PASSWORD, db);
|
return decodeRefKey(mDecodeRef, STR_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date getLastAccessTime() {
|
public void setTitle(String title) {
|
||||||
return lastAccess;
|
PwDatabaseV4 db = (PwDatabaseV4) mDatabase;
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
boolean protect = db.memoryProtection.protectTitle;
|
||||||
|
|
||||||
setString(STR_TITLE, title, protect);
|
setString(STR_TITLE, title, protect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUsername(String user, PwDatabase d) {
|
public void setUsername(String user) {
|
||||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
PwDatabaseV4 db = (PwDatabaseV4) mDatabase;
|
||||||
boolean protect = db.memoryProtection.protectUserName;
|
boolean protect = db.memoryProtection.protectUserName;
|
||||||
|
|
||||||
setString(STR_USERNAME, user, protect);
|
setString(STR_USERNAME, user, protect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPassword(String pass, PwDatabase d) {
|
public void setPassword(String pass) {
|
||||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
PwDatabaseV4 db = (PwDatabaseV4) mDatabase;
|
||||||
boolean protect = db.memoryProtection.protectPassword;
|
boolean protect = db.memoryProtection.protectPassword;
|
||||||
|
|
||||||
setString(STR_PASSWORD, pass, protect);
|
setString(STR_PASSWORD, pass, protect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUrl(String url, PwDatabase d) {
|
public void setUrl(String url) {
|
||||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
PwDatabaseV4 db = (PwDatabaseV4) mDatabase;
|
||||||
boolean protect = db.memoryProtection.protectUrl;
|
boolean protect = db.memoryProtection.protectUrl;
|
||||||
|
|
||||||
setString(STR_URL, url, protect);
|
setString(STR_URL, url, protect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setNotes(String notes, PwDatabase d) {
|
public void setNotes(String notes) {
|
||||||
PwDatabaseV4 db = (PwDatabaseV4) d;
|
PwDatabaseV4 db = (PwDatabaseV4) mDatabase;
|
||||||
boolean protect = db.memoryProtection.protectNotes;
|
boolean protect = db.memoryProtection.protectNotes;
|
||||||
|
|
||||||
setString(STR_NOTES, notes, protect);
|
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
|
@Override
|
||||||
public PwGroupV4 getParent() {
|
public PwGroupV4 getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID getUUID() {
|
public void setParent(PwGroup parent) {
|
||||||
return uuid;
|
this.parent = (PwGroupV4) parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUUID(UUID u) {
|
|
||||||
uuid = u;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getString(String key) {
|
public String getString(String key) {
|
||||||
ProtectedString value = strings.get(key);
|
ProtectedString value = fields.get(key);
|
||||||
|
|
||||||
if ( value == null ) return new String("");
|
if ( value == null ) return "";
|
||||||
|
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setString(String key, String value, boolean protect) {
|
public void setString(String key, String value, boolean protect) {
|
||||||
ProtectedString ps = new ProtectedString(protect, value);
|
ProtectedString ps = new ProtectedString(protect, value);
|
||||||
strings.put(key, ps);
|
fields.put(key, ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getLocationChanged() {
|
public PwIconCustom getCustomIcon() {
|
||||||
|
return customIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomIcon(PwIconCustom icon) {
|
||||||
|
this.customIcon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PwDate getLocationChanged() {
|
||||||
return parentGroupLastMod;
|
return parentGroupLastMod;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,112 +245,179 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger {
|
|||||||
return usageCount;
|
return usageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocationChanged(Date date) {
|
public void setLocationChanged(PwDate date) {
|
||||||
parentGroupLastMod = date;
|
parentGroupLastMod = date;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUsageCount(long count) {
|
public void setUsageCount(long count) {
|
||||||
usageCount = count;
|
usageCount = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean expires() {
|
|
||||||
return expires;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExpires(boolean exp) {
|
@Override
|
||||||
expires = exp;
|
public String getNotes() {
|
||||||
|
return decodeRefKey(mDecodeRef, STR_NOTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNotes(boolean decodeRef, PwDatabase db) {
|
public String getUrl() {
|
||||||
return decodeRefKey(decodeRef, STR_NOTES, db);
|
return decodeRefKey(mDecodeRef, STR_URL);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUrl(boolean decodeRef, PwDatabase db) {
|
|
||||||
return decodeRefKey(decodeRef, STR_URL, db);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwIcon getIcon() {
|
public PwIcon getIcon() {
|
||||||
if (customIcon == null || customIcon.uuid.equals(PwDatabaseV4.UUID_ZERO)) {
|
if (customIcon == null || customIcon.uuid.equals(PwDatabase.UUID_ZERO)) {
|
||||||
return super.getIcon();
|
return super.getIcon();
|
||||||
} else {
|
} else {
|
||||||
return customIcon;
|
return customIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean IsStandardString(String key) {
|
@Override
|
||||||
return key.equals(STR_TITLE) || key.equals(STR_USERNAME)
|
public boolean allowExtraFields() {
|
||||||
|| key.equals(STR_PASSWORD) || key.equals(STR_URL)
|
return true;
|
||||||
|| key.equals(STR_NOTES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createBackup(PwDatabaseV4 db) {
|
public Map<String, ProtectedString> getFields() {
|
||||||
PwEntryV4 copy = cloneDeep();
|
return fields;
|
||||||
copy.history = new ArrayList<PwEntryV4>();
|
}
|
||||||
history.add(copy);
|
|
||||||
|
@Override
|
||||||
if (db != null) maintainBackups(db);
|
public Map<String, ProtectedString> getExtraProtectedFields() {
|
||||||
}
|
Map<String, ProtectedString> protectedFields = super.getExtraProtectedFields();
|
||||||
|
if (fields.size() > 0) {
|
||||||
private boolean maintainBackups(PwDatabaseV4 db) {
|
for (Map.Entry<String, ProtectedString> pair : fields.entrySet()) {
|
||||||
boolean deleted = false;
|
String key = pair.getKey();
|
||||||
|
if (!PwEntryV4.isStandardField(key)) {
|
||||||
int maxItems = db.historyMaxItems;
|
protectedFields.put(key, pair.getValue());
|
||||||
if (maxItems >= 0) {
|
}
|
||||||
while (history.size() > maxItems) {
|
}
|
||||||
removeOldestBackup();
|
}
|
||||||
deleted = true;
|
return protectedFields;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
long maxSize = db.historyMaxSize;
|
public Map<String, String> getExtraFields() {
|
||||||
if (maxSize >= 0) {
|
Map<String, String> extraFields = super.getExtraFields();
|
||||||
while(true) {
|
SprEngineV4 spr = new SprEngineV4();
|
||||||
long histSize = 0;
|
// Display custom fields
|
||||||
for (PwEntryV4 entry : history) {
|
if (fields.size() > 0) {
|
||||||
histSize += entry.getSize();
|
for (Map.Entry<String, ProtectedString> pair : fields.entrySet()) {
|
||||||
}
|
String key = pair.getKey();
|
||||||
|
// TODO Add hidden style for protection field
|
||||||
if (histSize > maxSize) {
|
if (!PwEntryV4.isStandardField(key)) {
|
||||||
removeOldestBackup();
|
extraFields.put(key, spr.compile(pair.getValue().toString(), this, mDatabase));
|
||||||
deleted = true;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return extraFields;
|
||||||
return deleted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeOldestBackup() {
|
private static boolean isStandardField(String key) {
|
||||||
Date min = null;
|
return key.equals(STR_TITLE) || key.equals(STR_USERNAME)
|
||||||
int index = -1;
|
|| key.equals(STR_PASSWORD) || key.equals(STR_URL)
|
||||||
|
|| key.equals(STR_NOTES);
|
||||||
for (int i = 0; i < history.size(); i++) {
|
}
|
||||||
PwEntry entry = history.get(i);
|
|
||||||
Date lastMod = entry.getLastModificationTime();
|
public void addField(String label, ProtectedString value) {
|
||||||
if ((min == null) || lastMod.before(min)) {
|
fields.put(label, value);
|
||||||
index = i;
|
}
|
||||||
min = lastMod;
|
|
||||||
}
|
@Override
|
||||||
}
|
public void removeExtraFields() {
|
||||||
|
Iterator<Entry<String, ProtectedString>> iter = fields.entrySet().iterator();
|
||||||
if (index != -1) {
|
while (iter.hasNext()) {
|
||||||
history.remove(index);
|
Map.Entry<String, ProtectedString> pair = iter.next();
|
||||||
}
|
if (!PwEntryV4.isStandardField(pair.getKey())) {
|
||||||
}
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
private static final long FIXED_LENGTH_SIZE = 128; // Approximate fixed length size
|
}
|
||||||
|
|
||||||
|
public HashMap<String, ProtectedBinary> getBinaries() {
|
||||||
|
return binaries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putProtectedBinary(String key, ProtectedBinary value) {
|
||||||
|
binaries.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getForegroundColor() {
|
||||||
|
return foregroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setForegroundColor(String color) {
|
||||||
|
this.foregroundColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBackgroupColor() {
|
||||||
|
return backgroupColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackgroupColor(String color) {
|
||||||
|
this.backgroupColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOverrideURL() {
|
||||||
|
return overrideURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOverrideURL(String overrideURL) {
|
||||||
|
this.overrideURL = overrideURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AutoType getAutoType() {
|
||||||
|
return autoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoType(AutoType autoType) {
|
||||||
|
this.autoType = autoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<PwEntryV4> getHistory() {
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHistory(ArrayList<PwEntryV4> history) {
|
||||||
|
this.history = history;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToHistory(PwEntryV4 entry) {
|
||||||
|
history.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sizeOfHistory() {
|
||||||
|
return history.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAdditional() {
|
||||||
|
return additional;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdditional(String additional) {
|
||||||
|
this.additional = additional;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTags() {
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTags(String tags) {
|
||||||
|
this.tags = tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putCustomData(String key, String value) {
|
||||||
|
customData.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsCustomData() {
|
||||||
|
return customData.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long FIXED_LENGTH_SIZE = 128; // Approximate fixed length size
|
||||||
public long getSize() {
|
public long getSize() {
|
||||||
long size = FIXED_LENGTH_SIZE;
|
long size = FIXED_LENGTH_SIZE;
|
||||||
|
|
||||||
for (Entry<String, ProtectedString> pair : strings.entrySet()) {
|
for (Entry<String, ProtectedString> pair : fields.entrySet()) {
|
||||||
size += pair.getKey().length();
|
size += pair.getKey().length();
|
||||||
size += pair.getValue().length();
|
size += pair.getValue().length();
|
||||||
}
|
}
|
||||||
@@ -464,6 +443,68 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createBackup(PwDatabase db) {
|
||||||
|
super.createBackup(db);
|
||||||
|
|
||||||
|
PwEntryV4 copy = clone();
|
||||||
|
copy.history = new ArrayList<>();
|
||||||
|
history.add(copy);
|
||||||
|
|
||||||
|
if (db != null)
|
||||||
|
if (db instanceof PwDatabaseV4)
|
||||||
|
maintainBackups((PwDatabaseV4) 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().getDate();
|
||||||
|
if ((min == null) || lastMod.before(min)) {
|
||||||
|
index = i;
|
||||||
|
min = lastMod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index != -1) {
|
||||||
|
history.remove(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void touch(boolean modified, boolean touchParents) {
|
public void touch(boolean modified, boolean touchParents) {
|
||||||
super.touch(modified, touchParents);
|
super.touch(modified, touchParents);
|
||||||
@@ -473,19 +514,13 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void touchLocation() {
|
public void touchLocation() {
|
||||||
parentGroupLastMod = new Date();
|
parentGroupLastMod = new PwDate();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setParent(PwGroup parent) {
|
|
||||||
this.parent = (PwGroupV4) parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSearchingEnabled() {
|
public boolean isSearchingEnabled() {
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
return parent.isSearchEnabled();
|
return parent.isSearchEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
return PwGroupV4.DEFAULT_SEARCHING_ENABLED;
|
return PwGroupV4.DEFAULT_SEARCHING_ENABLED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,75 +19,126 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
import com.keepassdroid.utils.StrUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.keepassdroid.utils.StrUtil;
|
public abstract class PwGroup extends PwNode {
|
||||||
|
|
||||||
public abstract class PwGroup {
|
protected String name = "";
|
||||||
public List<PwGroup> childGroups = new ArrayList<PwGroup>();
|
|
||||||
public List<PwEntry> childEntries = new ArrayList<PwEntry>();
|
protected List<PwGroup> childGroups = new ArrayList<>();
|
||||||
public String name = "";
|
protected List<PwEntry> childEntries = new ArrayList<>();
|
||||||
public PwIconStandard icon;
|
|
||||||
|
public void initNewGroup(String nm, PwGroupId newId) {
|
||||||
|
setId(newId);
|
||||||
|
name = nm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PwGroup> getChildGroups() {
|
||||||
|
return childGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PwEntry> getChildEntries() {
|
||||||
|
return childEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroups(List<PwGroup> groups) {
|
||||||
|
childGroups = groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntries(List<PwEntry> entries) {
|
||||||
|
childEntries = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChildGroup(PwGroup group) {
|
||||||
|
this.childGroups.add(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChildEntry(PwEntry entry) {
|
||||||
|
this.childEntries.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo parameter type
|
||||||
|
public PwGroup getChildGroupAt(int number) {
|
||||||
|
return this.childGroups.get(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PwEntry getChildEntryAt(int number) {
|
||||||
|
return this.childEntries.get(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChildGroup(PwGroup group) {
|
||||||
|
this.childGroups.remove(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChildEntry(PwEntry entry) {
|
||||||
|
this.childEntries.remove(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int numbersOfChildGroups() {
|
||||||
|
return childGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int numbersOfChildEntries() {
|
||||||
|
return childEntries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter MetaStream entries and return children
|
||||||
|
* @return List of direct children (one level below) as PwNode
|
||||||
|
*/
|
||||||
|
public List<PwNode> getDirectChildren() {
|
||||||
|
List<PwNode> children = new ArrayList<>();
|
||||||
|
children.addAll(childGroups);
|
||||||
|
for(PwEntry child : childEntries) {
|
||||||
|
if (!child.isMetaStream())
|
||||||
|
children.add(child);
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isContainedIn(PwGroup container) {
|
||||||
|
PwGroup cur = this;
|
||||||
|
while (cur != null) {
|
||||||
|
if (cur == container) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cur = cur.getParent();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract PwGroup getParent();
|
|
||||||
public abstract void setParent(PwGroup parent);
|
|
||||||
|
|
||||||
public abstract PwGroupId getId();
|
public abstract PwGroupId getId();
|
||||||
public abstract void setId(PwGroupId id);
|
public abstract void setId(PwGroupId id);
|
||||||
|
|
||||||
public abstract String getName();
|
@Override
|
||||||
|
public String getDisplayTitle() {
|
||||||
public abstract Date getLastMod();
|
return getName();
|
||||||
|
}
|
||||||
public PwIcon getIcon() {
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sortGroupsByName() {
|
public String getName() {
|
||||||
Collections.sort(childGroups, new GroupNameComparator());
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GroupNameComparator implements Comparator<PwGroup> {
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
public int compare(PwGroup object1, PwGroup object2) {
|
public boolean allowAddEntryIfIsRoot() {
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void touch(boolean modified, boolean touchParents) {
|
public void touch(boolean modified, boolean touchParents) {
|
||||||
Date now = new Date();
|
PwDate now = new PwDate();
|
||||||
|
|
||||||
setLastAccessTime(now);
|
setLastAccessTime(now);
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
@@ -100,7 +151,6 @@ public abstract class PwGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void searchEntries(SearchParameters sp, List<PwEntry> listStorage) {
|
public void searchEntries(SearchParameters sp, List<PwEntry> listStorage) {
|
||||||
if (sp == null) { return; }
|
if (sp == null) { return; }
|
||||||
if (listStorage == null) { return; }
|
if (listStorage == null) { return; }
|
||||||
@@ -147,7 +197,6 @@ public abstract class PwGroup {
|
|||||||
complement.add(entry);
|
complement.add(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pg = complement;
|
pg = complement;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -181,19 +230,30 @@ public abstract class PwGroup {
|
|||||||
if (entryHandler != null) {
|
if (entryHandler != null) {
|
||||||
for (PwEntry entry : childEntries) {
|
for (PwEntry entry : childEntries) {
|
||||||
if (!entryHandler.operate(entry)) return false;
|
if (!entryHandler.operate(entry)) return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PwGroup group : childGroups) {
|
for (PwGroup group : childGroups) {
|
||||||
|
|
||||||
if ((groupHandler != null) && !groupHandler.operate(group)) return false;
|
if ((groupHandler != null) && !groupHandler.operate(group)) return false;
|
||||||
|
|
||||||
group.preOrderTraverseTree(groupHandler, entryHandler);
|
group.preOrderTraverseTree(groupHandler, entryHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
PwGroup pwGroup = (PwGroup) o;
|
||||||
|
return isSameType(pwGroup)
|
||||||
|
&& (getId() != null ? getId().equals(pwGroup.getId()) : pwGroup.getId() == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
PwGroupId groupId = getId();
|
||||||
|
return groupId != null ? groupId.hashCode() : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
public abstract class PwGroupId {
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public abstract class PwGroupId implements Serializable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,20 +32,17 @@ public class PwGroupIdV3 extends PwGroupId {
|
|||||||
if ( ! (compare instanceof PwGroupIdV3) ) {
|
if ( ! (compare instanceof PwGroupIdV3) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PwGroupIdV3 cmp = (PwGroupIdV3) compare;
|
PwGroupIdV3 cmp = (PwGroupIdV3) compare;
|
||||||
return id == cmp.id;
|
return id == cmp.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
Integer i = Integer.valueOf(id);
|
Integer i = id;
|
||||||
return i.hashCode();
|
return i.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,7 @@ public class PwGroupIdV4 extends PwGroupId {
|
|||||||
if ( ! (id instanceof PwGroupIdV4) ) {
|
if ( ! (id instanceof PwGroupIdV4) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PwGroupIdV4 v4 = (PwGroupIdV4) id;
|
PwGroupIdV4 v4 = (PwGroupIdV4) id;
|
||||||
|
|
||||||
return uuid.equals(v4.uuid);
|
return uuid.equals(v4.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,5 +26,4 @@ public class PwGroupIdV4 extends PwGroupId {
|
|||||||
public UUID getId() {
|
public UUID getId() {
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
* 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.
|
* This file is part of KeePass DX.
|
||||||
*
|
*
|
||||||
@@ -30,12 +20,6 @@ Copyright 2006 Bill Zwicky <billzwicky@users.sourceforge.net>
|
|||||||
|
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Brian Pellin <bpellin@gmail.com>
|
* @author Brian Pellin <bpellin@gmail.com>
|
||||||
* @author Naomaru Itoi <nao@phoneid.org>
|
* @author Naomaru Itoi <nao@phoneid.org>
|
||||||
@@ -43,42 +27,47 @@ import java.util.List;
|
|||||||
* @author Dominik Reichl <dominik.reichl@t-online.de>
|
* @author Dominik Reichl <dominik.reichl@t-online.de>
|
||||||
*/
|
*/
|
||||||
public class PwGroupV3 extends PwGroup {
|
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
|
// for tree traversing
|
||||||
public PwGroupV3 parent = null;
|
private PwGroupV3 parent = null;
|
||||||
|
private int groupId;
|
||||||
|
|
||||||
public int groupId;
|
private int level = 0; // short
|
||||||
|
|
||||||
public PwDate tCreation;
|
|
||||||
public PwDate tLastMod;
|
|
||||||
public PwDate tLastAccess;
|
|
||||||
public PwDate tExpire;
|
|
||||||
|
|
||||||
public int level; // short
|
|
||||||
|
|
||||||
/** Used by KeePass internally, don't use */
|
/** Used by KeePass internally, don't use */
|
||||||
public int flags;
|
private int flags;
|
||||||
|
|
||||||
public void setGroups(List<PwGroup> groups) {
|
public PwGroupV3() {
|
||||||
childGroups = groups;
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwGroup getParent() {
|
public PwGroup getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParent(PwGroup prt) {
|
||||||
|
parent = (PwGroupV3) prt;
|
||||||
|
level = parent.getLevel() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupId(int groupId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLevel() {
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevel(int level) {
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwGroupId getId() {
|
public PwGroupId getId() {
|
||||||
return new PwGroupIdV3(groupId);
|
return new PwGroupIdV3(groupId);
|
||||||
@@ -90,69 +79,27 @@ public class PwGroupV3 extends PwGroup {
|
|||||||
groupId = id3.getId();
|
groupId = id3.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public int getFlags() {
|
||||||
public String getName() {
|
return flags;
|
||||||
return name;
|
}
|
||||||
}
|
|
||||||
|
public void setFlags(int flags) {
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date getLastMod() {
|
public String toString() {
|
||||||
return tLastMod.getJDate();
|
return getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void populateBlankFields(PwDatabaseV3 db) {
|
||||||
public void setParent(PwGroup prt) {
|
// TODO populate blanck field
|
||||||
parent = (PwGroupV3) prt;
|
if (icon == null) {
|
||||||
level = parent.level + 1;
|
icon = db.iconFactory.getIcon(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (name == null) {
|
||||||
|
name = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,107 +19,84 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class PwGroupV4 extends PwGroup implements ITimeLogger {
|
public class PwGroupV4 extends PwGroup implements ITimeLogger {
|
||||||
|
|
||||||
//public static final int FOLDER_ICON = 48;
|
|
||||||
public static final boolean DEFAULT_SEARCHING_ENABLED = true;
|
public static final boolean DEFAULT_SEARCHING_ENABLED = true;
|
||||||
|
|
||||||
public PwGroupV4 parent = null;
|
private PwGroupV4 parent = null;
|
||||||
public UUID uuid = PwDatabaseV4.UUID_ZERO;
|
private UUID uuid = PwDatabase.UUID_ZERO;
|
||||||
public String notes = "";
|
private PwIconCustom customIcon = PwIconCustom.ZERO;
|
||||||
public PwIconCustom customIcon = PwIconCustom.ZERO;
|
private long usageCount = 0;
|
||||||
public boolean isExpanded = true;
|
private PwDate parentGroupLastMod = new PwDate();
|
||||||
public String defaultAutoTypeSequence = "";
|
private Map<String, String> customData = new HashMap<>();
|
||||||
public Boolean enableAutoType = null;
|
|
||||||
public Boolean enableSearching = null;
|
private boolean expires = false;
|
||||||
public UUID lastTopVisibleEntry = PwDatabaseV4.UUID_ZERO;
|
|
||||||
private Date parentGroupLastMod = PwDatabaseV4.DEFAULT_NOW;
|
private String notes = "";
|
||||||
private Date creation = PwDatabaseV4.DEFAULT_NOW;
|
private boolean isExpanded = true;
|
||||||
private Date lastMod = PwDatabaseV4.DEFAULT_NOW;
|
private String defaultAutoTypeSequence = "";
|
||||||
private Date lastAccess = PwDatabaseV4.DEFAULT_NOW;
|
private Boolean enableAutoType = null;
|
||||||
private Date expireDate = PwDatabaseV4.DEFAULT_NOW;
|
private Boolean enableSearching = null;
|
||||||
private boolean expires = false;
|
private UUID lastTopVisibleEntry = PwDatabase.UUID_ZERO;
|
||||||
private long usageCount = 0;
|
|
||||||
public Map<String, String> customData = new HashMap<String, String>();
|
|
||||||
|
|
||||||
public PwGroupV4() {
|
public PwGroupV4() {
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PwGroupV4(boolean createUUID, boolean setTimes, String name, PwIconStandard icon) {
|
public PwGroupV4(String name, PwIconStandard icon) {
|
||||||
if (createUUID) {
|
super.construct();
|
||||||
uuid = UUID.randomUUID();
|
this.uuid = UUID.randomUUID();
|
||||||
}
|
|
||||||
|
|
||||||
if (setTimes) {
|
|
||||||
creation = lastMod = lastAccess = new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initNewGroup(String nm, PwGroupId newId) {
|
||||||
|
super.initNewGroup(nm, newId);
|
||||||
|
parentGroupLastMod = new PwDate();
|
||||||
|
}
|
||||||
|
|
||||||
public void AddGroup(PwGroupV4 subGroup, boolean takeOwnership) {
|
public void AddGroup(PwGroupV4 subGroup) {
|
||||||
AddGroup(subGroup, takeOwnership, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddGroup(PwGroupV4 subGroup, boolean takeOwnership, boolean updateLocationChanged) {
|
|
||||||
if ( subGroup == null ) throw new RuntimeException("subGroup");
|
if ( subGroup == null ) throw new RuntimeException("subGroup");
|
||||||
|
|
||||||
childGroups.add(subGroup);
|
childGroups.add(subGroup);
|
||||||
|
subGroup.parent = this;
|
||||||
if ( takeOwnership ) subGroup.parent = this;
|
}
|
||||||
|
|
||||||
if ( updateLocationChanged ) subGroup.parentGroupLastMod = new Date(System.currentTimeMillis());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddEntry(PwEntryV4 pe, boolean takeOwnership) {
|
public void AddEntry(PwEntryV4 pe) {
|
||||||
AddEntry(pe, takeOwnership, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddEntry(PwEntryV4 pe, boolean takeOwnership, boolean updateLocationChanged) {
|
|
||||||
assert(pe != null);
|
assert(pe != null);
|
||||||
|
addChildEntry(pe);
|
||||||
childEntries.add(pe);
|
pe.setParent(this);
|
||||||
|
}
|
||||||
if ( takeOwnership ) pe.parent = this;
|
|
||||||
|
|
||||||
if ( updateLocationChanged ) pe.setLocationChanged(new Date(System.currentTimeMillis()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwGroup getParent() {
|
public PwGroup getParent() {
|
||||||
return parent;
|
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) {
|
@Override
|
||||||
for ( int i = 0; i < childEntries.size(); i++ ) {
|
public void setParent(PwGroup prt) {
|
||||||
list.add(childEntries.get(i));
|
parent = (PwGroupV4) prt;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int i = 0; i < childGroups.size(); i++ ) {
|
public UUID getUUID() {
|
||||||
PwGroupV4 child = (PwGroupV4) childGroups.get(i);
|
return uuid;
|
||||||
child.buildChildEntriesRecursive(list);
|
}
|
||||||
}
|
|
||||||
|
public void setUUID(UUID uuid) {
|
||||||
}
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PwIconCustom getCustomIcon() {
|
||||||
|
return customIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomIcon(PwIconCustom icon) {
|
||||||
|
this.customIcon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwGroupId getId() {
|
public PwGroupId getId() {
|
||||||
@@ -133,97 +110,106 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public PwDate getLocationChanged() {
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Date getLastMod() {
|
|
||||||
return parentGroupLastMod;
|
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
|
@Override
|
||||||
public void setLastAccessTime(Date date) {
|
public void setLocationChanged(PwDate date) {
|
||||||
lastAccess = date;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLastModificationTime(Date date) {
|
|
||||||
lastMod = date;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLocationChanged(Date date) {
|
|
||||||
parentGroupLastMod = date;
|
parentGroupLastMod = date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUsageCount() {
|
||||||
|
return usageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setUsageCount(long count) {
|
public void setUsageCount(long count) {
|
||||||
usageCount = count;
|
usageCount = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean expires() {
|
public boolean expires() {
|
||||||
return expires;
|
return expires;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setExpires(boolean exp) {
|
public void setExpires(boolean exp) {
|
||||||
expires = exp;
|
expires = exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setParent(PwGroup prt) {
|
public boolean allowAddEntryIfIsRoot() {
|
||||||
parent = (PwGroupV4) prt;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PwIcon getIcon() {
|
public PwIcon getIcon() {
|
||||||
if (customIcon == null || customIcon.uuid.equals(PwDatabaseV4.UUID_ZERO)) {
|
if (customIcon == null || customIcon.uuid.equals(PwDatabase.UUID_ZERO)) {
|
||||||
return super.getIcon();
|
return super.getIcon();
|
||||||
} else {
|
} else {
|
||||||
return customIcon;
|
return customIcon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void putCustomData(String key, String value) {
|
||||||
public void initNewGroup(String nm, PwGroupId newId) {
|
customData.put(key, value);
|
||||||
super.initNewGroup(nm, newId);
|
}
|
||||||
|
|
||||||
lastAccess = lastMod = creation = parentGroupLastMod = new Date();
|
public boolean containsCustomData() {
|
||||||
}
|
return customData.size() > 0;
|
||||||
|
}
|
||||||
public boolean isSearchEnabled() {
|
|
||||||
|
public String getNotes() {
|
||||||
|
return notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotes(String notes) {
|
||||||
|
this.notes = notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExpanded() {
|
||||||
|
return isExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpanded(boolean expanded) {
|
||||||
|
isExpanded = expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultAutoTypeSequence() {
|
||||||
|
return defaultAutoTypeSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultAutoTypeSequence(String defaultAutoTypeSequence) {
|
||||||
|
this.defaultAutoTypeSequence = defaultAutoTypeSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getEnableAutoType() {
|
||||||
|
return enableAutoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableAutoType(Boolean enableAutoType) {
|
||||||
|
this.enableAutoType = enableAutoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getEnableSearching() {
|
||||||
|
return enableSearching;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableSearching(Boolean enableSearching) {
|
||||||
|
this.enableSearching = enableSearching;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getLastTopVisibleEntry() {
|
||||||
|
return lastTopVisibleEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastTopVisibleEntry(UUID lastTopVisibleEntry) {
|
||||||
|
this.lastTopVisibleEntry = lastTopVisibleEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSearchEnabled() {
|
||||||
PwGroupV4 group = this;
|
PwGroupV4 group = this;
|
||||||
while (group != null) {
|
while (group != null) {
|
||||||
Boolean search = group.enableSearching;
|
Boolean search = group.enableSearching;
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package com.keepassdroid.database;
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
public abstract class PwIcon {
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public abstract class PwIcon implements Serializable {
|
||||||
|
|
||||||
public boolean isMetaStreamIcon() {
|
public boolean isMetaStreamIcon() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeBytes() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ package com.keepassdroid.database;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class PwIconCustom extends PwIcon {
|
public class PwIconCustom extends PwIcon {
|
||||||
public static final PwIconCustom ZERO = new PwIconCustom(PwDatabaseV4.UUID_ZERO, new byte[0]);
|
public static final PwIconCustom ZERO = new PwIconCustom(PwDatabase.UUID_ZERO, new byte[0]);
|
||||||
|
|
||||||
public final UUID uuid;
|
public final UUID uuid;
|
||||||
public byte[] imageData;
|
public byte[] imageData;
|
||||||
@@ -32,6 +32,11 @@ public class PwIconCustom extends PwIcon {
|
|||||||
imageData = data;
|
imageData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PwIconCustom(PwIconCustom icon) {
|
||||||
|
uuid = icon.uuid;
|
||||||
|
imageData = icon.imageData;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ public class PwIconStandard extends PwIcon {
|
|||||||
this.iconId = iconId;
|
this.iconId = iconId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PwIconStandard(PwIconStandard icon) {
|
||||||
|
this.iconId = icon.iconId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMetaStreamIcon() {
|
public boolean isMetaStreamIcon() {
|
||||||
return iconId == 0;
|
return iconId == 0;
|
||||||
|
|||||||
165
app/src/main/java/com/keepassdroid/database/PwNode.java
Normal file
165
app/src/main/java/com/keepassdroid/database/PwNode.java
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 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.io.Serializable;
|
||||||
|
|
||||||
|
import static com.keepassdroid.database.PwDate.NEVER_EXPIRE;
|
||||||
|
import static com.keepassdroid.database.PwDate.PW_NEVER_EXPIRE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class who manage Groups and Entries
|
||||||
|
*/
|
||||||
|
public abstract class PwNode implements ISmallTimeLogger, Serializable {
|
||||||
|
|
||||||
|
protected PwIconStandard icon = PwIconStandard.FIRST;
|
||||||
|
|
||||||
|
protected PwDate creation = new PwDate();
|
||||||
|
protected PwDate lastMod = new PwDate();
|
||||||
|
protected PwDate lastAccess = new PwDate();
|
||||||
|
protected PwDate expireDate = new PwDate(NEVER_EXPIRE);
|
||||||
|
|
||||||
|
protected void construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assign(PwNode source) {
|
||||||
|
this.icon = source.icon;
|
||||||
|
|
||||||
|
this.creation = source.creation;
|
||||||
|
this.lastMod = source.lastMod;
|
||||||
|
this.lastAccess = source.lastAccess;
|
||||||
|
this.expireDate = source.expireDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addCloneAttributesToNewEntry(PwEntry newEntry) {
|
||||||
|
newEntry.icon = new PwIconStandard(this.icon);
|
||||||
|
|
||||||
|
newEntry.creation = creation.clone();
|
||||||
|
newEntry.lastMod = lastMod.clone();
|
||||||
|
newEntry.lastAccess = lastAccess.clone();
|
||||||
|
newEntry.expireDate = expireDate.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of available Nodes
|
||||||
|
*/
|
||||||
|
public enum Type {
|
||||||
|
GROUP, ENTRY
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Type of Node
|
||||||
|
*/
|
||||||
|
public abstract Type getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Title to display as view
|
||||||
|
*/
|
||||||
|
public abstract String getDisplayTitle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Visual icon
|
||||||
|
*/
|
||||||
|
public PwIcon getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PwIconStandard getIconStandard() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(PwIconStandard icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the parent node
|
||||||
|
* @return PwGroup parent as group
|
||||||
|
*/
|
||||||
|
public abstract PwGroup getParent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign a parent to this node
|
||||||
|
*/
|
||||||
|
public abstract void setParent(PwGroup parent);
|
||||||
|
|
||||||
|
public PwDate getCreationTime() {
|
||||||
|
return creation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreationTime(PwDate date) {
|
||||||
|
creation = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PwDate getLastModificationTime() {
|
||||||
|
return lastMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastModificationTime(PwDate date) {
|
||||||
|
lastMod = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PwDate getLastAccessTime() {
|
||||||
|
return lastAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastAccessTime(PwDate date) {
|
||||||
|
lastAccess = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PwDate getExpiryTime() {
|
||||||
|
return expireDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpiryTime(PwDate date) {
|
||||||
|
expireDate = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpires(boolean expires) {
|
||||||
|
if (!expires) {
|
||||||
|
expireDate = PW_NEVER_EXPIRE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean expires() {
|
||||||
|
return ! PwDate.IsSameDate(NEVER_EXPIRE, expireDate.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the content (type, title, icon) is visually the same
|
||||||
|
* @param o Node to compare
|
||||||
|
* @return True if visually as o
|
||||||
|
*/
|
||||||
|
public boolean isContentVisuallyTheSame(PwNode o) {
|
||||||
|
return getType().equals(o.getType())
|
||||||
|
&& getDisplayTitle().equals(o.getDisplayTitle())
|
||||||
|
&& getIcon().equals(o.getIcon());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define if it's the same type of another node
|
||||||
|
* @param otherNode The other node to test
|
||||||
|
* @return true if both have the same type
|
||||||
|
*/
|
||||||
|
boolean isSameType(PwNode otherNode) {
|
||||||
|
return getType() != null ? getType().equals(otherNode.getType()) : otherNode.getType() == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.keepassdroid.database;
|
||||||
|
|
||||||
|
public enum PwVersion {
|
||||||
|
V3, V4
|
||||||
|
}
|
||||||
266
app/src/main/java/com/keepassdroid/database/SortNodeEnum.java
Normal file
266
app/src/main/java/com/keepassdroid/database/SortNodeEnum.java
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 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;
|
||||||
|
|
||||||
|
public enum SortNodeEnum {
|
||||||
|
DB, TITLE, USERNAME, CREATION_TIME, LAST_MODIFY_TIME, LAST_ACCESS_TIME;
|
||||||
|
|
||||||
|
public Comparator<PwNode> getNodeComparator(boolean ascending, boolean groupsBefore) {
|
||||||
|
switch (this) {
|
||||||
|
case DB:
|
||||||
|
return new NodeCreationComparator(ascending, groupsBefore); // TODO Sort
|
||||||
|
default:
|
||||||
|
case TITLE:
|
||||||
|
return new NodeTitleComparator(ascending, groupsBefore);
|
||||||
|
case USERNAME:
|
||||||
|
return new NodeCreationComparator(ascending, groupsBefore); // TODO Sort
|
||||||
|
case CREATION_TIME:
|
||||||
|
return new NodeCreationComparator(ascending, groupsBefore);
|
||||||
|
case LAST_MODIFY_TIME:
|
||||||
|
return new NodeCreationComparator(ascending, groupsBefore); // TODO Sort
|
||||||
|
case LAST_ACCESS_TIME:
|
||||||
|
return new NodeCreationComparator(ascending, groupsBefore); // TODO Sort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static abstract class NodeComparator implements Comparator<PwNode> {
|
||||||
|
boolean ascending;
|
||||||
|
boolean groupsBefore;
|
||||||
|
|
||||||
|
NodeComparator(boolean ascending, boolean groupsBefore) {
|
||||||
|
this.ascending = ascending;
|
||||||
|
this.groupsBefore = groupsBefore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparator of Node by Title, Groups first, Entries second
|
||||||
|
*/
|
||||||
|
public static class NodeTitleComparator extends NodeComparator {
|
||||||
|
|
||||||
|
public NodeTitleComparator(boolean ascending, boolean groupsBefore) {
|
||||||
|
super(ascending, groupsBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(PwNode object1, PwNode object2) {
|
||||||
|
if (object1.equals(object2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (object1 instanceof PwGroup) {
|
||||||
|
if (object2 instanceof PwGroup) {
|
||||||
|
return new GroupNameComparator(ascending)
|
||||||
|
.compare((PwGroup) object1, (PwGroup) object2);
|
||||||
|
} else if (object2 instanceof PwEntry) {
|
||||||
|
if(groupsBefore)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (object1 instanceof PwEntry) {
|
||||||
|
if(object2 instanceof PwEntry) {
|
||||||
|
return new EntryNameComparator(ascending)
|
||||||
|
.compare((PwEntry) object1, (PwEntry) object2);
|
||||||
|
} else if (object2 instanceof PwGroup) {
|
||||||
|
if(groupsBefore)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int nodeNameComp = object1.getDisplayTitle()
|
||||||
|
.compareToIgnoreCase(object2.getDisplayTitle());
|
||||||
|
// If same name, can be different
|
||||||
|
if (nodeNameComp == 0)
|
||||||
|
return object1.hashCode() - object2.hashCode();
|
||||||
|
return nodeNameComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparator of node by creation, Groups first, Entries second
|
||||||
|
*/
|
||||||
|
public static class NodeCreationComparator extends NodeComparator {
|
||||||
|
|
||||||
|
|
||||||
|
public NodeCreationComparator(boolean ascending, boolean groupsBefore) {
|
||||||
|
super(ascending, groupsBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(PwNode object1, PwNode object2) {
|
||||||
|
if (object1.equals(object2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (object1 instanceof PwGroup) {
|
||||||
|
if (object2 instanceof PwGroup) {
|
||||||
|
return new GroupCreationComparator(ascending)
|
||||||
|
.compare((PwGroup) object1, (PwGroup) object2);
|
||||||
|
} else if (object2 instanceof PwEntry) {
|
||||||
|
if(groupsBefore)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (object1 instanceof PwEntry) {
|
||||||
|
if(object2 instanceof PwEntry) {
|
||||||
|
return new EntryCreationComparator(ascending)
|
||||||
|
.compare((PwEntry) object1, (PwEntry) object2);
|
||||||
|
} else if (object2 instanceof PwGroup) {
|
||||||
|
if(groupsBefore)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int nodeCreationComp = object1.getCreationTime().getDate()
|
||||||
|
.compareTo(object2.getCreationTime().getDate());
|
||||||
|
// If same creation, can be different
|
||||||
|
if (nodeCreationComp == 0) {
|
||||||
|
return object1.hashCode() - object2.hashCode();
|
||||||
|
}
|
||||||
|
return nodeCreationComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group comparator by name
|
||||||
|
*/
|
||||||
|
public static class GroupNameComparator implements Comparator<PwGroup> {
|
||||||
|
|
||||||
|
private boolean ascending;
|
||||||
|
|
||||||
|
public GroupNameComparator(boolean ascending) {
|
||||||
|
this.ascending = ascending;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(PwGroup object1, PwGroup object2) {
|
||||||
|
if (object1.equals(object2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int groupNameComp = object1.getName().compareToIgnoreCase(object2.getName());
|
||||||
|
// If same name, can be different
|
||||||
|
if (groupNameComp == 0) {
|
||||||
|
return object1.hashCode() - object2.hashCode();
|
||||||
|
}
|
||||||
|
// If descending
|
||||||
|
if (!ascending)
|
||||||
|
groupNameComp = -groupNameComp;
|
||||||
|
|
||||||
|
return groupNameComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group comparator by name
|
||||||
|
*/
|
||||||
|
public static class GroupCreationComparator implements Comparator<PwGroup> {
|
||||||
|
|
||||||
|
private boolean ascending;
|
||||||
|
|
||||||
|
public GroupCreationComparator(boolean ascending) {
|
||||||
|
this.ascending = ascending;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(PwGroup object1, PwGroup object2) {
|
||||||
|
if (object1.equals(object2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int groupCreationComp = object1.getCreationTime().getDate()
|
||||||
|
.compareTo(object2.getCreationTime().getDate());
|
||||||
|
// If same creation, can be different
|
||||||
|
if (groupCreationComp == 0) {
|
||||||
|
return object1.hashCode() - object2.hashCode();
|
||||||
|
}
|
||||||
|
// If descending
|
||||||
|
if (!ascending)
|
||||||
|
groupCreationComp = -groupCreationComp;
|
||||||
|
|
||||||
|
return groupCreationComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparator of Entry by Name
|
||||||
|
*/
|
||||||
|
public static class EntryNameComparator implements Comparator<PwEntry> {
|
||||||
|
|
||||||
|
private boolean ascending;
|
||||||
|
|
||||||
|
public EntryNameComparator(boolean ascending) {
|
||||||
|
this.ascending = ascending;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(PwEntry object1, PwEntry object2) {
|
||||||
|
if (object1.equals(object2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int entryTitleComp = object1.getTitle().compareToIgnoreCase(object2.getTitle());
|
||||||
|
// If same title, can be different
|
||||||
|
if (entryTitleComp == 0) {
|
||||||
|
return object1.hashCode() - object2.hashCode();
|
||||||
|
}
|
||||||
|
// If descending
|
||||||
|
if (!ascending)
|
||||||
|
entryTitleComp = -entryTitleComp;
|
||||||
|
|
||||||
|
return entryTitleComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparator of Entry by Creation
|
||||||
|
*/
|
||||||
|
public static class EntryCreationComparator implements Comparator<PwEntry> {
|
||||||
|
|
||||||
|
private boolean ascending;
|
||||||
|
|
||||||
|
public EntryCreationComparator(boolean ascending) {
|
||||||
|
this.ascending = ascending;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(PwEntry object1, PwEntry object2) {
|
||||||
|
if (object1.equals(object2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int entryCreationComp = object1.getCreationTime().getDate()
|
||||||
|
.compareTo(object2.getCreationTime().getDate());
|
||||||
|
// If same creation, can be different
|
||||||
|
if (entryCreationComp == 0) {
|
||||||
|
return object1.hashCode() - object2.hashCode();
|
||||||
|
}
|
||||||
|
// If descending
|
||||||
|
if (!ascending)
|
||||||
|
entryCreationComp = -entryCreationComp;
|
||||||
|
|
||||||
|
return entryCreationComp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,21 +21,16 @@ package com.keepassdroid.database.edit;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.database.PwDatabase;
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.PwEntry;
|
import com.keepassdroid.database.PwEntry;
|
||||||
import com.keepassdroid.database.PwGroup;
|
|
||||||
|
|
||||||
public class AddEntry extends RunnableOnFinish {
|
public class AddEntry extends RunnableOnFinish {
|
||||||
protected Database mDb;
|
protected Database mDb;
|
||||||
private PwEntry mEntry;
|
private PwEntry mEntry;
|
||||||
private Context ctx;
|
private Context ctx;
|
||||||
|
|
||||||
public static AddEntry getInstance(Context ctx, Database db, PwEntry entry, OnFinish finish) {
|
public AddEntry(Context ctx, Database db, PwEntry entry, OnFinish finish) {
|
||||||
return new AddEntry(ctx, db, entry, finish);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AddEntry(Context ctx, Database db, PwEntry entry, OnFinish finish) {
|
|
||||||
super(finish);
|
super(finish);
|
||||||
|
|
||||||
mDb = db;
|
mDb = db;
|
||||||
@@ -56,27 +51,18 @@ public class AddEntry extends RunnableOnFinish {
|
|||||||
|
|
||||||
private class AfterAdd extends OnFinish {
|
private class AfterAdd extends OnFinish {
|
||||||
|
|
||||||
public AfterAdd(OnFinish finish) {
|
AfterAdd(OnFinish finish) {
|
||||||
super(finish);
|
super(finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
PwDatabase pm = mDb.pm;
|
PwDatabase pm = mDb.pm;
|
||||||
if ( mSuccess ) {
|
if ( !mSuccess ) {
|
||||||
|
|
||||||
PwGroup parent = mEntry.getParent();
|
|
||||||
|
|
||||||
// Mark parent tree dirty
|
|
||||||
mDb.dirty.add(parent);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
pm.removeEntryFrom(mEntry, mEntry.getParent());
|
pm.removeEntryFrom(mEntry, mEntry.getParent());
|
||||||
}
|
}
|
||||||
|
// TODO if add entry callback
|
||||||
super.run();
|
super.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package com.keepassdroid.database.edit;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.database.PwDatabase;
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.PwGroup;
|
import com.keepassdroid.database.PwGroup;
|
||||||
|
|
||||||
@@ -32,24 +32,20 @@ public class AddGroup extends RunnableOnFinish {
|
|||||||
private PwGroup mGroup;
|
private PwGroup mGroup;
|
||||||
private PwGroup mParent;
|
private PwGroup mParent;
|
||||||
private Context ctx;
|
private Context ctx;
|
||||||
protected boolean mDontSave;
|
private boolean mDontSave;
|
||||||
|
|
||||||
|
public AddGroup(Context ctx, Database db, String name, int iconid,
|
||||||
public static AddGroup getInstance(Context ctx, Database db, String name, int iconid, PwGroup parent, OnFinish finish, boolean dontSave) {
|
PwGroup parent, AfterAddNodeOnFinish afterAddNode,
|
||||||
return new AddGroup(ctx, db, name, iconid, parent, finish, dontSave);
|
boolean dontSave) {
|
||||||
}
|
super(afterAddNode);
|
||||||
|
|
||||||
|
|
||||||
private AddGroup(Context ctx, Database db, String name, int iconid, PwGroup parent, OnFinish finish, boolean dontSave) {
|
|
||||||
super(finish);
|
|
||||||
|
|
||||||
mDb = db;
|
mDb = db;
|
||||||
mName = name;
|
mName = name;
|
||||||
mIconID = iconid;
|
mIconID = iconid;
|
||||||
mParent = parent;
|
mParent = parent;
|
||||||
mDontSave = dontSave;
|
mDontSave = dontSave;
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
|
|
||||||
mFinish = new AfterAdd(mFinish);
|
mFinish = new AfterAdd(mFinish);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,11 +56,9 @@ public class AddGroup extends RunnableOnFinish {
|
|||||||
// Generate new group
|
// Generate new group
|
||||||
mGroup = pm.createGroup();
|
mGroup = pm.createGroup();
|
||||||
mGroup.initNewGroup(mName, pm.newGroupId());
|
mGroup.initNewGroup(mName, pm.newGroupId());
|
||||||
mGroup.icon = mDb.pm.iconFactory.getIcon(mIconID);
|
mGroup.setIcon(mDb.pm.iconFactory.getIcon(mIconID));
|
||||||
pm.addGroupTo(mGroup, mParent);
|
pm.addGroupTo(mGroup, mParent);
|
||||||
|
|
||||||
//mParent.sortGroupsByName();
|
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDB save = new SaveDB(ctx, mDb, mFinish, mDontSave);
|
SaveDB save = new SaveDB(ctx, mDb, mFinish, mDontSave);
|
||||||
save.run();
|
save.run();
|
||||||
@@ -72,24 +66,23 @@ public class AddGroup extends RunnableOnFinish {
|
|||||||
|
|
||||||
private class AfterAdd extends OnFinish {
|
private class AfterAdd extends OnFinish {
|
||||||
|
|
||||||
public AfterAdd(OnFinish finish) {
|
AfterAdd(OnFinish finish) {
|
||||||
super(finish);
|
super(finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
PwDatabase pm = mDb.pm;
|
PwDatabase pm = mDb.pm;
|
||||||
if ( mSuccess ) {
|
if ( !mSuccess ) {
|
||||||
// Mark parent group dirty
|
pm.removeGroupFrom(mGroup, mParent);
|
||||||
mDb.dirty.add(mParent);
|
|
||||||
} else {
|
|
||||||
pm.removeGroupFrom(mGroup, mParent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.run();
|
// TODO Better callback
|
||||||
|
AfterAddNodeOnFinish afterAddNode =
|
||||||
|
(AfterAddNodeOnFinish) super.mOnFinish;
|
||||||
|
afterAddNode.mSuccess = mSuccess;
|
||||||
|
afterAddNode.mMessage = mMessage;
|
||||||
|
afterAddNode.run(mGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.keepassdroid.database.edit;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
import com.keepassdroid.database.PwNode;
|
||||||
|
|
||||||
|
public abstract class AfterAddNodeOnFinish extends OnFinish {
|
||||||
|
public AfterAddNodeOnFinish(Handler handler) {
|
||||||
|
super(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void run(PwNode pwNode);
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ package com.keepassdroid.database.edit;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.app.App;
|
import com.keepassdroid.app.App;
|
||||||
import com.keepassdroid.database.PwDatabase;
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.utils.UriUtil;
|
import com.keepassdroid.utils.UriUtil;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package com.keepassdroid.database.edit;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.database.PwDatabase;
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.PwEntry;
|
import com.keepassdroid.database.PwEntry;
|
||||||
import com.keepassdroid.database.PwGroup;
|
import com.keepassdroid.database.PwGroup;
|
||||||
@@ -71,8 +71,6 @@ public class DeleteEntry extends RunnableOnFinish {
|
|||||||
// Commit database
|
// Commit database
|
||||||
SaveDB save = new SaveDB(ctx, mDb, mFinish, mDontSave);
|
SaveDB save = new SaveDB(ctx, mDb, mFinish, mDontSave);
|
||||||
save.run();
|
save.run();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterDelete extends OnFinish {
|
private class AfterDelete extends OnFinish {
|
||||||
@@ -81,7 +79,7 @@ public class DeleteEntry extends RunnableOnFinish {
|
|||||||
private PwEntry mEntry;
|
private PwEntry mEntry;
|
||||||
private boolean recycled;
|
private boolean recycled;
|
||||||
|
|
||||||
public AfterDelete(OnFinish finish, PwGroup parent, PwEntry entry, boolean r) {
|
AfterDelete(OnFinish finish, PwGroup parent, PwEntry entry, boolean r) {
|
||||||
super(finish);
|
super(finish);
|
||||||
|
|
||||||
mParent = parent;
|
mParent = parent;
|
||||||
@@ -92,18 +90,7 @@ public class DeleteEntry extends RunnableOnFinish {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
PwDatabase pm = mDb.pm;
|
PwDatabase pm = mDb.pm;
|
||||||
if ( mSuccess ) {
|
if ( !mSuccess ) {
|
||||||
// Mark parent dirty
|
|
||||||
if ( mParent != null ) {
|
|
||||||
mDb.dirty.add(mParent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recycled) {
|
|
||||||
PwGroup recycleBin = pm.getRecycleBin();
|
|
||||||
mDb.dirty.add(recycleBin);
|
|
||||||
mDb.dirty.add(mDb.pm.rootGroup);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (recycled) {
|
if (recycled) {
|
||||||
pm.undoRecycle(mEntry, mParent);
|
pm.undoRecycle(mEntry, mParent);
|
||||||
}
|
}
|
||||||
@@ -111,11 +98,9 @@ public class DeleteEntry extends RunnableOnFinish {
|
|||||||
pm.undoDeleteEntry(mEntry, mParent);
|
pm.undoDeleteEntry(mEntry, mParent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO Callback after delete entry
|
||||||
|
|
||||||
super.run();
|
super.run();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,109 +19,115 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database.edit;
|
package com.keepassdroid.database.edit;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import android.content.Context;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
|
||||||
import com.keepassdroid.GroupBaseActivity;
|
|
||||||
import com.keepassdroid.app.App;
|
import com.keepassdroid.app.App;
|
||||||
|
import com.keepassdroid.database.Database;
|
||||||
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.PwEntry;
|
import com.keepassdroid.database.PwEntry;
|
||||||
import com.keepassdroid.database.PwGroup;
|
import com.keepassdroid.database.PwGroup;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class DeleteGroup extends RunnableOnFinish {
|
public class DeleteGroup extends RunnableOnFinish {
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
private Database mDb;
|
private Database mDb;
|
||||||
private PwGroup mGroup;
|
private PwGroup mGroup;
|
||||||
private GroupBaseActivity mAct;
|
|
||||||
private boolean mDontSave;
|
private boolean mDontSave;
|
||||||
|
|
||||||
public DeleteGroup(Database db, PwGroup group, GroupBaseActivity act, OnFinish finish) {
|
public DeleteGroup(Context ctx, Database db, PwGroup group, OnFinish finish) {
|
||||||
super(finish);
|
super(finish);
|
||||||
setMembers(db, group, act, false);
|
setMembers(ctx, db, group, false);
|
||||||
}
|
|
||||||
|
|
||||||
public DeleteGroup(Database db, PwGroup group, GroupBaseActivity act, OnFinish finish, boolean dontSave) {
|
|
||||||
super(finish);
|
|
||||||
setMembers(db, group, act, dontSave);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DeleteGroup(Context ctx, Database db, PwGroup group, OnFinish finish, boolean dontSave) {
|
||||||
|
super(finish);
|
||||||
|
setMembers(ctx, db, group, dontSave);
|
||||||
|
}
|
||||||
|
|
||||||
public DeleteGroup(Database db, PwGroup group, OnFinish finish, boolean dontSave) {
|
public DeleteGroup(Database db, PwGroup group, OnFinish finish, boolean dontSave) {
|
||||||
super(finish);
|
super(finish);
|
||||||
setMembers(db, group, null, dontSave);
|
setMembers(null, db, group, dontSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setMembers(Database db, PwGroup group, GroupBaseActivity act, boolean dontSave) {
|
private void setMembers(Context ctx, Database db, PwGroup group, boolean dontSave) {
|
||||||
mDb = db;
|
mDb = db;
|
||||||
mGroup = group;
|
mGroup = group;
|
||||||
mAct = act;
|
mContext = ctx;
|
||||||
mDontSave = dontSave;
|
mDontSave = dontSave;
|
||||||
|
|
||||||
mFinish = new AfterDelete(mFinish);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
PwDatabase pm = mDb.pm;
|
||||||
// Remove child entries
|
PwGroup parent = mGroup.getParent();
|
||||||
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();
|
|
||||||
|
|
||||||
|
// Remove Group from parent
|
||||||
|
boolean recycle = pm.canRecycle(mGroup);
|
||||||
|
if (recycle) {
|
||||||
|
pm.recycle(mGroup);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO tests
|
||||||
|
// Remove child entries
|
||||||
|
List<PwEntry> childEnt = new ArrayList<>(mGroup.getChildEntries());
|
||||||
|
for ( int i = 0; i < childEnt.size(); i++ ) {
|
||||||
|
DeleteEntry task = new DeleteEntry(mContext, mDb, childEnt.get(i), null, true);
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove child groups
|
||||||
|
List<PwGroup> childGrp = new ArrayList<>(mGroup.getChildGroups());
|
||||||
|
for ( int i = 0; i < childGrp.size(); i++ ) {
|
||||||
|
DeleteGroup task = new DeleteGroup(mContext, mDb, childGrp.get(i), null, true);
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
pm.deleteGroup(mGroup);
|
||||||
|
|
||||||
|
// Remove from PwDatabaseV3
|
||||||
|
// TODO ENcapsulate
|
||||||
|
mDb.pm.getGroups().remove(mGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save
|
||||||
|
mFinish = new AfterDelete(mFinish, parent, mGroup, recycle);
|
||||||
|
|
||||||
|
// Commit Database
|
||||||
|
SaveDB save = new SaveDB(mContext, mDb, mFinish, mDontSave);
|
||||||
|
save.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterDelete extends OnFinish {
|
private class AfterDelete extends OnFinish {
|
||||||
public AfterDelete(OnFinish finish) {
|
|
||||||
|
private PwGroup mParent;
|
||||||
|
private PwGroup mGroup;
|
||||||
|
private boolean recycled;
|
||||||
|
|
||||||
|
AfterDelete(OnFinish finish, PwGroup parent, PwGroup mGroup, boolean recycle) {
|
||||||
super(finish);
|
super(finish);
|
||||||
|
|
||||||
|
this.mParent = parent;
|
||||||
|
this.mGroup = mGroup;
|
||||||
|
this.recycled = recycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
if ( mSuccess ) {
|
PwDatabase pm = mDb.pm;
|
||||||
// Remove from tree global
|
if ( !mSuccess ) {
|
||||||
mDb.pm.groups.remove(mGroup.getId());
|
if (recycled) {
|
||||||
|
pm.undoRecycle(mGroup, mParent);
|
||||||
// Remove tree from the dirty global (if it is present), not a big deal if this fails
|
}
|
||||||
mDb.dirty.remove(mGroup);
|
else {
|
||||||
|
// Let's not bother recovering from a failure to save a deleted tree. It is too much work.
|
||||||
// Mark parent dirty
|
App.setShutdown();
|
||||||
PwGroup parent = mGroup.getParent();
|
// TODO TEST pm.undoDeleteGroup(mGroup, mParent);
|
||||||
if ( parent != null ) {
|
}
|
||||||
mDb.dirty.add(parent);
|
}
|
||||||
}
|
// TODO Callback after delete group
|
||||||
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();
|
super.run();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import android.net.Uri;
|
|||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R;
|
import com.kunzisoft.keepass.R;
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.app.App;
|
import com.keepassdroid.app.App;
|
||||||
import com.keepassdroid.database.exception.ArcFourException;
|
import com.keepassdroid.database.exception.ArcFourException;
|
||||||
import com.keepassdroid.database.exception.ContentFileNotFoundException;
|
import com.keepassdroid.database.exception.ContentFileNotFoundException;
|
||||||
@@ -78,7 +78,7 @@ public class LoadDB extends RunnableOnFinish {
|
|||||||
finish(false, mCtx.getString(R.string.file_not_found_content));
|
finish(false, mCtx.getString(R.string.file_not_found_content));
|
||||||
return;
|
return;
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
finish(false, mCtx.getString(R.string.FileNotFound));
|
finish(false, mCtx.getString(R.string.file_not_found));
|
||||||
return;
|
return;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
finish(false, e.getMessage());
|
finish(false, e.getMessage());
|
||||||
@@ -104,6 +104,9 @@ public class LoadDB extends RunnableOnFinish {
|
|||||||
} catch (OutOfMemoryError e) {
|
} catch (OutOfMemoryError e) {
|
||||||
finish(false, mCtx.getString(R.string.error_out_of_memory));
|
finish(false, mCtx.getString(R.string.error_out_of_memory));
|
||||||
return;
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
finish(false, e.getMessage());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
finish(true);
|
finish(true);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database.edit;
|
package com.keepassdroid.database.edit;
|
||||||
|
|
||||||
import com.keepassdroid.UpdateStatus;
|
import com.keepassdroid.tasks.UpdateStatus;
|
||||||
|
|
||||||
|
|
||||||
public abstract class RunnableOnFinish implements Runnable {
|
public abstract class RunnableOnFinish implements Runnable {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import android.content.Context;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.database.exception.PwDbOutputException;
|
import com.keepassdroid.database.exception.PwDbOutputException;
|
||||||
|
|
||||||
public class SaveDB extends RunnableOnFinish {
|
public class SaveDB extends RunnableOnFinish {
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ import android.content.Context;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.database.PwDatabase;
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
import com.keepassdroid.database.exception.InvalidKeyFileException;
|
||||||
import com.keepassdroid.dialog.PasswordEncodingDialogHelper;
|
import com.keepassdroid.dialogs.PasswordEncodingDialogHelper;
|
||||||
import com.keepassdroid.utils.UriUtil;
|
import com.keepassdroid.utils.UriUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -21,9 +21,8 @@ package com.keepassdroid.database.edit;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.keepassdroid.Database;
|
import com.keepassdroid.database.Database;
|
||||||
import com.keepassdroid.database.PwEntry;
|
import com.keepassdroid.database.PwEntry;
|
||||||
import com.keepassdroid.database.PwGroup;
|
|
||||||
|
|
||||||
public class UpdateEntry extends RunnableOnFinish {
|
public class UpdateEntry extends RunnableOnFinish {
|
||||||
private Database mDb;
|
private Database mDb;
|
||||||
@@ -41,7 +40,7 @@ public class UpdateEntry extends RunnableOnFinish {
|
|||||||
|
|
||||||
// Keep backup of original values in case save fails
|
// Keep backup of original values in case save fails
|
||||||
PwEntry backup;
|
PwEntry backup;
|
||||||
backup = (PwEntry) mOldE.clone();
|
backup = mOldE.clone();
|
||||||
|
|
||||||
mFinish = new AfterUpdate(backup, finish);
|
mFinish = new AfterUpdate(backup, finish);
|
||||||
}
|
}
|
||||||
@@ -61,36 +60,19 @@ public class UpdateEntry extends RunnableOnFinish {
|
|||||||
private class AfterUpdate extends OnFinish {
|
private class AfterUpdate extends OnFinish {
|
||||||
private PwEntry mBackup;
|
private PwEntry mBackup;
|
||||||
|
|
||||||
public AfterUpdate(PwEntry backup, OnFinish finish) {
|
AfterUpdate(PwEntry backup, OnFinish finish) {
|
||||||
super(finish);
|
super(finish);
|
||||||
|
|
||||||
mBackup = backup;
|
mBackup = backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if ( mSuccess ) {
|
if ( !mSuccess ) {
|
||||||
// Mark group dirty if title or icon changes
|
|
||||||
if ( ! mBackup.getTitle().equals(mNewE.getTitle()) || ! mBackup.getIcon().equals(mNewE.getIcon()) ) {
|
|
||||||
PwGroup parent = mBackup.getParent();
|
|
||||||
if ( parent != null ) {
|
|
||||||
// Resort entries
|
|
||||||
parent.sortEntriesByName();
|
|
||||||
|
|
||||||
// Mark parent group dirty
|
|
||||||
mDb.dirty.add(parent);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If we fail to save, back out changes to global structure
|
// If we fail to save, back out changes to global structure
|
||||||
mOldE.assign(mBackup);
|
mOldE.assign(mBackup);
|
||||||
}
|
}
|
||||||
|
// TODO Callback for update entry
|
||||||
super.run();
|
super.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,14 +35,14 @@ public class EntrySearchStringIteratorV4 extends EntrySearchStringIterator {
|
|||||||
|
|
||||||
public EntrySearchStringIteratorV4(PwEntryV4 entry) {
|
public EntrySearchStringIteratorV4(PwEntryV4 entry) {
|
||||||
this.sp = SearchParametersV4.DEFAULT;
|
this.sp = SearchParametersV4.DEFAULT;
|
||||||
setIterator = entry.strings.entrySet().iterator();
|
setIterator = entry.getFields().entrySet().iterator();
|
||||||
advance();
|
advance();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntrySearchStringIteratorV4(PwEntryV4 entry, SearchParametersV4 sp) {
|
public EntrySearchStringIteratorV4(PwEntryV4 entry, SearchParametersV4 sp) {
|
||||||
this.sp = sp;
|
this.sp = sp;
|
||||||
setIterator = entry.strings.entrySet().iterator();
|
setIterator = entry.getFields().entrySet().iterator();
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ package com.keepassdroid.database.load;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import com.keepassdroid.UpdateStatus;
|
import com.keepassdroid.tasks.UpdateStatus;
|
||||||
import com.keepassdroid.database.PwDatabase;
|
import com.keepassdroid.database.PwDatabase;
|
||||||
import com.keepassdroid.database.exception.InvalidDBException;
|
import com.keepassdroid.database.exception.InvalidDBException;
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R;
|
import com.kunzisoft.keepass.R;
|
||||||
import com.keepassdroid.UpdateStatus;
|
import com.keepassdroid.tasks.UpdateStatus;
|
||||||
import com.keepassdroid.crypto.CipherFactory;
|
import com.keepassdroid.crypto.CipherFactory;
|
||||||
import com.keepassdroid.database.PwDatabaseV3;
|
import com.keepassdroid.database.PwDatabaseV3;
|
||||||
import com.keepassdroid.database.PwDate;
|
import com.keepassdroid.database.PwDate;
|
||||||
@@ -340,31 +340,31 @@ public class ImporterV3 extends Importer {
|
|||||||
// Ignore field
|
// Ignore field
|
||||||
break;
|
break;
|
||||||
case 0x0001 :
|
case 0x0001 :
|
||||||
grp.groupId = LEDataInputStream.readInt(buf, offset);
|
grp.setGroupId(LEDataInputStream.readInt(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0002 :
|
case 0x0002 :
|
||||||
grp.name = Types.readCString(buf, offset);
|
grp.setName(Types.readCString(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0003 :
|
case 0x0003 :
|
||||||
grp.tCreation = new PwDate(buf, offset);
|
grp.setCreationTime(new PwDate(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0004 :
|
case 0x0004 :
|
||||||
grp.tLastMod = new PwDate(buf, offset);
|
grp.setLastModificationTime(new PwDate(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0005 :
|
case 0x0005 :
|
||||||
grp.tLastAccess = new PwDate(buf, offset);
|
grp.setLastAccessTime(new PwDate(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0006 :
|
case 0x0006 :
|
||||||
grp.tExpire = new PwDate(buf, offset);
|
grp.setExpiryTime(new PwDate(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0007 :
|
case 0x0007 :
|
||||||
grp.icon = db.iconFactory.getIcon(LEDataInputStream.readInt(buf, offset));
|
grp.setIcon(db.iconFactory.getIcon(LEDataInputStream.readInt(buf, offset)));
|
||||||
break;
|
break;
|
||||||
case 0x0008 :
|
case 0x0008 :
|
||||||
grp.level = LEDataInputStream.readUShort(buf, offset);
|
grp.setLevel(LEDataInputStream.readUShort(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0009 :
|
case 0x0009 :
|
||||||
grp.flags = LEDataInputStream.readInt(buf, offset);
|
grp.setFlags(LEDataInputStream.readInt(buf, offset));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,7 +387,7 @@ public class ImporterV3 extends Importer {
|
|||||||
ent.setUUID(Types.bytestoUUID(buf, offset));
|
ent.setUUID(Types.bytestoUUID(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0002 :
|
case 0x0002 :
|
||||||
ent.groupId = LEDataInputStream.readInt(buf, offset);
|
ent.setGroupId(LEDataInputStream.readInt(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0003 :
|
case 0x0003 :
|
||||||
int iconId = LEDataInputStream.readInt(buf, offset);
|
int iconId = LEDataInputStream.readInt(buf, offset);
|
||||||
@@ -397,37 +397,37 @@ public class ImporterV3 extends Importer {
|
|||||||
iconId = 0;
|
iconId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ent.icon = db.iconFactory.getIcon(iconId);
|
ent.setIcon(db.iconFactory.getIcon(iconId));
|
||||||
break;
|
break;
|
||||||
case 0x0004 :
|
case 0x0004 :
|
||||||
ent.title = Types.readCString(buf, offset);
|
ent.setTitle(Types.readCString(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0005 :
|
case 0x0005 :
|
||||||
ent.url = Types.readCString(buf, offset);
|
ent.setUrl(Types.readCString(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0006 :
|
case 0x0006 :
|
||||||
ent.username = Types.readCString(buf, offset);
|
ent.setUsername(Types.readCString(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0007 :
|
case 0x0007 :
|
||||||
ent.setPassword(buf, offset, Types.strlen(buf, offset));
|
ent.setPassword(buf, offset, Types.strlen(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0008 :
|
case 0x0008 :
|
||||||
ent.additional = Types.readCString(buf, offset);
|
ent.setNotes(Types.readCString(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x0009 :
|
case 0x0009 :
|
||||||
ent.tCreation = new PwDate(buf, offset);
|
ent.setCreationTime(new PwDate(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x000A :
|
case 0x000A :
|
||||||
ent.tLastMod = new PwDate(buf, offset);
|
ent.setLastModificationTime(new PwDate(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x000B :
|
case 0x000B :
|
||||||
ent.tLastAccess = new PwDate(buf, offset);
|
ent.setLastAccessTime(new PwDate(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x000C :
|
case 0x000C :
|
||||||
ent.tExpire = new PwDate(buf, offset);
|
ent.setExpiryTime(new PwDate(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x000D :
|
case 0x000D :
|
||||||
ent.binaryDesc = Types.readCString(buf, offset);
|
ent.setBinaryDesc(Types.readCString(buf, offset));
|
||||||
break;
|
break;
|
||||||
case 0x000E :
|
case 0x000E :
|
||||||
ent.setBinaryData(buf, offset, fieldSize);
|
ent.setBinaryData(buf, offset, fieldSize);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ package com.keepassdroid.database.load;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import com.keepassdroid.UpdateStatus;
|
import com.keepassdroid.tasks.UpdateStatus;
|
||||||
import com.keepassdroid.database.PwDatabaseV3Debug;
|
import com.keepassdroid.database.PwDatabaseV3Debug;
|
||||||
import com.keepassdroid.database.exception.InvalidDBException;
|
import com.keepassdroid.database.exception.InvalidDBException;
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,12 @@ import org.xmlpull.v1.XmlPullParserFactory;
|
|||||||
|
|
||||||
import biz.source_code.base64Coder.Base64Coder;
|
import biz.source_code.base64Coder.Base64Coder;
|
||||||
|
|
||||||
import com.keepassdroid.UpdateStatus;
|
import com.keepassdroid.database.PwDatabase;
|
||||||
|
import com.keepassdroid.database.PwDate;
|
||||||
|
import com.keepassdroid.tasks.UpdateStatus;
|
||||||
import com.keepassdroid.crypto.CipherFactory;
|
import com.keepassdroid.crypto.CipherFactory;
|
||||||
import com.keepassdroid.crypto.PwStreamCipherFactory;
|
import com.keepassdroid.crypto.PwStreamCipherFactory;
|
||||||
import com.keepassdroid.crypto.engine.CipherEngine;
|
import com.keepassdroid.crypto.engine.CipherEngine;
|
||||||
import com.keepassdroid.database.BinaryPool;
|
|
||||||
import com.keepassdroid.database.ITimeLogger;
|
import com.keepassdroid.database.ITimeLogger;
|
||||||
import com.keepassdroid.database.PwCompressionAlgorithm;
|
import com.keepassdroid.database.PwCompressionAlgorithm;
|
||||||
import com.keepassdroid.database.PwDatabaseV4;
|
import com.keepassdroid.database.PwDatabaseV4;
|
||||||
@@ -309,7 +310,7 @@ public class ImporterV4 extends Importer {
|
|||||||
private boolean entryInHistory = false;
|
private boolean entryInHistory = false;
|
||||||
private PwEntryV4 ctxHistoryBase = null;
|
private PwEntryV4 ctxHistoryBase = null;
|
||||||
private PwDeletedObject ctxDeletedObject = null;
|
private PwDeletedObject ctxDeletedObject = null;
|
||||||
private UUID customIconID = PwDatabaseV4.UUID_ZERO;
|
private UUID customIconID = PwDatabase.UUID_ZERO;
|
||||||
private byte[] customIconData;
|
private byte[] customIconData;
|
||||||
private String customDataKey = null;
|
private String customDataKey = null;
|
||||||
private String customDataValue = null;
|
private String customDataValue = null;
|
||||||
@@ -558,38 +559,38 @@ public class ImporterV4 extends Importer {
|
|||||||
|
|
||||||
case Group:
|
case Group:
|
||||||
if ( name.equalsIgnoreCase(ElemUuid) ) {
|
if ( name.equalsIgnoreCase(ElemUuid) ) {
|
||||||
ctxGroup.uuid = ReadUuid(xpp);
|
ctxGroup.setUUID(ReadUuid(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemName) ) {
|
} else if ( name.equalsIgnoreCase(ElemName) ) {
|
||||||
ctxGroup.name = ReadString(xpp);
|
ctxGroup.setName(ReadString(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemNotes) ) {
|
} else if ( name.equalsIgnoreCase(ElemNotes) ) {
|
||||||
ctxGroup.notes = ReadString(xpp);
|
ctxGroup.setNotes(ReadString(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemIcon) ) {
|
} else if ( name.equalsIgnoreCase(ElemIcon) ) {
|
||||||
ctxGroup.icon = db.iconFactory.getIcon((int)ReadUInt(xpp, 0));
|
ctxGroup.setIcon(db.iconFactory.getIcon((int)ReadUInt(xpp, 0)));
|
||||||
} else if ( name.equalsIgnoreCase(ElemCustomIconID) ) {
|
} else if ( name.equalsIgnoreCase(ElemCustomIconID) ) {
|
||||||
ctxGroup.customIcon = db.iconFactory.getIcon(ReadUuid(xpp));
|
ctxGroup.setCustomIcon(db.iconFactory.getIcon(ReadUuid(xpp)));
|
||||||
} else if ( name.equalsIgnoreCase(ElemTimes) ) {
|
} else if ( name.equalsIgnoreCase(ElemTimes) ) {
|
||||||
return SwitchContext(ctx, KdbContext.GroupTimes, xpp);
|
return SwitchContext(ctx, KdbContext.GroupTimes, xpp);
|
||||||
} else if ( name.equalsIgnoreCase(ElemIsExpanded) ) {
|
} else if ( name.equalsIgnoreCase(ElemIsExpanded) ) {
|
||||||
ctxGroup.isExpanded = ReadBool(xpp, true);
|
ctxGroup.setExpanded(ReadBool(xpp, true));
|
||||||
} else if ( name.equalsIgnoreCase(ElemGroupDefaultAutoTypeSeq) ) {
|
} else if ( name.equalsIgnoreCase(ElemGroupDefaultAutoTypeSeq) ) {
|
||||||
ctxGroup.defaultAutoTypeSequence = ReadString(xpp);
|
ctxGroup.setDefaultAutoTypeSequence(ReadString(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemEnableAutoType) ) {
|
} else if ( name.equalsIgnoreCase(ElemEnableAutoType) ) {
|
||||||
ctxGroup.enableAutoType = StringToBoolean(ReadString(xpp));
|
ctxGroup.setEnableAutoType(StringToBoolean(ReadString(xpp)));
|
||||||
} else if ( name.equalsIgnoreCase(ElemEnableSearching) ) {
|
} else if ( name.equalsIgnoreCase(ElemEnableSearching) ) {
|
||||||
ctxGroup.enableSearching = StringToBoolean(ReadString(xpp));
|
ctxGroup.setEnableSearching(StringToBoolean(ReadString(xpp)));
|
||||||
} else if ( name.equalsIgnoreCase(ElemLastTopVisibleEntry) ) {
|
} else if ( name.equalsIgnoreCase(ElemLastTopVisibleEntry) ) {
|
||||||
ctxGroup.lastTopVisibleEntry = ReadUuid(xpp);
|
ctxGroup.setLastTopVisibleEntry(ReadUuid(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemCustomData) ) {
|
} else if ( name.equalsIgnoreCase(ElemCustomData) ) {
|
||||||
return SwitchContext(ctx, KdbContext.GroupCustomData, xpp);
|
return SwitchContext(ctx, KdbContext.GroupCustomData, xpp);
|
||||||
} else if ( name.equalsIgnoreCase(ElemGroup) ) {
|
} else if ( name.equalsIgnoreCase(ElemGroup) ) {
|
||||||
ctxGroup = new PwGroupV4();
|
ctxGroup = new PwGroupV4();
|
||||||
ctxGroups.peek().AddGroup(ctxGroup, true);
|
ctxGroups.peek().AddGroup(ctxGroup);
|
||||||
ctxGroups.push(ctxGroup);
|
ctxGroups.push(ctxGroup);
|
||||||
|
|
||||||
return SwitchContext(ctx, KdbContext.Group, xpp);
|
return SwitchContext(ctx, KdbContext.Group, xpp);
|
||||||
} else if ( name.equalsIgnoreCase(ElemEntry) ) {
|
} else if ( name.equalsIgnoreCase(ElemEntry) ) {
|
||||||
ctxEntry = new PwEntryV4();
|
ctxEntry = new PwEntryV4();
|
||||||
ctxGroup.AddEntry(ctxEntry, true);
|
ctxGroup.AddEntry(ctxEntry);
|
||||||
|
|
||||||
entryInHistory = false;
|
entryInHistory = false;
|
||||||
return SwitchContext(ctx, KdbContext.Entry, xpp);
|
return SwitchContext(ctx, KdbContext.Entry, xpp);
|
||||||
@@ -619,17 +620,17 @@ public class ImporterV4 extends Importer {
|
|||||||
if ( name.equalsIgnoreCase(ElemUuid) ) {
|
if ( name.equalsIgnoreCase(ElemUuid) ) {
|
||||||
ctxEntry.setUUID(ReadUuid(xpp));
|
ctxEntry.setUUID(ReadUuid(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemIcon) ) {
|
} else if ( name.equalsIgnoreCase(ElemIcon) ) {
|
||||||
ctxEntry.icon = db.iconFactory.getIcon((int)ReadUInt(xpp, 0));
|
ctxEntry.setIcon(db.iconFactory.getIcon((int)ReadUInt(xpp, 0)));
|
||||||
} else if ( name.equalsIgnoreCase(ElemCustomIconID) ) {
|
} else if ( name.equalsIgnoreCase(ElemCustomIconID) ) {
|
||||||
ctxEntry.customIcon = db.iconFactory.getIcon(ReadUuid(xpp));
|
ctxEntry.setCustomIcon(db.iconFactory.getIcon(ReadUuid(xpp)));
|
||||||
} else if ( name.equalsIgnoreCase(ElemFgColor) ) {
|
} else if ( name.equalsIgnoreCase(ElemFgColor) ) {
|
||||||
ctxEntry.foregroundColor = ReadString(xpp);
|
ctxEntry.setForegroundColor(ReadString(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemBgColor) ) {
|
} else if ( name.equalsIgnoreCase(ElemBgColor) ) {
|
||||||
ctxEntry.backgroupColor = ReadString(xpp);
|
ctxEntry.setBackgroupColor(ReadString(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemOverrideUrl) ) {
|
} else if ( name.equalsIgnoreCase(ElemOverrideUrl) ) {
|
||||||
ctxEntry.overrideURL = ReadString(xpp);
|
ctxEntry.setOverrideURL(ReadString(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemTags) ) {
|
} else if ( name.equalsIgnoreCase(ElemTags) ) {
|
||||||
ctxEntry.tags = ReadString(xpp);
|
ctxEntry.setTags(ReadString(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemTimes) ) {
|
} else if ( name.equalsIgnoreCase(ElemTimes) ) {
|
||||||
return SwitchContext(ctx, KdbContext.EntryTimes, xpp);
|
return SwitchContext(ctx, KdbContext.EntryTimes, xpp);
|
||||||
} else if ( name.equalsIgnoreCase(ElemString) ) {
|
} else if ( name.equalsIgnoreCase(ElemString) ) {
|
||||||
@@ -680,19 +681,19 @@ public class ImporterV4 extends Importer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( name.equalsIgnoreCase(ElemLastModTime) ) {
|
if ( name.equalsIgnoreCase(ElemLastModTime) ) {
|
||||||
tl.setLastModificationTime(ReadTime(xpp));
|
tl.setLastModificationTime(ReadPwTime(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemCreationTime) ) {
|
} else if ( name.equalsIgnoreCase(ElemCreationTime) ) {
|
||||||
tl.setCreationTime(ReadTime(xpp));
|
tl.setCreationTime(ReadPwTime(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemLastAccessTime) ) {
|
} else if ( name.equalsIgnoreCase(ElemLastAccessTime) ) {
|
||||||
tl.setLastAccessTime(ReadTime(xpp));
|
tl.setLastAccessTime(ReadPwTime(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemExpiryTime) ) {
|
} else if ( name.equalsIgnoreCase(ElemExpiryTime) ) {
|
||||||
tl.setExpiryTime(ReadTime(xpp));
|
tl.setExpiryTime(ReadPwTime(xpp));
|
||||||
} else if ( name.equalsIgnoreCase(ElemExpires) ) {
|
} else if ( name.equalsIgnoreCase(ElemExpires) ) {
|
||||||
tl.setExpires(ReadBool(xpp, false));
|
tl.setExpires(ReadBool(xpp, false));
|
||||||
} else if ( name.equalsIgnoreCase(ElemUsageCount) ) {
|
} else if ( name.equalsIgnoreCase(ElemUsageCount) ) {
|
||||||
tl.setUsageCount(ReadULong(xpp, 0));
|
tl.setUsageCount(ReadULong(xpp, 0));
|
||||||
} else if ( name.equalsIgnoreCase(ElemLocationChanged) ) {
|
} else if ( name.equalsIgnoreCase(ElemLocationChanged) ) {
|
||||||
tl.setLocationChanged(ReadTime(xpp));
|
tl.setLocationChanged(ReadPwTime(xpp));
|
||||||
} else {
|
} else {
|
||||||
ReadUnknown(xpp);
|
ReadUnknown(xpp);
|
||||||
}
|
}
|
||||||
@@ -718,11 +719,11 @@ public class ImporterV4 extends Importer {
|
|||||||
|
|
||||||
case EntryAutoType:
|
case EntryAutoType:
|
||||||
if ( name.equalsIgnoreCase(ElemAutoTypeEnabled) ) {
|
if ( name.equalsIgnoreCase(ElemAutoTypeEnabled) ) {
|
||||||
ctxEntry.autoType.enabled = ReadBool(xpp, true);
|
ctxEntry.getAutoType().enabled = ReadBool(xpp, true);
|
||||||
} else if ( name.equalsIgnoreCase(ElemAutoTypeObfuscation) ) {
|
} else if ( name.equalsIgnoreCase(ElemAutoTypeObfuscation) ) {
|
||||||
ctxEntry.autoType.obfuscationOptions = ReadUInt(xpp, 0);
|
ctxEntry.getAutoType().obfuscationOptions = ReadUInt(xpp, 0);
|
||||||
} else if ( name.equalsIgnoreCase(ElemAutoTypeDefaultSeq) ) {
|
} else if ( name.equalsIgnoreCase(ElemAutoTypeDefaultSeq) ) {
|
||||||
ctxEntry.autoType.defaultSequence = ReadString(xpp);
|
ctxEntry.getAutoType().defaultSequence = ReadString(xpp);
|
||||||
} else if ( name.equalsIgnoreCase(ElemAutoTypeItem) ) {
|
} else if ( name.equalsIgnoreCase(ElemAutoTypeItem) ) {
|
||||||
return SwitchContext(ctx, KdbContext.EntryAutoTypeItem, xpp);
|
return SwitchContext(ctx, KdbContext.EntryAutoTypeItem, xpp);
|
||||||
} else {
|
} else {
|
||||||
@@ -743,7 +744,7 @@ public class ImporterV4 extends Importer {
|
|||||||
case EntryHistory:
|
case EntryHistory:
|
||||||
if ( name.equalsIgnoreCase(ElemEntry) ) {
|
if ( name.equalsIgnoreCase(ElemEntry) ) {
|
||||||
ctxEntry = new PwEntryV4();
|
ctxEntry = new PwEntryV4();
|
||||||
ctxHistoryBase.history.add(ctxEntry);
|
ctxHistoryBase.addToHistory(ctxEntry);
|
||||||
|
|
||||||
entryInHistory = true;
|
entryInHistory = true;
|
||||||
return SwitchContext(ctx, KdbContext.Entry, xpp);
|
return SwitchContext(ctx, KdbContext.Entry, xpp);
|
||||||
@@ -796,13 +797,13 @@ public class ImporterV4 extends Importer {
|
|||||||
} else if ( ctx == KdbContext.CustomIcons && name.equalsIgnoreCase(ElemCustomIcons) ) {
|
} else if ( ctx == KdbContext.CustomIcons && name.equalsIgnoreCase(ElemCustomIcons) ) {
|
||||||
return KdbContext.Meta;
|
return KdbContext.Meta;
|
||||||
} else if ( ctx == KdbContext.CustomIcon && name.equalsIgnoreCase(ElemCustomIconItem) ) {
|
} else if ( ctx == KdbContext.CustomIcon && name.equalsIgnoreCase(ElemCustomIconItem) ) {
|
||||||
if ( ! customIconID.equals(PwDatabaseV4.UUID_ZERO) ) {
|
if ( ! customIconID.equals(PwDatabase.UUID_ZERO) ) {
|
||||||
PwIconCustom icon = new PwIconCustom(customIconID, customIconData);
|
PwIconCustom icon = new PwIconCustom(customIconID, customIconData);
|
||||||
db.customIcons.add(icon);
|
db.customIcons.add(icon);
|
||||||
db.iconFactory.put(icon);
|
db.iconFactory.put(icon);
|
||||||
} else assert(false);
|
} else assert(false);
|
||||||
|
|
||||||
customIconID = PwDatabaseV4.UUID_ZERO;
|
customIconID = PwDatabase.UUID_ZERO;
|
||||||
customIconData = null;
|
customIconData = null;
|
||||||
|
|
||||||
return KdbContext.CustomIcons;
|
return KdbContext.CustomIcons;
|
||||||
@@ -820,8 +821,8 @@ public class ImporterV4 extends Importer {
|
|||||||
|
|
||||||
return KdbContext.CustomData;
|
return KdbContext.CustomData;
|
||||||
} else if ( ctx == KdbContext.Group && name.equalsIgnoreCase(ElemGroup) ) {
|
} else if ( ctx == KdbContext.Group && name.equalsIgnoreCase(ElemGroup) ) {
|
||||||
if ( ctxGroup.uuid == null || ctxGroup.uuid.equals(PwDatabaseV4.UUID_ZERO) ) {
|
if ( ctxGroup.getUUID() == null || ctxGroup.getUUID().equals(PwDatabase.UUID_ZERO) ) {
|
||||||
ctxGroup.uuid = UUID.randomUUID();
|
ctxGroup.setUUID(UUID.randomUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxGroups.pop();
|
ctxGroups.pop();
|
||||||
@@ -839,7 +840,7 @@ public class ImporterV4 extends Importer {
|
|||||||
return KdbContext.Group;
|
return KdbContext.Group;
|
||||||
} else if ( ctx == KdbContext.GroupCustomDataItem && name.equalsIgnoreCase(ElemStringDictExItem)) {
|
} else if ( ctx == KdbContext.GroupCustomDataItem && name.equalsIgnoreCase(ElemStringDictExItem)) {
|
||||||
if (groupCustomDataKey != null && groupCustomDataValue != null) {
|
if (groupCustomDataKey != null && groupCustomDataValue != null) {
|
||||||
ctxGroup.customData.put(groupCustomDataKey, groupCustomDataKey);
|
ctxGroup.putCustomData(groupCustomDataKey, groupCustomDataKey);
|
||||||
} else {
|
} else {
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
@@ -850,8 +851,8 @@ public class ImporterV4 extends Importer {
|
|||||||
return KdbContext.GroupCustomData;
|
return KdbContext.GroupCustomData;
|
||||||
|
|
||||||
} else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(ElemEntry) ) {
|
} else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(ElemEntry) ) {
|
||||||
if ( ctxEntry.uuid == null || ctxEntry.uuid.equals(PwDatabaseV4.UUID_ZERO) ) {
|
if ( ctxEntry.getUUID() == null || ctxEntry.getUUID().equals(PwDatabase.UUID_ZERO) ) {
|
||||||
ctxEntry.uuid = UUID.randomUUID();
|
ctxEntry.setUUID(UUID.randomUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( entryInHistory ) {
|
if ( entryInHistory ) {
|
||||||
@@ -863,13 +864,13 @@ public class ImporterV4 extends Importer {
|
|||||||
} else if ( ctx == KdbContext.EntryTimes && name.equalsIgnoreCase(ElemTimes) ) {
|
} else if ( ctx == KdbContext.EntryTimes && name.equalsIgnoreCase(ElemTimes) ) {
|
||||||
return KdbContext.Entry;
|
return KdbContext.Entry;
|
||||||
} else if ( ctx == KdbContext.EntryString && name.equalsIgnoreCase(ElemString) ) {
|
} else if ( ctx == KdbContext.EntryString && name.equalsIgnoreCase(ElemString) ) {
|
||||||
ctxEntry.strings.put(ctxStringName, ctxStringValue);
|
ctxEntry.addField(ctxStringName, ctxStringValue);
|
||||||
ctxStringName = null;
|
ctxStringName = null;
|
||||||
ctxStringValue = null;
|
ctxStringValue = null;
|
||||||
|
|
||||||
return KdbContext.Entry;
|
return KdbContext.Entry;
|
||||||
} else if ( ctx == KdbContext.EntryBinary && name.equalsIgnoreCase(ElemBinary) ) {
|
} else if ( ctx == KdbContext.EntryBinary && name.equalsIgnoreCase(ElemBinary) ) {
|
||||||
ctxEntry.binaries.put(ctxBinaryName, ctxBinaryValue);
|
ctxEntry.putProtectedBinary(ctxBinaryName, ctxBinaryValue);
|
||||||
ctxBinaryName = null;
|
ctxBinaryName = null;
|
||||||
ctxBinaryValue = null;
|
ctxBinaryValue = null;
|
||||||
|
|
||||||
@@ -877,7 +878,7 @@ public class ImporterV4 extends Importer {
|
|||||||
} else if ( ctx == KdbContext.EntryAutoType && name.equalsIgnoreCase(ElemAutoType) ) {
|
} else if ( ctx == KdbContext.EntryAutoType && name.equalsIgnoreCase(ElemAutoType) ) {
|
||||||
return KdbContext.Entry;
|
return KdbContext.Entry;
|
||||||
} else if ( ctx == KdbContext.EntryAutoTypeItem && name.equalsIgnoreCase(ElemAutoTypeItem) ) {
|
} else if ( ctx == KdbContext.EntryAutoTypeItem && name.equalsIgnoreCase(ElemAutoTypeItem) ) {
|
||||||
ctxEntry.autoType.put(ctxATName, ctxATSeq);
|
ctxEntry.getAutoType().put(ctxATName, ctxATSeq);
|
||||||
ctxATName = null;
|
ctxATName = null;
|
||||||
ctxATSeq = null;
|
ctxATSeq = null;
|
||||||
|
|
||||||
@@ -886,7 +887,7 @@ public class ImporterV4 extends Importer {
|
|||||||
return KdbContext.Entry;
|
return KdbContext.Entry;
|
||||||
} else if ( ctx == KdbContext.EntryCustomDataItem && name.equalsIgnoreCase(ElemStringDictExItem)) {
|
} else if ( ctx == KdbContext.EntryCustomDataItem && name.equalsIgnoreCase(ElemStringDictExItem)) {
|
||||||
if (entryCustomDataKey != null && entryCustomDataValue != null) {
|
if (entryCustomDataKey != null && entryCustomDataValue != null) {
|
||||||
ctxEntry.customData.put(entryCustomDataKey, entryCustomDataValue);
|
ctxEntry.putCustomData(entryCustomDataKey, entryCustomDataValue);
|
||||||
} else {
|
} else {
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
@@ -913,6 +914,10 @@ public class ImporterV4 extends Importer {
|
|||||||
throw new RuntimeException("Invalid end element: Context " + contextName + "End element: " + name);
|
throw new RuntimeException("Invalid end element: Context " + contextName + "End element: " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PwDate ReadPwTime(XmlPullParser xpp) throws IOException, XmlPullParserException {
|
||||||
|
return new PwDate(ReadTime(xpp));
|
||||||
|
}
|
||||||
|
|
||||||
private Date ReadTime(XmlPullParser xpp) throws IOException, XmlPullParserException {
|
private Date ReadTime(XmlPullParser xpp) throws IOException, XmlPullParserException {
|
||||||
String sDate = ReadString(xpp);
|
String sDate = ReadString(xpp);
|
||||||
@@ -922,7 +927,7 @@ public class ImporterV4 extends Importer {
|
|||||||
byte[] buf = Base64Coder.decode(sDate);
|
byte[] buf = Base64Coder.decode(sDate);
|
||||||
if (buf.length != 8) {
|
if (buf.length != 8) {
|
||||||
byte[] buf8 = new byte[8];
|
byte[] buf8 = new byte[8];
|
||||||
System.arraycopy(buf, 0, buf8, 0, buf.length);
|
System.arraycopy(buf, 0, buf8, 0, Math.min(buf.length, 8));
|
||||||
buf = buf8;
|
buf = buf8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -981,7 +986,7 @@ public class ImporterV4 extends Importer {
|
|||||||
String encoded = ReadString(xpp);
|
String encoded = ReadString(xpp);
|
||||||
|
|
||||||
if (encoded == null || encoded.length() == 0 ) {
|
if (encoded == null || encoded.length() == 0 ) {
|
||||||
return PwDatabaseV4.UUID_ZERO;
|
return PwDatabase.UUID_ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Switch to framework Base64 once API level 8 is the minimum
|
// TODO: Switch to framework Base64 once API level 8 is the minimum
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database.load;
|
package com.keepassdroid.database.load;
|
||||||
|
|
||||||
import com.keepassdroid.UpdateStatus;
|
import com.keepassdroid.tasks.UpdateStatus;
|
||||||
import com.keepassdroid.database.PwDatabaseV4Debug;
|
import com.keepassdroid.database.PwDatabaseV4Debug;
|
||||||
import com.keepassdroid.database.exception.InvalidDBException;
|
import com.keepassdroid.database.exception.InvalidDBException;
|
||||||
|
|
||||||
|
|||||||
@@ -260,8 +260,8 @@ public class PwDbV3Output extends PwDbOutput {
|
|||||||
groupList.add(group);
|
groupList.add(group);
|
||||||
|
|
||||||
// Recurse over children
|
// Recurse over children
|
||||||
for ( int i = 0; i < group.childGroups.size(); i++ ) {
|
for ( int i = 0; i < group.numbersOfChildGroups(); i++ ) {
|
||||||
sortGroup((PwGroupV3) group.childGroups.get(i), groupList);
|
sortGroup((PwGroupV3) group.getChildGroupAt(i), groupList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import com.keepassdroid.database.PwDefsV4;
|
|||||||
import com.keepassdroid.database.PwDeletedObject;
|
import com.keepassdroid.database.PwDeletedObject;
|
||||||
import com.keepassdroid.database.PwEntry;
|
import com.keepassdroid.database.PwEntry;
|
||||||
import com.keepassdroid.database.PwEntryV4;
|
import com.keepassdroid.database.PwEntryV4;
|
||||||
import com.keepassdroid.database.PwEntryV4.AutoType;
|
import com.keepassdroid.database.AutoType;
|
||||||
import com.keepassdroid.database.PwGroup;
|
import com.keepassdroid.database.PwGroup;
|
||||||
import com.keepassdroid.database.PwGroupV4;
|
import com.keepassdroid.database.PwGroupV4;
|
||||||
import com.keepassdroid.database.PwIconCustom;
|
import com.keepassdroid.database.PwIconCustom;
|
||||||
@@ -245,7 +245,7 @@ public class PwDbV4Output extends PwDbOutput {
|
|||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
try {
|
try {
|
||||||
if (group.parent == groupStack.peek()) {
|
if (group.getParent() == groupStack.peek()) {
|
||||||
groupStack.push(group);
|
groupStack.push(group);
|
||||||
startGroup(group);
|
startGroup(group);
|
||||||
break;
|
break;
|
||||||
@@ -434,21 +434,21 @@ public class PwDbV4Output extends PwDbOutput {
|
|||||||
|
|
||||||
private void startGroup(PwGroupV4 group) throws IllegalArgumentException, IllegalStateException, IOException {
|
private void startGroup(PwGroupV4 group) throws IllegalArgumentException, IllegalStateException, IOException {
|
||||||
xml.startTag(null, ElemGroup);
|
xml.startTag(null, ElemGroup);
|
||||||
writeObject(ElemUuid, group.uuid);
|
writeObject(ElemUuid, group.getUUID());
|
||||||
writeObject(ElemName, group.name);
|
writeObject(ElemName, group.getName());
|
||||||
writeObject(ElemNotes, group.notes);
|
writeObject(ElemNotes, group.getNotes());
|
||||||
writeObject(ElemIcon, group.icon.iconId);
|
writeObject(ElemIcon, group.getIconStandard().iconId);
|
||||||
|
|
||||||
if (!group.customIcon.equals(PwIconCustom.ZERO)) {
|
if (!group.getCustomIcon().equals(PwIconCustom.ZERO)) {
|
||||||
writeObject(ElemCustomIconID, group.customIcon.uuid);
|
writeObject(ElemCustomIconID, group.getCustomIcon().uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeList(ElemTimes, group);
|
writeList(ElemTimes, group);
|
||||||
writeObject(ElemIsExpanded, group.isExpanded);
|
writeObject(ElemIsExpanded, group.isExpanded());
|
||||||
writeObject(ElemGroupDefaultAutoTypeSeq, group.defaultAutoTypeSequence);
|
writeObject(ElemGroupDefaultAutoTypeSeq, group.getDefaultAutoTypeSequence());
|
||||||
writeObject(ElemEnableAutoType, group.enableAutoType);
|
writeObject(ElemEnableAutoType, group.getEnableAutoType());
|
||||||
writeObject(ElemEnableSearching, group.enableSearching);
|
writeObject(ElemEnableSearching, group.getEnableSearching());
|
||||||
writeObject(ElemLastTopVisibleEntry, group.lastTopVisibleEntry);
|
writeObject(ElemLastTopVisibleEntry, group.getLastTopVisibleEntry());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,28 +461,28 @@ public class PwDbV4Output extends PwDbOutput {
|
|||||||
|
|
||||||
xml.startTag(null, ElemEntry);
|
xml.startTag(null, ElemEntry);
|
||||||
|
|
||||||
writeObject(ElemUuid, entry.uuid);
|
writeObject(ElemUuid, entry.getUUID());
|
||||||
writeObject(ElemIcon, entry.icon.iconId);
|
writeObject(ElemIcon, entry.getIconStandard().iconId);
|
||||||
|
|
||||||
if (!entry.customIcon.equals(PwIconCustom.ZERO)) {
|
if (!entry.getCustomIcon().equals(PwIconCustom.ZERO)) {
|
||||||
writeObject(ElemCustomIconID, entry.customIcon.uuid);
|
writeObject(ElemCustomIconID, entry.getCustomIcon().uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeObject(ElemFgColor, entry.foregroundColor);
|
writeObject(ElemFgColor, entry.getForegroundColor());
|
||||||
writeObject(ElemBgColor, entry.backgroupColor);
|
writeObject(ElemBgColor, entry.getBackgroupColor());
|
||||||
writeObject(ElemOverrideUrl, entry.overrideURL);
|
writeObject(ElemOverrideUrl, entry.getOverrideURL());
|
||||||
writeObject(ElemTags, entry.tags);
|
writeObject(ElemTags, entry.getTags());
|
||||||
|
|
||||||
writeList(ElemTimes, entry);
|
writeList(ElemTimes, entry);
|
||||||
|
|
||||||
writeList(entry.strings, true);
|
writeList(entry.getFields(), true);
|
||||||
writeList(entry.binaries);
|
writeList(entry.getBinaries());
|
||||||
writeList(ElemAutoType, entry.autoType);
|
writeList(ElemAutoType, entry.getAutoType());
|
||||||
|
|
||||||
if (!isHistory) {
|
if (!isHistory) {
|
||||||
writeList(ElemHistory, entry.history, true);
|
writeList(ElemHistory, entry.getHistory(), true);
|
||||||
} else {
|
} else {
|
||||||
assert(entry.history.size() == 0);
|
assert(entry.sizeOfHistory() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
xml.endTag(null, ElemEntry);
|
xml.endTag(null, ElemEntry);
|
||||||
@@ -755,13 +755,13 @@ public class PwDbV4Output extends PwDbOutput {
|
|||||||
|
|
||||||
xml.startTag(null, name);
|
xml.startTag(null, name);
|
||||||
|
|
||||||
writeObject(ElemLastModTime, it.getLastModificationTime());
|
writeObject(ElemLastModTime, it.getLastModificationTime().getDate());
|
||||||
writeObject(ElemCreationTime, it.getCreationTime());
|
writeObject(ElemCreationTime, it.getCreationTime().getDate());
|
||||||
writeObject(ElemLastAccessTime, it.getLastAccessTime());
|
writeObject(ElemLastAccessTime, it.getLastAccessTime().getDate());
|
||||||
writeObject(ElemExpiryTime, it.getExpiryTime());
|
writeObject(ElemExpiryTime, it.getExpiryTime().getDate());
|
||||||
writeObject(ElemExpires, it.expires());
|
writeObject(ElemExpires, it.expires());
|
||||||
writeObject(ElemUsageCount, it.getUsageCount());
|
writeObject(ElemUsageCount, it.getUsageCount());
|
||||||
writeObject(ElemLocationChanged, it.getLocationChanged());
|
writeObject(ElemLocationChanged, it.getLocationChanged().getDate());
|
||||||
|
|
||||||
xml.endTag(null, name);
|
xml.endTag(null, name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,27 +79,27 @@ public class PwEntryOutputV3 {
|
|||||||
// Group ID
|
// Group ID
|
||||||
mOS.write(GROUPID_FIELD_TYPE);
|
mOS.write(GROUPID_FIELD_TYPE);
|
||||||
mOS.write(LONG_FOUR);
|
mOS.write(LONG_FOUR);
|
||||||
mOS.write(LEDataOutputStream.writeIntBuf(mPE.groupId));
|
mOS.write(LEDataOutputStream.writeIntBuf(mPE.getGroupId()));
|
||||||
|
|
||||||
// Image ID
|
// Image ID
|
||||||
mOS.write(IMAGEID_FIELD_TYPE);
|
mOS.write(IMAGEID_FIELD_TYPE);
|
||||||
mOS.write(LONG_FOUR);
|
mOS.write(LONG_FOUR);
|
||||||
mOS.write(LEDataOutputStream.writeIntBuf(mPE.icon.iconId));
|
mOS.write(LEDataOutputStream.writeIntBuf(mPE.getIconStandard().iconId));
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
//byte[] title = mPE.title.getBytes("UTF-8");
|
//byte[] title = mPE.title.getBytes("UTF-8");
|
||||||
mOS.write(TITLE_FIELD_TYPE);
|
mOS.write(TITLE_FIELD_TYPE);
|
||||||
int titleLen = Types.writeCString(mPE.title, mOS);
|
int titleLen = Types.writeCString(mPE.getTitle(), mOS);
|
||||||
outputBytes += titleLen;
|
outputBytes += titleLen;
|
||||||
|
|
||||||
// URL
|
// URL
|
||||||
mOS.write(URL_FIELD_TYPE);
|
mOS.write(URL_FIELD_TYPE);
|
||||||
int urlLen = Types.writeCString(mPE.url, mOS);
|
int urlLen = Types.writeCString(mPE.getUrl(), mOS);
|
||||||
outputBytes += urlLen;
|
outputBytes += urlLen;
|
||||||
|
|
||||||
// Username
|
// Username
|
||||||
mOS.write(USERNAME_FIELD_TYPE);
|
mOS.write(USERNAME_FIELD_TYPE);
|
||||||
int userLen = Types.writeCString(mPE.username, mOS);
|
int userLen = Types.writeCString(mPE.getUsername(), mOS);
|
||||||
outputBytes += userLen;
|
outputBytes += userLen;
|
||||||
|
|
||||||
// Password
|
// Password
|
||||||
@@ -112,24 +112,24 @@ public class PwEntryOutputV3 {
|
|||||||
|
|
||||||
// Additional
|
// Additional
|
||||||
mOS.write(ADDITIONAL_FIELD_TYPE);
|
mOS.write(ADDITIONAL_FIELD_TYPE);
|
||||||
int addlLen = Types.writeCString(mPE.additional, mOS);
|
int addlLen = Types.writeCString(mPE.getNotes(), mOS);
|
||||||
outputBytes += addlLen;
|
outputBytes += addlLen;
|
||||||
|
|
||||||
// Create date
|
// Create date
|
||||||
writeDate(CREATE_FIELD_TYPE, mPE.tCreation.getCDate());
|
writeDate(CREATE_FIELD_TYPE, mPE.getCreationTime().getCDate());
|
||||||
|
|
||||||
// Modification date
|
// Modification date
|
||||||
writeDate(MOD_FIELD_TYPE, mPE.tLastMod.getCDate());
|
writeDate(MOD_FIELD_TYPE, mPE.getLastModificationTime().getCDate());
|
||||||
|
|
||||||
// Access date
|
// Access date
|
||||||
writeDate(ACCESS_FIELD_TYPE, mPE.tLastAccess.getCDate());
|
writeDate(ACCESS_FIELD_TYPE, mPE.getLastAccessTime().getCDate());
|
||||||
|
|
||||||
// Expiration date
|
// Expiration date
|
||||||
writeDate(EXPIRE_FIELD_TYPE, mPE.tExpire.getCDate());
|
writeDate(EXPIRE_FIELD_TYPE, mPE.getExpiryTime().getCDate());
|
||||||
|
|
||||||
// Binary desc
|
// Binary desc
|
||||||
mOS.write(BINARY_DESC_FIELD_TYPE);
|
mOS.write(BINARY_DESC_FIELD_TYPE);
|
||||||
int descLen = Types.writeCString(mPE.binaryDesc, mOS);
|
int descLen = Types.writeCString(mPE.getBinaryDesc(), mOS);
|
||||||
outputBytes += descLen;
|
outputBytes += descLen;
|
||||||
|
|
||||||
// Binary data
|
// Binary data
|
||||||
|
|||||||
@@ -65,46 +65,46 @@ public class PwGroupOutputV3 {
|
|||||||
// Group ID
|
// Group ID
|
||||||
mOS.write(GROUPID_FIELD_TYPE);
|
mOS.write(GROUPID_FIELD_TYPE);
|
||||||
mOS.write(GROUPID_FIELD_SIZE);
|
mOS.write(GROUPID_FIELD_SIZE);
|
||||||
mOS.write(LEDataOutputStream.writeIntBuf(mPG.groupId));
|
mOS.write(LEDataOutputStream.writeIntBuf(mPG.getGroupId()));
|
||||||
|
|
||||||
// Name
|
// Name
|
||||||
mOS.write(NAME_FIELD_TYPE);
|
mOS.write(NAME_FIELD_TYPE);
|
||||||
Types.writeCString(mPG.name, mOS);
|
Types.writeCString(mPG.getName(), mOS);
|
||||||
|
|
||||||
// Create date
|
// Create date
|
||||||
mOS.write(CREATE_FIELD_TYPE);
|
mOS.write(CREATE_FIELD_TYPE);
|
||||||
mOS.write(DATE_FIELD_SIZE);
|
mOS.write(DATE_FIELD_SIZE);
|
||||||
mOS.write(mPG.tCreation.getCDate());
|
mOS.write(mPG.getCreationTime().getCDate());
|
||||||
|
|
||||||
// Modification date
|
// Modification date
|
||||||
mOS.write(MOD_FIELD_TYPE);
|
mOS.write(MOD_FIELD_TYPE);
|
||||||
mOS.write(DATE_FIELD_SIZE);
|
mOS.write(DATE_FIELD_SIZE);
|
||||||
mOS.write(mPG.tLastMod.getCDate());
|
mOS.write(mPG.getLastModificationTime().getCDate());
|
||||||
|
|
||||||
// Access date
|
// Access date
|
||||||
mOS.write(ACCESS_FIELD_TYPE);
|
mOS.write(ACCESS_FIELD_TYPE);
|
||||||
mOS.write(DATE_FIELD_SIZE);
|
mOS.write(DATE_FIELD_SIZE);
|
||||||
mOS.write(mPG.tLastAccess.getCDate());
|
mOS.write(mPG.getLastAccessTime().getCDate());
|
||||||
|
|
||||||
// Expiration date
|
// Expiration date
|
||||||
mOS.write(EXPIRE_FIELD_TYPE);
|
mOS.write(EXPIRE_FIELD_TYPE);
|
||||||
mOS.write(DATE_FIELD_SIZE);
|
mOS.write(DATE_FIELD_SIZE);
|
||||||
mOS.write(mPG.tExpire.getCDate());
|
mOS.write(mPG.getExpiryTime().getCDate());
|
||||||
|
|
||||||
// Image ID
|
// Image ID
|
||||||
mOS.write(IMAGEID_FIELD_TYPE);
|
mOS.write(IMAGEID_FIELD_TYPE);
|
||||||
mOS.write(IMAGEID_FIELD_SIZE);
|
mOS.write(IMAGEID_FIELD_SIZE);
|
||||||
mOS.write(LEDataOutputStream.writeIntBuf(mPG.icon.iconId));
|
mOS.write(LEDataOutputStream.writeIntBuf(mPG.getIconStandard().iconId));
|
||||||
|
|
||||||
// Level
|
// Level
|
||||||
mOS.write(LEVEL_FIELD_TYPE);
|
mOS.write(LEVEL_FIELD_TYPE);
|
||||||
mOS.write(LEVEL_FIELD_SIZE);
|
mOS.write(LEVEL_FIELD_SIZE);
|
||||||
mOS.write(LEDataOutputStream.writeUShortBuf(mPG.level));
|
mOS.write(LEDataOutputStream.writeUShortBuf(mPG.getLevel()));
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
mOS.write(FLAGS_FIELD_TYPE);
|
mOS.write(FLAGS_FIELD_TYPE);
|
||||||
mOS.write(FLAGS_FIELD_SIZE);
|
mOS.write(FLAGS_FIELD_SIZE);
|
||||||
mOS.write(LEDataOutputStream.writeIntBuf(mPG.flags));
|
mOS.write(LEDataOutputStream.writeIntBuf(mPG.getFlags()));
|
||||||
|
|
||||||
// End
|
// End
|
||||||
mOS.write(END_FIELD_TYPE);
|
mOS.write(END_FIELD_TYPE);
|
||||||
|
|||||||
@@ -19,9 +19,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database.security;
|
package com.keepassdroid.database.security;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class ProtectedBinary {
|
public class ProtectedBinary implements Serializable {
|
||||||
|
|
||||||
public final static ProtectedBinary EMPTY = new ProtectedBinary();
|
public final static ProtectedBinary EMPTY = new ProtectedBinary();
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.keepassdroid.database.security;
|
package com.keepassdroid.database.security;
|
||||||
|
|
||||||
public class ProtectedString {
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class ProtectedString implements Serializable {
|
||||||
|
|
||||||
private String string;
|
private String string;
|
||||||
private boolean protect;
|
private boolean protect;
|
||||||
@@ -38,7 +40,6 @@ public class ProtectedString {
|
|||||||
|
|
||||||
public ProtectedString() {
|
public ProtectedString() {
|
||||||
this(false, "");
|
this(false, "");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProtectedString(boolean enableProtection, String string) {
|
public ProtectedString(boolean enableProtection, String string) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.keepassdroid;
|
package com.keepassdroid.dialogs;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -39,10 +39,10 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import com.keepassdroid.utils.EmptyUtils;
|
import com.keepassdroid.utils.EmptyUtils;
|
||||||
import com.keepassdroid.utils.UriUtil;
|
import com.keepassdroid.utils.UriUtil;
|
||||||
import com.keepassdroid.view.KeyFileHelper;
|
import com.keepassdroid.fileselect.KeyFileHelper;
|
||||||
import com.kunzisoft.keepass.R;
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
public class AssignMasterKeyDialog extends DialogFragment {
|
public class AssignMasterKeyDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
private String masterPassword;
|
private String masterPassword;
|
||||||
private Uri mKeyfile;
|
private Uri mKeyfile;
|
||||||
@@ -226,7 +226,7 @@ public class AssignMasterKeyDialog extends DialogFragment {
|
|||||||
mListener.onAssignKeyDialogPositiveClick(
|
mListener.onAssignKeyDialogPositiveClick(
|
||||||
passwordCheckBox.isChecked(), masterPassword,
|
passwordCheckBox.isChecked(), masterPassword,
|
||||||
keyfileCheckBox.isChecked(), mKeyfile);
|
keyfileCheckBox.isChecked(), mKeyfile);
|
||||||
AssignMasterKeyDialog.this.dismiss();
|
AssignMasterKeyDialogFragment.this.dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -244,7 +244,7 @@ public class AssignMasterKeyDialog extends DialogFragment {
|
|||||||
mListener.onAssignKeyDialogPositiveClick(
|
mListener.onAssignKeyDialogPositiveClick(
|
||||||
passwordCheckBox.isChecked(), masterPassword,
|
passwordCheckBox.isChecked(), masterPassword,
|
||||||
keyfileCheckBox.isChecked(), mKeyfile);
|
keyfileCheckBox.isChecked(), mKeyfile);
|
||||||
AssignMasterKeyDialog.this.dismiss();
|
AssignMasterKeyDialogFragment.this.dismiss();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.keepassdroid;
|
package com.keepassdroid.dialogs;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
@@ -38,6 +38,7 @@ import android.widget.Button;
|
|||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import com.keepassdroid.fileselect.FilePickerStylishActivity;
|
||||||
import com.keepassdroid.utils.UriUtil;
|
import com.keepassdroid.utils.UriUtil;
|
||||||
import com.kunzisoft.keepass.R;
|
import com.kunzisoft.keepass.R;
|
||||||
import com.nononsenseapps.filepicker.FilePickerActivity;
|
import com.nononsenseapps.filepicker.FilePickerActivity;
|
||||||
@@ -45,7 +46,7 @@ import com.nononsenseapps.filepicker.Utils;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
public class CreateFileDialog extends DialogFragment implements AdapterView.OnItemSelectedListener{
|
public class CreateFileDialogFragment extends DialogFragment implements AdapterView.OnItemSelectedListener{
|
||||||
|
|
||||||
private final int FILE_CODE = 3853;
|
private final int FILE_CODE = 3853;
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ public class CreateFileDialog extends DialogFragment implements AdapterView.OnIt
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(final View v) {
|
public void onClick(final View v) {
|
||||||
if(mListener.onDefinePathDialogPositiveClick(buildPath()))
|
if(mListener.onDefinePathDialogPositiveClick(buildPath()))
|
||||||
CreateFileDialog.this.dismiss();
|
CreateFileDialogFragment.this.dismiss();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Button negativeButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_NEGATIVE);
|
Button negativeButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_NEGATIVE);
|
||||||
@@ -142,7 +143,7 @@ public class CreateFileDialog extends DialogFragment implements AdapterView.OnIt
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(final View v) {
|
public void onClick(final View v) {
|
||||||
if(mListener.onDefinePathDialogNegativeClick(buildPath()))
|
if(mListener.onDefinePathDialogNegativeClick(buildPath()))
|
||||||
CreateFileDialog.this.dismiss();
|
CreateFileDialogFragment.this.dismiss();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -17,18 +17,16 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.keepassdroid;
|
package com.keepassdroid.dialogs;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
@@ -36,12 +34,12 @@ import android.widget.SeekBar;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.keepassdroid.password.PasswordGenerator;
|
import com.keepassdroid.password.PasswordGenerator;
|
||||||
import com.keepassdroid.settings.PrefsUtil;
|
import com.keepassdroid.settings.PreferencesUtil;
|
||||||
import com.kunzisoft.keepass.R;
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class GeneratePasswordFragment extends DialogFragment {
|
public class GeneratePasswordDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
public static final String KEY_PASSWORD_ID = "KEY_PASSWORD_ID";
|
public static final String KEY_PASSWORD_ID = "KEY_PASSWORD_ID";
|
||||||
|
|
||||||
@@ -57,6 +55,7 @@ public class GeneratePasswordFragment extends DialogFragment {
|
|||||||
private CompoundButton spaceBox;
|
private CompoundButton spaceBox;
|
||||||
private CompoundButton specialsBox;
|
private CompoundButton specialsBox;
|
||||||
private CompoundButton bracketsBox;
|
private CompoundButton bracketsBox;
|
||||||
|
private CompoundButton extendedBox;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
@@ -76,20 +75,21 @@ public class GeneratePasswordFragment extends DialogFragment {
|
|||||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||||
root = inflater.inflate(R.layout.generate_password, null);
|
root = inflater.inflate(R.layout.generate_password, null);
|
||||||
|
|
||||||
lengthTextView = (EditText) root.findViewById(R.id.length);
|
lengthTextView = root.findViewById(R.id.length);
|
||||||
|
|
||||||
uppercaseBox = (CompoundButton) root.findViewById(R.id.cb_uppercase);
|
uppercaseBox = root.findViewById(R.id.cb_uppercase);
|
||||||
lowercaseBox = (CompoundButton) root.findViewById(R.id.cb_lowercase);
|
lowercaseBox = root.findViewById(R.id.cb_lowercase);
|
||||||
digitsBox = (CompoundButton) root.findViewById(R.id.cb_digits);
|
digitsBox = root.findViewById(R.id.cb_digits);
|
||||||
minusBox = (CompoundButton) root.findViewById(R.id.cb_minus);
|
minusBox = root.findViewById(R.id.cb_minus);
|
||||||
underlineBox = (CompoundButton) root.findViewById(R.id.cb_underline);
|
underlineBox = root.findViewById(R.id.cb_underline);
|
||||||
spaceBox = (CompoundButton) root.findViewById(R.id.cb_space);
|
spaceBox = root.findViewById(R.id.cb_space);
|
||||||
specialsBox = (CompoundButton) root.findViewById(R.id.cb_specials);
|
specialsBox = root.findViewById(R.id.cb_specials);
|
||||||
bracketsBox = (CompoundButton) root.findViewById(R.id.cb_brackets);
|
bracketsBox = root.findViewById(R.id.cb_brackets);
|
||||||
|
extendedBox = root.findViewById(R.id.cb_extended);
|
||||||
|
|
||||||
assignDefaultCharacters();
|
assignDefaultCharacters();
|
||||||
|
|
||||||
SeekBar seekBar = (SeekBar) root.findViewById(R.id.seekbar_length);
|
SeekBar seekBar = root.findViewById(R.id.seekbar_length);
|
||||||
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
@@ -102,34 +102,25 @@ public class GeneratePasswordFragment extends DialogFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) {}
|
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||||
});
|
});
|
||||||
seekBar.setProgress(PrefsUtil.getDefaultPasswordLength(getContext().getApplicationContext()));
|
seekBar.setProgress(PreferencesUtil.getDefaultPasswordLength(getContext()));
|
||||||
|
|
||||||
Button genPassButton = (Button) root.findViewById(R.id.generate_password_button);
|
Button genPassButton = root.findViewById(R.id.generate_password_button);
|
||||||
genPassButton.setOnClickListener(new OnClickListener() {
|
genPassButton.setOnClickListener(v -> fillPassword());
|
||||||
public void onClick(View v) {
|
|
||||||
fillPassword();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.setView(root)
|
builder.setView(root)
|
||||||
.setPositiveButton(R.string.accept, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.accept, (dialog, id) -> {
|
||||||
@Override
|
EditText password = root.findViewById(R.id.password);
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
Bundle bundle = new Bundle();
|
||||||
EditText password = (EditText) root.findViewById(R.id.password);
|
bundle.putString(KEY_PASSWORD_ID, password.getText().toString());
|
||||||
Bundle bundle = new Bundle();
|
mListener.acceptPassword(bundle);
|
||||||
bundle.putString(KEY_PASSWORD_ID, password.getText().toString());
|
|
||||||
mListener.acceptPassword(bundle);
|
|
||||||
|
|
||||||
dismiss();
|
dismiss();
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.cancel, (dialog, id) -> {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
Bundle bundle = new Bundle();
|
||||||
Bundle bundle = new Bundle();
|
mListener.cancelPassword(bundle);
|
||||||
mListener.cancelPassword(bundle);
|
|
||||||
|
|
||||||
dismiss();
|
dismiss();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pre-populate a password to possibly save the user a few clicks
|
// Pre-populate a password to possibly save the user a few clicks
|
||||||
@@ -147,9 +138,10 @@ public class GeneratePasswordFragment extends DialogFragment {
|
|||||||
spaceBox.setChecked(false);
|
spaceBox.setChecked(false);
|
||||||
specialsBox.setChecked(false);
|
specialsBox.setChecked(false);
|
||||||
bracketsBox.setChecked(false);
|
bracketsBox.setChecked(false);
|
||||||
|
extendedBox.setChecked(false);
|
||||||
|
|
||||||
Set<String> defaultPasswordChars =
|
Set<String> defaultPasswordChars =
|
||||||
PrefsUtil.getDefaultPasswordCharacters(getContext().getApplicationContext());
|
PreferencesUtil.getDefaultPasswordCharacters(getContext());
|
||||||
for(String passwordChar : defaultPasswordChars) {
|
for(String passwordChar : defaultPasswordChars) {
|
||||||
if (passwordChar.equals(getString(R.string.value_password_uppercase))) {
|
if (passwordChar.equals(getString(R.string.value_password_uppercase))) {
|
||||||
uppercaseBox.setChecked(true);
|
uppercaseBox.setChecked(true);
|
||||||
@@ -175,11 +167,14 @@ public class GeneratePasswordFragment extends DialogFragment {
|
|||||||
else if (passwordChar.equals(getString(R.string.value_password_brackets))) {
|
else if (passwordChar.equals(getString(R.string.value_password_brackets))) {
|
||||||
bracketsBox.setChecked(true);
|
bracketsBox.setChecked(true);
|
||||||
}
|
}
|
||||||
|
else if (passwordChar.equals(getString(R.string.value_password_extended))) {
|
||||||
|
extendedBox.setChecked(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillPassword() {
|
private void fillPassword() {
|
||||||
EditText txtPassword = (EditText) root.findViewById(R.id.password);
|
EditText txtPassword = root.findViewById(R.id.password);
|
||||||
txtPassword.setText(generatePassword());
|
txtPassword.setText(generatePassword());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +192,8 @@ public class GeneratePasswordFragment extends DialogFragment {
|
|||||||
underlineBox.isChecked(),
|
underlineBox.isChecked(),
|
||||||
spaceBox.isChecked(),
|
spaceBox.isChecked(),
|
||||||
specialsBox.isChecked(),
|
specialsBox.isChecked(),
|
||||||
bracketsBox.isChecked());
|
bracketsBox.isChecked(),
|
||||||
|
extendedBox.isChecked());
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
Toast.makeText(getContext(), R.string.error_wrong_length, Toast.LENGTH_LONG).show();
|
Toast.makeText(getContext(), R.string.error_wrong_length, Toast.LENGTH_LONG).show();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.keepassdroid;
|
package com.keepassdroid.dialogs;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -32,56 +32,78 @@ import android.widget.ImageButton;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R;
|
import com.keepassdroid.database.PwNode;
|
||||||
import com.keepassdroid.icons.Icons;
|
import com.keepassdroid.icons.Icons;
|
||||||
|
import com.kunzisoft.keepass.R;
|
||||||
|
|
||||||
public class GroupEditFragment extends DialogFragment
|
public class GroupEditDialogFragment extends DialogFragment
|
||||||
implements IconPickerFragment.IconPickerListener {
|
implements IconPickerDialogFragment.IconPickerListener {
|
||||||
|
|
||||||
|
public static final String TAG_CREATE_GROUP = "TAG_CREATE_GROUP";
|
||||||
|
|
||||||
public static final String KEY_NAME = "name";
|
public static final String KEY_NAME = "name";
|
||||||
public static final String KEY_ICON_ID = "icon_id";
|
public static final String KEY_ICON_ID = "icon_id";
|
||||||
|
|
||||||
private CreateGroupListener createGroupListener;
|
private EditGroupListener editGroupListener;
|
||||||
|
|
||||||
|
private TextView nameField;
|
||||||
|
private ImageButton iconButton;
|
||||||
private int mSelectedIconID;
|
private int mSelectedIconID;
|
||||||
private View root;
|
private View root;
|
||||||
|
|
||||||
|
public static GroupEditDialogFragment build(PwNode group) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putString(KEY_NAME, group.getDisplayTitle());
|
||||||
|
// TODO Change
|
||||||
|
bundle.putInt(KEY_ICON_ID, group.getIcon().hashCode());
|
||||||
|
GroupEditDialogFragment fragment = new GroupEditDialogFragment();
|
||||||
|
fragment.setArguments(bundle);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
// Verify that the host activity implements the callback interface
|
// Verify that the host activity implements the callback interface
|
||||||
try {
|
try {
|
||||||
// Instantiate the NoticeDialogListener so we can send events to the host
|
// Instantiate the NoticeDialogListener so we can send events to the host
|
||||||
createGroupListener = (CreateGroupListener) context;
|
editGroupListener = (EditGroupListener) context;
|
||||||
createGroupListener = (CreateGroupListener) context;
|
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
// The activity doesn't implement the interface, throw exception
|
// The activity doesn't implement the interface, throw exception
|
||||||
throw new ClassCastException(context.toString()
|
throw new ClassCastException(context.toString()
|
||||||
+ " must implement " + GroupEditFragment.class.getName());
|
+ " must implement " + GroupEditDialogFragment.class.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
|
||||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
|
||||||
|
|
||||||
|
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||||
root = inflater.inflate(R.layout.group_edit, null);
|
root = inflater.inflate(R.layout.group_edit, null);
|
||||||
|
nameField = (TextView) root.findViewById(R.id.group_name);
|
||||||
|
iconButton = (ImageButton) root.findViewById(R.id.icon_button);
|
||||||
|
|
||||||
|
if (getArguments() != null
|
||||||
|
&& getArguments().containsKey(KEY_NAME)
|
||||||
|
&& getArguments().containsKey(KEY_ICON_ID)) {
|
||||||
|
nameField.setText(getArguments().getString(KEY_NAME));
|
||||||
|
populateIcon(getArguments().getInt(KEY_ICON_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
builder.setView(root)
|
builder.setView(root)
|
||||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
TextView nameField = (TextView) root.findViewById(R.id.group_name);
|
|
||||||
String name = nameField.getText().toString();
|
String name = nameField.getText().toString();
|
||||||
|
|
||||||
if ( name.length() > 0 ) {
|
if ( name.length() > 0 ) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString(KEY_NAME, name);
|
bundle.putString(KEY_NAME, name);
|
||||||
bundle.putInt(KEY_ICON_ID, mSelectedIconID);
|
bundle.putInt(KEY_ICON_ID, mSelectedIconID);
|
||||||
createGroupListener.approveCreateGroup(bundle);
|
editGroupListener.approveEditGroup(bundle);
|
||||||
|
|
||||||
GroupEditFragment.this.getDialog().cancel();
|
GroupEditDialogFragment.this.getDialog().cancel();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Toast.makeText(getContext(), R.string.error_no_name, Toast.LENGTH_LONG).show();
|
Toast.makeText(getContext(), R.string.error_no_name, Toast.LENGTH_LONG).show();
|
||||||
@@ -91,32 +113,35 @@ public class GroupEditFragment extends DialogFragment
|
|||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
createGroupListener.cancelCreateGroup(bundle);
|
editGroupListener.cancelEditGroup(bundle);
|
||||||
|
|
||||||
GroupEditFragment.this.getDialog().cancel();
|
GroupEditDialogFragment.this.getDialog().cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final ImageButton iconButton = (ImageButton) root.findViewById(R.id.icon_button);
|
final ImageButton iconButton = (ImageButton) root.findViewById(R.id.icon_button);
|
||||||
iconButton.setOnClickListener(new View.OnClickListener() {
|
iconButton.setOnClickListener(new View.OnClickListener() {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
IconPickerFragment iconPickerFragment = new IconPickerFragment();
|
IconPickerDialogFragment iconPickerDialogFragment = new IconPickerDialogFragment();
|
||||||
iconPickerFragment.show(getFragmentManager(), "IconPickerFragment");
|
iconPickerDialogFragment.show(getFragmentManager(), "IconPickerDialogFragment");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void populateIcon(int iconId) {
|
||||||
public void iconPicked(Bundle bundle) {
|
iconButton.setImageResource(Icons.iconToResId(iconId));
|
||||||
mSelectedIconID = bundle.getInt(IconPickerFragment.KEY_ICON_ID);
|
|
||||||
ImageButton currIconButton = (ImageButton) root.findViewById(R.id.icon_button);
|
|
||||||
currIconButton.setImageResource(Icons.iconToResId(mSelectedIconID));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface CreateGroupListener {
|
@Override
|
||||||
void approveCreateGroup(Bundle bundle);
|
public void iconPicked(Bundle bundle) {
|
||||||
void cancelCreateGroup(Bundle bundle);
|
mSelectedIconID = bundle.getInt(IconPickerDialogFragment.KEY_ICON_ID);
|
||||||
|
populateIcon(mSelectedIconID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface EditGroupListener {
|
||||||
|
void approveEditGroup(Bundle bundle);
|
||||||
|
void cancelEditGroup(Bundle bundle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user