Compare commits

..

117 Commits

Author SHA1 Message Date
Jeremy
c24c18d89e Merge branch 'release/2.5.0.0beta4' 2018-02-01 17:44:29 +01:00
Jeremy
61043b3acb Add fastlane/README.md 2018-02-01 17:18:35 +01:00
Jeremy
52f8862e71 Add changelogs 2018-02-01 15:44:36 +01:00
J-Jamet
8bd32e6605 Update fastlane script 2018-02-01 02:53:05 +01:00
J-Jamet
d430305eb1 Upgrade version 2018-02-01 02:52:45 +01:00
J-Jamet
2cb3972865 Add fastlane 2018-01-31 20:26:43 +01:00
J-Jamet
724bb1fc86 Solve issue for gradle compilation 2018-01-31 20:07:05 +01:00
J-Jamet
38def26865 Solve crash when keyfile is selected 2018-01-28 22:43:13 +01:00
Jeremy
a08e65733d Upgrade disclaimer 2018-01-28 20:04:45 +01:00
Jeremy
e5bc9bfd1d Merge branch 'JanThomas118-update-german-translation' into develop 2018-01-28 19:31:17 +01:00
Jeremy
767f7b06d6 Merge branch 'upstream-update' issue #23 into develop 2018-01-28 18:55:56 +01:00
Jeremy
a06977cd25 Upgrade CHANGELOG 2018-01-28 18:48:05 +01:00
Jeremy
60be6f1223 Merge branch 'feature/FingerPrint' issue #20 into develop 2018-01-28 18:13:37 +01:00
Jeremy
e9929ed848 Fingerprint decrypt without fill password view 2018-01-28 16:56:26 +01:00
Jeremy
21c657c107 Code factoring 2018-01-28 16:37:18 +01:00
Jan Thomas
a93271401d fixed one punctuation error and removed accidental space at start of file 2018-01-27 22:59:40 +01:00
Jan Thomas
a66cd68ae2 Added more german translation strings 2018-01-27 22:54:33 +01:00
Jeremy
9ac060ea05 New settings to delete fingerprints 2018-01-27 15:49:23 +01:00
Jeremy
c20f453b90 Add fingerprints keystore deletion when disabled 2018-01-27 15:08:10 +01:00
J-Jamet
27d633a1e9 Lock fingerprint settings 2018-01-26 21:36:08 +01:00
J-Jamet
221a851f44 Solve fingerprints bugs 2018-01-26 20:50:37 +01:00
J-Jamet
c091ffb5e1 Remove thread when typing 2018-01-26 17:11:22 +01:00
J-Jamet
d8f81b669d Remove singleInstance in PasswordActivity -> bug in Kitkat 2018-01-24 22:03:53 +01:00
J-Jamet
fb72f37ebb Solve bug KeepassDX #18 2018-01-24 21:11:43 +01:00
Jeremy
6c5936d15d Add singleInstance tag for some activities 2018-01-23 22:31:40 +01:00
Jeremy
e68c682cac Decode FileUri in views 2018-01-23 22:31:01 +01:00
Jeremy
04da145513 Solve DocumentFile bug 2018-01-23 22:19:16 +01:00
J-Jamet
a15a039f2a Merge branch 'feature/ListOpening' into develop 2018-01-17 18:27:06 +01:00
J-Jamet
818c0a769b Update screenshots and CHANGELOG 2018-01-17 18:24:14 +01:00
J-Jamet
cc7b8a3fd8 Add setting to select file path and solve bugs 2018-01-17 17:42:26 +01:00
J-Jamet
43b4d00902 Add file deletion, move information and solve bugs 2018-01-17 15:28:11 +01:00
J-Jamet
06b126469a Add information dialog for file 2018-01-14 22:46:38 +01:00
J-Jamet
2ca4e817e9 Solve bug of titleFileList 2018-01-14 20:11:13 +01:00
J-Jamet
42a52b26bf FileSelect as RecyclerView 2018-01-14 19:34:32 +01:00
J-Jamet
5d8a73080b Change donation url 2018-01-14 17:45:59 +01:00
J-Jamet
0b736ce0b3 Change edit text layout color 2018-01-12 20:44:28 +01:00
J-Jamet
dea6515e90 Update file layout 2018-01-12 20:04:53 +01:00
J-Jamet
1bd1b2a224 Update "Open recent database" text 2018-01-12 19:01:00 +01:00
J-Jamet
b7a76ed2e7 Change file_selection 2018-01-12 18:56:25 +01:00
Jeremy
a1237215cc New layout 2018-01-10 23:20:54 +01:00
J-Jamet
4bb869e288 Merge branch 'develop' into upstream-update 2018-01-03 12:33:41 +01:00
J-Jamet
1d528488d3 Merge branch 'master' 2.2.1 of https://github.com/bpellin/keepassdroid into upstream-update 2018-01-03 12:16:49 +01:00
Brian Pellin
90282d9722 Version bump 2018-01-02 22:37:31 -06:00
bpellin
2c7b19d67d Merge pull request #255 from shanempope/dev
Fixing issue where Search opens new task instead of using same task.
2018-01-02 22:26:35 -06:00
bpellin
981fef8fb1 Merge pull request #256 from EdlerProgrammierer/patch-1
Add DE translation
2018-01-02 22:24:56 -06:00
Brian Pellin
77c8207c73 Fix kdbx4 date corruption 2018-01-02 22:08:22 -06:00
Jérémy JAMET
825a5c7e73 Update ReadMe.md 2018-01-02 19:10:29 +01:00
Jeremy
d921a0ae1a Change fingerprint exceptions 2018-01-02 00:49:11 +01:00
EdlerProgrammierer
1946844858 Add DE translation 2017-12-13 19:29:31 +01:00
J-Jamet
b62106068b Solve compilation bug for fdroid 2017-12-13 18:55:32 +01:00
Jeremy
1218d89173 Solve preferences bugs 2017-12-12 02:57:53 +01:00
Shane Pope
052641c556 Removing extraneous comment 2017-12-11 18:18:55 -06:00
Shane Pope
7950933d1f Fixing issue where Search opens new task instead of using same task.
If you open KeePassDroid from a file in another app, the KeePassDroid activity opens in that Task. However, if you search the ACTION_SEARCH intent opens a new task putting the app in a weird state that has various bugs. The easy way to fix this is setting PasswordActivity to LaunchMode="singleTask" in AndroidManifest. The other option is to remove the FLAG_ACTIVITY_NEW_TASK intent flagbit on search allowing the search activity to stay in the current task. I've included the second option in this changeset.
2017-12-11 18:10:58 -06:00
J-Jamet
37279af514 Change preferences as compat 2017-12-09 00:48:34 +01:00
J-Jamet
b45a8b5c94 Solve bug for fingerprint layout 2017-12-09 00:08:32 +01:00
J-Jamet
8a344170b3 Change description for rounds fix 2017-12-08 22:36:47 +01:00
J-Jamet
09f6498907 Solve settings style for API < 21 2017-12-08 21:04:02 +01:00
J-Jamet
57b8b9e53b Add features in Changelog 2017-12-08 21:03:33 +01:00
J-Jamet
8edb2c26d4 Merge branch 'upstream-update' 2.2.0.9 into develop #11 2017-12-08 20:30:26 +01:00
J-Jamet
7d7b34b2d0 Merge branch 'feature/EmptyPassword' into develop #2 2017-12-08 20:02:36 +01:00
J-Jamet
73765a7ecb Merge branch 'feature/FingerprintDialog' into develop #4 2017-12-08 19:18:26 +01:00
Jeremy
fb99c32708 Add fix for encryption rounds 2017-12-08 17:08:47 +01:00
Jeremy
6bb0b1a7e7 Add database lock for screen off 2017-12-08 15:53:25 +01:00
Jeremy
7a66c964a7 Add fingerprint setting for API < 23 2017-12-07 19:19:31 +01:00
Jeremy
a5b1535fb8 Add fingerprint setting 2017-12-07 15:57:16 +01:00
J-Jamet
05a5da4c8d Add fingerprint dialog and update icons 2017-12-06 22:28:25 +01:00
J-Jamet
f5314e0a9a Update ReadMe and screens 2017-12-05 21:14:54 +01:00
J-Jamet
332d1e0e06 Solve dialogs bugs (checkboxes) 2017-12-05 21:00:02 +01:00
Jeremy
9173c99928 Solve layout for selections 2017-12-04 21:28:44 +01:00
Jeremy
421bdae0d7 Solve select path bugs 2017-12-04 17:55:54 +01:00
Jeremy
421de6ea49 Add folder picker and solve asynctask 2017-12-04 04:39:52 +01:00
J-Jamet
ec2dd3db64 Add dialog to create database file 2017-12-03 16:45:40 +01:00
J-Jamet
e6cc9b628e New keyfile management 2017-12-01 20:03:22 +01:00
J-Jamet
7cce527e43 New workflow to assign master password 2017-12-01 18:24:20 +01:00
Jeremy
dd35fd508f Merge 2.2.0.9 into upstream-update 2017-11-30 22:40:34 +01:00
Brian Pellin
a7b5fd512a Version bump 2017-11-30 14:37:07 -06:00
Brian Pellin
45b03231dd Update build tools version 2017-11-30 14:34:20 -06:00
bpellin
85357e2168 Merge pull request #248 from pepeEL/patch-1
Update translation PL
2017-11-30 14:30:35 -06:00
bpellin
d6709254e2 Merge pull request #247 from bboa/master
Updated Russian strings.xml
2017-11-30 14:29:41 -06:00
J-Jamet
ef42dcd47f Change views and add checkboxes 2017-11-30 19:16:34 +01:00
J-Jamet
8536586c13 Solve settings title and change box by switch 2017-11-30 01:39:59 +01:00
J-Jamet
4794ccb38a Option to disable notifications #7 2017-11-30 01:32:16 +01:00
J-Jamet
d14dc22918 Solve layout password issue 2017-11-30 00:52:12 +01:00
J-Jamet
79084cee62 Merge branch 'develop' of repo 2017-11-30 00:39:23 +01:00
J-Jamet
f0ec9229cd Add visual representation of characters 2017-11-30 00:37:52 +01:00
J-Jamet
f6b04dff24 Password characters in settings 2017-11-30 00:19:26 +01:00
J-Jamet
b6721b32e7 Password length in settings #3 2017-11-29 20:42:01 +01:00
Jérémy JAMET
b1804a94b6 Update ReadMe.md 2017-11-29 20:41:22 +01:00
J-Jamet
f6bcd51cd3 Update gradle, .gitignore, add art screen and update readme 2017-11-29 20:41:22 +01:00
J-Jamet
a833dfe64a Remove unused libs and upgrade version 2017-11-29 20:41:22 +01:00
J-Jamet
87efd1daa4 Merge keepassdroid 2.2.0.8 and update changelog 2017-11-29 18:47:26 +01:00
J-Jamet
ecdd744b8e Merge keepassdroid 2.2.0.8 and update changelog 2017-11-29 18:46:46 +01:00
J-Jamet
9012fd6da6 Merge branch 'master' into develop 2017-11-28 01:27:31 +01:00
J-Jamet
c6a711dec5 Solve open file bug 2017-11-28 01:27:22 +01:00
pepeEL
9b1a0285c4 Update translation PL 2017-11-27 07:15:08 +01:00
Igor Nedoboy
fd6dd01ee7 Update strings.xml 2017-11-27 03:32:54 +03:00
Jérémy JAMET
bd10fe8542 Update ReadMe.md 2017-11-27 00:08:37 +01:00
J-Jamet
8333a80d88 Update gradle, .gitignore, add art screen and update readme 2017-11-27 00:07:08 +01:00
Brian Pellin
192f116925 Version bump 2017-11-25 23:45:25 -06:00
Brian Pellin
409ffd4f95 Fix UTF8 encoding 2017-11-25 23:42:24 -06:00
Brian Pellin
0add3c76c8 Add rounds corruption fix mode. 2017-11-25 23:42:21 -06:00
Brian Pellin
b4f9e06bd6 Version bump 2017-11-24 21:33:28 -06:00
Brian Pellin
c686ff7156 Fix kdbx4 attachments 2017-11-24 21:28:23 -06:00
Brian Pellin
da1370e7f5 Use the correct number of key encryption rounds on KDBX <4 2017-11-24 20:26:23 -06:00
Brian Pellin
c564253206 Use ACTION_GET_CONTENT when not using the storage access framework 2017-11-24 19:19:33 -06:00
Brian Pellin
3f22bf9e5c Updated Hungarian translations. 2017-11-24 19:18:06 -06:00
Brian Pellin
eae321d034 Add additional ndk abis 2017-11-21 20:59:07 -06:00
Brian Pellin
e2022183ea Version bump 2017-11-21 20:43:18 -06:00
Brian Pellin
924db245ec Make DateFormatter threadsafe 2017-11-21 20:40:55 -06:00
Brian Pellin
2dae805ac0 Only show fingerprint prompt on devices with fingerprint hardware 2017-11-21 20:39:40 -06:00
Brian Pellin
9ce0a413ad Update build file versions 2017-11-21 20:38:30 -06:00
Brian Pellin
1ece7b1df7 Version bump 2017-11-19 21:12:38 -06:00
Brian Pellin
b7aec355a2 Fingerprint fixes 2017-11-19 21:09:55 -06:00
Brian Pellin
780a0cd42d Version bump 2017-11-19 19:30:20 -06:00
Brian Pellin
de45421b50 Handle devices with unconfigured fingerprint sensors better 2017-11-19 19:27:47 -06:00
Brian Pellin
c11e17af05 Remove new APIs from fingerprint helper 2017-11-19 19:11:16 -06:00
Brian Pellin
260de4099b Fix crash on empty search results 2017-11-19 16:46:47 -06:00
146 changed files with 6350 additions and 1506 deletions

15
.gitignore vendored
View File

@@ -6,6 +6,7 @@
# Built application files
*.apk
*.ap_
app/free_google/*
# Files for the ART/Dalvik VM
*.dex
@@ -39,7 +40,6 @@ captures/
# Intellij
*.iml
<<<<<<< HEAD
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
@@ -49,19 +49,16 @@ captures/
# Keystore files
# Uncomment the following line if you do not want to check your keystore files in.
#*.jks
=======
.idea/*
# Keystore files
*.jks
>>>>>>> master
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
google-services.json
<<<<<<< HEAD
# Freeline
freeline.py
@@ -70,5 +67,11 @@ freeline_project_description.json
# Iml Files
app/app.iml
=======
>>>>>>> master
# Art
art/screen*.png
art/logo_512.png
# Dir linux
.directory
*/.directory

View File

@@ -1,3 +1,26 @@
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)
* New database workflow with new screens and folder selection
* Settings for default password generation
* Fingerprint dialog for explanations
* Setting to disable fingerprint
* Directly opening kdbx file
* Setting to disable notifications
* Setting to lock database when screen is shut off
* Merge KeePassDroid 2.2.0.9
* Add corruption fix mode
KeepassDX (2.5.0.0beta2)
* Remove libs for F-Droid
KeepassDX (2.5.0.0beta1)
* Fork KeepassDroid
* Add Material Design
@@ -7,6 +30,37 @@ KeepassDX (2.5.0.0beta1)
* Update French translation
* 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)
* Update build tools version to workaround CM/Lineage bug (closes: #249)
* Update Russian translations
* Update Polish translations
KeePassDroid (2.2.0.8)
* Add corruption fix mode
* Update Hungarian translations
KeePassDroid (2.2.0.7)
* Fix KDBX3 encryption rounds corruption
* Fix KDBX4 attachement crashes
KeePassDroid (2.2.0.6)
* Add additional ndk ABIs
KeePassDroid (2.2.0.5)
* Don't show fingerprint prompt on devices without fingerprint hardware
* Fix dateformat crashes
KeePassDroid (2.2.0.4)
* Fingerprint fixes
KeePassDroid (2.2.0.3)
* Search crash fix
* Improve fingerprint handling
KeePassDroid (2.2.0.2)
* Fix non fingerprint password layout

View File

@@ -14,14 +14,14 @@ Tadashi Saito
vhschlenker
bumper314 - Samsung multiwindow support
Hans Cappelle - fingerprint sensor integration
Jeremy Jamet - Material Design - Patches
Jeremy Jamet - Keepass DX Material Design - Patches
Translations:
Diego Pierotto - Italian
Laurent, Norman Obry, Nam, Bruno Parmentier, Credomo - French
Maciej Bieniek, cod3r - Polish
Максим Сёмочкин, i.nedoboy, filimonic, bboa - Russian
MaWi, rvs2008, meviox, MaDill - German
MaWi, rvs2008, meviox, MaDill, EdlerProgrammierer, Jan Thomas - German
yslandro - Norwegian Nynorsk
王科峰 - Chinese
Typhoon - Slovak
@@ -30,7 +30,7 @@ Matsuu Takuto - Japanese
Carlos Schlyter - Portugese (Brazil)
YSmhXQDd6Z - Portugese (Portugal)
andriykopanytsia - Ukranian
intel - Hungarian
intel, Zoltán Antal - Hungarian
H Vanek - Czech
jipanos - Spanish
Erik Fdevriendt, Erik Jan Meijer - Dutch

View File

@@ -4,13 +4,14 @@
### Features
- Create database file / entries and groups
- Simplified creation of the database file
- Create entries and groups
- Open database, copy username / password, open URI / URL
- Fingerprint for fast unlocking
- Material design with themes
- Device integration and AutoFill (In progress)
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen0.jpg" width="220">
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen1.jpg" width="220">
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen2.jpg" width="220">
## What is KeePass?
@@ -27,13 +28,23 @@ Yes, KeePass is really free, and more than that: it is open source (OSI certifie
Even if the application is free, to maintain the application, you can make donations.
[![Donation Paypal](https://4.bp.blogspot.com/-ncaIbUGaHOk/WfhaThYUPGI/AAAAAAAAAVQ/_HidNgdB1q4DaC24ujaKNzH64KUUJiQewCLcBGAs/s1600/pay-with-paypal.png "")](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KM6QMDAXZM3UU "Kunzisoft Paypal Donation")
[![Donation Paypal](https://4.bp.blogspot.com/-ncaIbUGaHOk/WfhaThYUPGI/AAAAAAAAAVQ/_HidNgdB1q4DaC24ujaKNzH64KUUJiQewCLcBGAs/s1600/pay-with-paypal.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KM6QMDAXZM3UU "Kunzisoft Paypal Donation")
[![Donation Liberapay](https://liberapay.com/assets/widgets/donate.svg "")](https://liberapay.com/Kunzisoft/donate "Kunzisoft Liberapay Donation")
[![Donation Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/Kunzisoft/donate "Kunzisoft Liberapay Donation")
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen4.jpg" width="220">
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen5.jpg" width="220">
## Download
[<img src="https://f-droid.org/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.kunzisoft.keepass.libre/)
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play"
height="80">](https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.free)
### JNI
Native library build instructions:

View File

@@ -2,17 +2,22 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion = 25
buildToolsVersion = "26.0.2"
buildToolsVersion = "27.0.1"
defaultConfig {
applicationId "com.kunzisoft.keepass"
minSdkVersion 14
targetSdkVersion 25
versionCode = 2
versionName = "2.5.0.0beta2"
versionCode = 4
versionName = "2.5.0.0beta4"
testApplicationId = "com.keepassdroid.tests"
testInstrumentationRunner = "android.test.InstrumentationTestRunner"
ndk {
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
'arm64-v8a', 'mips', 'mips64'
}
}
externalNativeBuild {
@@ -21,6 +26,7 @@ android {
}
}
buildTypes {
release {
minifyEnabled = false
@@ -63,7 +69,10 @@ dependencies {
compile "com.android.support:design:$supportVersion"
compile "com.android.support:preference-v7:$supportVersion"
compile "com.android.support:preference-v14:$supportVersion"
compile "com.android.support:cardview-v7:$supportVersion"
compile "com.madgag.spongycastle:core:$spongycastleVersion"
compile "com.madgag.spongycastle:prov:$spongycastleVersion"
compile "joda-time:joda-time:2.9.9"
compile "org.sufficientlysecure:html-textview:3.5"
compile "com.nononsenseapps:filepicker:4.1.0"
}

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kunzisoft.keepass"
android:installLocation="auto">
xmlns:tools="http://schemas.android.com/tools"
package="com.kunzisoft.keepass"
android:installLocation="auto">
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
@@ -19,10 +20,31 @@
android:allowBackup="true"
android:fullBackupContent="@xml/backup"
android:backupAgent="com.keepassdroid.backup.SettingsBackupAgent"
android:theme="@style/KeepassDXStyle.Light">
android:theme="@style/KeepassDXStyle.Light"
tools:replace="android:theme">
<!-- TODO backup API Key -->
<meta-data android:name="com.google.android.backup.api_key"
android:value="" />
<!-- Folder picker -->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/nnf_provider_paths" />
</provider>
<activity
android:name="com.keepassdroid.FilePickerStylishActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".KeePass"
android:label="@string/app_name">
<intent-filter>
@@ -31,17 +53,21 @@
</intent-filter>
</activity>
<activity android:name="com.keepassdroid.fileselect.FileSelectActivity"
android:launchMode="singleInstance"
android:configChanges="orientation|keyboardHidden"
android:windowSoftInputMode="stateHidden" />
<activity android:name="com.keepassdroid.AboutActivity"
android:launchMode="singleInstance"
android:label="@string/menu_about" />
<activity android:name="com.keepassdroid.PasswordActivity" android:configChanges="orientation|keyboardHidden">
<activity android:name="com.keepassdroid.PasswordActivity"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:scheme="content" />
<data android:mimeType="application/octet-stream" />
<data android:host="*" />
<data android:pathPattern=".*\\.kdb" />
<data android:pathPattern=".*\\..*\\.kdb" />
@@ -66,14 +92,10 @@
</intent-filter>
</activity>
<activity android:name="com.keepassdroid.GroupActivityV3" android:configChanges="orientation|keyboardHidden">
<!-- This metadata entry causes .app.SearchQueryResults to be the default context -->
<!-- whenever the user invokes search while in this Activity. -->
<meta-data android:name="android.app.default_searchable"
android:value="com.keepassdroid.search.SearchResults" />
</activity>
<activity android:name="com.keepassdroid.GroupActivityV4" android:configChanges="orientation|keyboardHidden">
<!-- This metadata entry causes .app.SearchQueryResults to be the default context -->
<!-- whenever the user invokes search while in this Activity. -->
<meta-data android:name="android.app.default_searchable"
android:value="com.keepassdroid.search.SearchResults"
android:exported="false" />

View File

@@ -27,8 +27,10 @@ import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
import com.kunzisoft.keepass.R;
import com.keepassdroid.stylish.StylishActivity;
import com.kunzisoft.keepass.R;
import org.joda.time.DateTime;
public class AboutActivity extends StylishActivity {
@@ -56,6 +58,9 @@ public class AboutActivity extends StylishActivity {
version = getString(R.string.version_label) + " " + version;
TextView versionText = (TextView) findViewById(R.id.activity_about_version);
versionText.setText(version);
TextView disclaimerText = (TextView) findViewById(R.id.disclaimer);
disclaimerText.setText(getString(R.string.disclaimer_formal, new DateTime().getYear()));
}
@Override

View File

@@ -0,0 +1,274 @@
/*
* 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.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;
import com.keepassdroid.utils.EmptyUtils;
import com.keepassdroid.utils.UriUtil;
import com.keepassdroid.view.KeyFileHelper;
import com.kunzisoft.keepass.R;
public class AssignMasterKeyDialog extends DialogFragment {
private String masterPassword;
private Uri mKeyfile;
private View rootView;
private CompoundButton passwordCheckBox;
private TextView passView;
private TextView passConfView;
private CompoundButton keyfileCheckBox;
private TextView keyfileView;
private AssignPasswordDialogListener mListener;
private KeyFileHelper keyFileHelper;
public interface AssignPasswordDialogListener {
void onAssignKeyDialogPositiveClick(boolean masterPasswordChecked, String masterPassword,
boolean keyFileChecked, Uri keyFile);
void onAssignKeyDialogNegativeClick(boolean masterPasswordChecked, String masterPassword,
boolean keyFileChecked, Uri keyFile);
}
@Override
public void onAttach(Context activity) {
super.onAttach(activity);
try {
mListener = (AssignPasswordDialogListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement " + AssignPasswordDialogListener.class.getName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
rootView = inflater.inflate(R.layout.set_password, null);
builder.setView(rootView)
.setTitle(R.string.assign_master_key)
// Add action buttons
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
passwordCheckBox = (CompoundButton) rootView.findViewById(R.id.password_checkbox);
passView = (TextView) rootView.findViewById(R.id.pass_password);
passView.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) {
passwordCheckBox.setChecked(true);
}
});
passConfView = (TextView) rootView.findViewById(R.id.pass_conf_password);
keyfileCheckBox = (CompoundButton) rootView.findViewById(R.id.keyfile_checkox);
keyfileView = (TextView) rootView.findViewById(R.id.pass_keyfile);
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) {
keyfileCheckBox.setChecked(true);
}
});
keyFileHelper = new KeyFileHelper(this);
rootView.findViewById(R.id.browse_button)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
keyFileHelper.getOpenFileOnClickViewListener().onClick(view);
}
});
AlertDialog dialog = builder.create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(final DialogInterface dialog) {
Button positiveButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE);
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
masterPassword = "";
mKeyfile = null;
boolean error = verifyPassword() || verifyFile();
if (!passwordCheckBox.isChecked() && !keyfileCheckBox.isChecked()) {
error = true;
showNoKeyConfirmationDialog();
}
if (!error) {
mListener.onAssignKeyDialogPositiveClick(
passwordCheckBox.isChecked(), masterPassword,
keyfileCheckBox.isChecked(), mKeyfile);
dismiss();
}
}
});
Button negativeButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_NEGATIVE);
negativeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
mListener.onAssignKeyDialogNegativeClick(
passwordCheckBox.isChecked(), masterPassword,
keyfileCheckBox.isChecked(), mKeyfile);
dismiss();
}
});
}
});
return dialog;
}
private boolean verifyPassword() {
boolean error = false;
if (passwordCheckBox.isChecked()) {
masterPassword = passView.getText().toString();
String confpass = passConfView.getText().toString();
// Verify that passwords match
if (!masterPassword.equals(confpass)) {
error = true;
// Passwords do not match
Toast.makeText(getContext(), R.string.error_pass_match, Toast.LENGTH_LONG).show();
}
if (masterPassword == null || masterPassword.isEmpty()) {
error = true;
showEmptyPasswordConfirmationDialog();
}
}
return error;
}
private boolean verifyFile() {
boolean error = false;
if (keyfileCheckBox.isChecked()) {
Uri keyfile = UriUtil.parseDefaultFile(keyfileView.getText().toString());
mKeyfile = keyfile;
// Verify that a keyfile is set
if (EmptyUtils.isNullOrEmpty(keyfile)) {
error = true;
Toast.makeText(getContext(), R.string.error_nokeyfile, Toast.LENGTH_LONG).show();
}
}
return error;
}
private void showEmptyPasswordConfirmationDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.warning_empty_password)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if (!verifyFile()) {
mListener.onAssignKeyDialogPositiveClick(
passwordCheckBox.isChecked(), masterPassword,
keyfileCheckBox.isChecked(), mKeyfile);
AssignMasterKeyDialog.this.dismiss();
}
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
builder.create().show();
}
private void showNoKeyConfirmationDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.warning_no_encryption_key)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mListener.onAssignKeyDialogPositiveClick(
passwordCheckBox.isChecked(), masterPassword,
keyfileCheckBox.isChecked(), mKeyfile);
AssignMasterKeyDialog.this.dismiss();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
builder.create().show();
}
@Override
public 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) {
Uri pathString = UriUtil.parseDefaultFile(uri.toString());
if (pathString != null) {
keyfileCheckBox.setChecked(true);
keyfileView.setText(pathString.toString());
}
}
}
});
}
}

View File

@@ -0,0 +1,182 @@
/*
* 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;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import com.keepassdroid.utils.UriUtil;
import com.kunzisoft.keepass.R;
import com.nononsenseapps.filepicker.FilePickerActivity;
import com.nononsenseapps.filepicker.Utils;
import java.io.File;
public class CreateFileDialog extends DialogFragment implements AdapterView.OnItemSelectedListener{
private final int FILE_CODE = 3853;
private EditText folderPathView;
private EditText fileNameView;
private DefinePathDialogListener mListener;
private String extension;
private Uri uriPath;
public interface DefinePathDialogListener {
boolean onDefinePathDialogPositiveClick(Uri pathFile);
boolean onDefinePathDialogNegativeClick(Uri pathFile);
}
@Override
public void onAttach(Context activity) {
super.onAttach(activity);
try {
mListener = (DefinePathDialogListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement " + DefinePathDialogListener.class.getName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View rootView = inflater.inflate(R.layout.file_creation, null);
builder.setView(rootView)
.setTitle(R.string.create_keepass_file)
// Add action buttons
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
// Folder selection
View browseView = rootView.findViewById(R.id.browse_button);
folderPathView = (EditText) rootView.findViewById(R.id.folder_path);
fileNameView = (EditText) rootView.findViewById(R.id.filename);
String defaultPath = Environment.getExternalStorageDirectory().getPath()
+ getString(R.string.database_file_path_default);
folderPathView.setText(defaultPath);
browseView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getContext(), FilePickerStylishActivity.class);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true);
i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR);
i.putExtra(FilePickerActivity.EXTRA_START_PATH,
Environment.getExternalStorageDirectory().getPath());
startActivityForResult(i, FILE_CODE);
}
});
// Init path
uriPath = null;
// Extension
extension = getString(R.string.database_file_extension_default);
Spinner spinner = (Spinner) rootView.findViewById(R.id.file_types);
spinner.setOnItemSelectedListener(this);
// Spinner Drop down elements
String[] fileTypes = getResources().getStringArray(R.array.file_types);
ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, fileTypes);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(dataAdapter);
AlertDialog dialog = builder.create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(final DialogInterface dialog) {
Button positiveButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE);
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if(mListener.onDefinePathDialogPositiveClick(buildPath()))
CreateFileDialog.this.dismiss();
}
});
Button negativeButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_NEGATIVE);
negativeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if(mListener.onDefinePathDialogNegativeClick(buildPath()))
CreateFileDialog.this.dismiss();
}
});
}
});
return dialog;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) {
uriPath = data.getData();
if (uriPath != null) {
File file = Utils.getFileForUri(uriPath);
folderPathView.setText(file.getPath());
}
}
}
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
extension = adapterView.getItemAtPosition(position).toString();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
// Do nothing
}
private Uri buildPath() {
Uri path = new Uri.Builder().path(folderPathView.getText().toString())
.appendPath(fileNameView.getText().toString() + extension)
.build();
path = UriUtil.translate(getContext(), path);
return path;
}
}

View File

@@ -19,9 +19,29 @@
*/
package com.keepassdroid;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.util.Log;
import com.keepassdroid.database.PwDatabase;
import com.keepassdroid.database.PwDatabaseV3;
import com.keepassdroid.database.PwGroup;
import com.keepassdroid.database.exception.ContentFileNotFoundException;
import com.keepassdroid.database.exception.InvalidDBException;
import com.keepassdroid.database.exception.InvalidPasswordException;
import com.keepassdroid.database.exception.PwDbOutputException;
import com.keepassdroid.database.load.Importer;
import com.keepassdroid.database.load.ImporterFactory;
import com.keepassdroid.database.save.PwDbOutput;
import com.keepassdroid.icons.DrawableFactory;
import com.keepassdroid.search.SearchDbHelper;
import com.keepassdroid.utils.UriUtil;
import com.kunzisoft.keepass.R;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -31,24 +51,6 @@ import java.io.SyncFailedException;
import java.util.HashSet;
import java.util.Set;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import com.keepassdroid.database.PwDatabase;
import com.keepassdroid.database.PwDatabaseV3;
import com.keepassdroid.database.PwGroup;
import com.keepassdroid.database.exception.ContentFileNotFoundException;
import com.keepassdroid.database.exception.InvalidDBException;
import com.keepassdroid.database.exception.PwDbOutputException;
import com.keepassdroid.database.load.Importer;
import com.keepassdroid.database.load.ImporterFactory;
import com.keepassdroid.database.save.PwDbOutput;
import com.keepassdroid.icons.DrawableFactory;
import com.keepassdroid.search.SearchDbHelper;
import com.keepassdroid.utils.UriUtil;
/**
* @author bpellin
*/
@@ -92,6 +94,26 @@ public class Database {
readOnly = !file.canWrite();
}
try {
passUrisAsInputStreams(ctx, uri, password, keyfile, status, debug, 0);
} catch (InvalidPasswordException e) {
// Retry with rounds fix
try {
passUrisAsInputStreams(ctx, uri, password, keyfile, status, debug, getFixRounds(ctx));
} catch (Exception e2) {
// Rethrow original exception
throw e;
}
}
}
private long getFixRounds(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getLong(ctx.getString(R.string.roundsFix_key), ctx.getResources().getInteger(R.integer.roundsFix_default));
}
private void passUrisAsInputStreams(Context ctx, Uri uri, String password, Uri keyfile, UpdateStatus status, boolean debug, long roundsFix) throws IOException, FileNotFoundException, InvalidDBException {
InputStream is, kfIs;
try {
is = UriUtil.getUriInputStream(ctx, uri);
@@ -106,7 +128,7 @@ public class Database {
Log.e("KPD", "Database::LoadData", e);
throw ContentFileNotFoundException.getInstance(keyfile);
}
LoadData(ctx, is, password, kfIs, status, debug);
LoadData(ctx, is, password, kfIs, status, debug, roundsFix);
}
public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, boolean debug) throws IOException, InvalidDBException {
@@ -114,6 +136,10 @@ public class Database {
}
public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, UpdateStatus status, boolean debug) throws IOException, InvalidDBException {
LoadData(ctx, is, password, kfIs, status, debug, 0);
}
public void LoadData(Context ctx, InputStream is, String password, InputStream kfIs, UpdateStatus status, boolean debug, long roundsFix) throws IOException, InvalidDBException {
BufferedInputStream bis = new BufferedInputStream(is);
if ( ! bis.markSupported() ) {
@@ -127,7 +153,7 @@ public class Database {
bis.reset(); // Return to the start
pm = imp.openDatabase(bis, password, kfIs, status);
pm = imp.openDatabase(bis, password, kfIs, status, roundsFix);
if ( pm != null ) {
PwGroup root = pm.rootGroup;
pm.populateGlobals(root);

View File

@@ -69,6 +69,8 @@ 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";
@@ -153,7 +155,6 @@ public class EntryActivity extends LockCloseHideActivity {
Intent i = getIntent();
UUID uuid = Types.bytestoUUID(i.getByteArrayExtra(KEY_ENTRY));
mPos = i.getIntExtra(KEY_REFRESH_POS, -1);
assert(uuid != null);
mEntry = db.pm.entries.get(uuid);
if (mEntry == null) {
@@ -171,37 +172,41 @@ public class EntryActivity extends LockCloseHideActivity {
fillData(false);
setupEditButtons();
// 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);
}
// 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.equals(Intents.COPY_USERNAME) ) {
String username = mEntry.getUsername();
if ( username.length() > 0 ) {
timeoutCopyToClipboard(username);
}
} else if ( action.equals(Intents.COPY_PASSWORD) ) {
String password = new String(mEntry.getPassword());
if ( password.length() > 0 ) {
timeoutCopyToClipboard(new String(mEntry.getPassword()));
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);
}
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.util.Log;
import com.keepassdroid.stylish.Stylish;
import com.kunzisoft.keepass.R;
import com.nononsenseapps.filepicker.FilePickerActivity;
public class FilePickerStylishActivity extends FilePickerActivity {
private @StyleRes
int themeId;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
this.themeId = FilePickerStylish.getThemeId(this);
setTheme(themeId);
super.onCreate(savedInstanceState);
}
@Override
protected void onResume() {
super.onResume();
if(FilePickerStylish.getThemeId(this) != this.themeId) {
Log.d(this.getClass().getName(), "Theme change detected, restarting activity");
this.recreate();
}
}
public static class FilePickerStylish extends Stylish {
public static @StyleRes int getThemeId(Context context) {
if (themeString.equals(context.getString(R.string.list_style_name_night)))
return R.style.KeepassDXStyle_FilePickerStyle_Night;
return R.style.KeepassDXStyle_FilePickerStyle_Light;
}
}
}

View File

@@ -30,13 +30,16 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.Toast;
import com.kunzisoft.keepass.R;
import com.keepassdroid.password.PasswordGenerator;
import com.keepassdroid.settings.PrefsUtil;
import com.kunzisoft.keepass.R;
import java.util.Set;
public class GeneratePasswordFragment extends DialogFragment {
@@ -46,6 +49,15 @@ public class GeneratePasswordFragment extends DialogFragment {
private View root;
private EditText lengthTextView;
private CompoundButton uppercaseBox;
private CompoundButton lowercaseBox;
private CompoundButton digitsBox;
private CompoundButton minusBox;
private CompoundButton underlineBox;
private CompoundButton spaceBox;
private CompoundButton specialsBox;
private CompoundButton bracketsBox;
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -66,6 +78,17 @@ public class GeneratePasswordFragment extends DialogFragment {
lengthTextView = (EditText) root.findViewById(R.id.length);
uppercaseBox = (CompoundButton) root.findViewById(R.id.cb_uppercase);
lowercaseBox = (CompoundButton) root.findViewById(R.id.cb_lowercase);
digitsBox = (CompoundButton) root.findViewById(R.id.cb_digits);
minusBox = (CompoundButton) root.findViewById(R.id.cb_minus);
underlineBox = (CompoundButton) root.findViewById(R.id.cb_underline);
spaceBox = (CompoundButton) root.findViewById(R.id.cb_space);
specialsBox = (CompoundButton) root.findViewById(R.id.cb_specials);
bracketsBox = (CompoundButton) root.findViewById(R.id.cb_brackets);
assignDefaultCharacters();
SeekBar seekBar = (SeekBar) root.findViewById(R.id.seekbar_length);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
@@ -79,6 +102,7 @@ public class GeneratePasswordFragment extends DialogFragment {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
seekBar.setProgress(PrefsUtil.getDefaultPasswordLength(getContext().getApplicationContext()));
Button genPassButton = (Button) root.findViewById(R.id.generate_password_button);
genPassButton.setOnClickListener(new OnClickListener() {
@@ -113,6 +137,46 @@ public class GeneratePasswordFragment extends DialogFragment {
return builder.create();
}
private void assignDefaultCharacters() {
uppercaseBox.setChecked(false);
lowercaseBox.setChecked(false);
digitsBox.setChecked(false);
minusBox.setChecked(false);
underlineBox.setChecked(false);
spaceBox.setChecked(false);
specialsBox.setChecked(false);
bracketsBox.setChecked(false);
Set<String> defaultPasswordChars =
PrefsUtil.getDefaultPasswordCharacters(getContext().getApplicationContext());
for(String passwordChar : defaultPasswordChars) {
if (passwordChar.equals(getString(R.string.value_password_uppercase))) {
uppercaseBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_lowercase))) {
lowercaseBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_digits))) {
digitsBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_minus))) {
minusBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_underline))) {
underlineBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_space))) {
spaceBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_special))) {
specialsBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_brackets))) {
bracketsBox.setChecked(true);
}
}
}
private void fillPassword() {
EditText txtPassword = (EditText) root.findViewById(R.id.password);
@@ -121,23 +185,19 @@ public class GeneratePasswordFragment extends DialogFragment {
public String generatePassword() {
String password = "";
try {
int length = Integer.valueOf(((EditText) root.findViewById(R.id.length)).getText().toString());
((CheckBox) root.findViewById(R.id.cb_uppercase)).isChecked();
PasswordGenerator generator = new PasswordGenerator(getActivity());
password = generator.generatePassword(length,
((CheckBox) root.findViewById(R.id.cb_uppercase)).isChecked(),
((CheckBox) root.findViewById(R.id.cb_lowercase)).isChecked(),
((CheckBox) root.findViewById(R.id.cb_digits)).isChecked(),
((CheckBox) root.findViewById(R.id.cb_minus)).isChecked(),
((CheckBox) root.findViewById(R.id.cb_underline)).isChecked(),
((CheckBox) root.findViewById(R.id.cb_space)).isChecked(),
((CheckBox) root.findViewById(R.id.cb_specials)).isChecked(),
((CheckBox) root.findViewById(R.id.cb_brackets)).isChecked());
uppercaseBox.isChecked(),
lowercaseBox.isChecked(),
digitsBox.isChecked(),
minusBox.isChecked(),
underlineBox.isChecked(),
spaceBox.isChecked(),
specialsBox.isChecked(),
bracketsBox.isChecked());
} catch (NumberFormatException e) {
Toast.makeText(getContext(), R.string.error_wrong_length, Toast.LENGTH_LONG).show();
} catch (IllegalArgumentException e) {

View File

@@ -19,12 +19,14 @@
*/
package com.keepassdroid;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Build;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
@@ -40,8 +42,6 @@ import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.kunzisoft.keepass.KeePass;
import com.kunzisoft.keepass.R;
import com.keepassdroid.app.App;
import com.keepassdroid.compat.ActivityCompat;
import com.keepassdroid.compat.EditorCompat;
@@ -49,10 +49,14 @@ 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 {
public abstract class GroupBaseActivity extends LockCloseListActivity
implements AssignMasterKeyDialog.AssignPasswordDialogListener {
protected ListView mList;
protected ListAdapter mAdapter;
@@ -148,14 +152,15 @@ public abstract class GroupBaseActivity extends LockCloseListActivity {
private void ensureCorrectListView(){
mList = (ListView)findViewById(R.id.group_list);
mList.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
{
onListItemClick((ListView)parent, v, position, id);
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
@@ -265,8 +270,26 @@ public abstract class GroupBaseActivity extends LockCloseListActivity {
}
@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() {
SetPasswordDialog dialog = new SetPasswordDialog();
AssignMasterKeyDialog dialog = new AssignMasterKeyDialog();
dialog.show(getSupportFragmentManager(), "passwordDialog");
}
@@ -284,6 +307,24 @@ public abstract class GroupBaseActivity extends LockCloseListActivity {
}
}
}
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
/*
* ACTION_SEARCH automatically forces a new task. This occurs when you open a kdb file in
* another app such as Files or GoogleDrive and then Search for an entry. Here we remove the
* FLAG_ACTIVITY_NEW_TASK flag bit allowing search to open it's activity in the current task.
*/
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
int flags = intent.getFlags();
flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK;
intent.setFlags(flags);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
super.startActivityForResult(intent, requestCode, options);
}
}
public class AfterDeleteGroup extends OnFinish {
public AfterDeleteGroup(Handler handler) {

View File

@@ -22,26 +22,8 @@ package com.keepassdroid;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import com.kunzisoft.keepass.KeePass;
import com.keepassdroid.app.App;
public abstract class LockCloseActivity extends LockingActivity {
@Override
protected void onResume() {
super.onResume();
checkShutdown();
}
private void checkShutdown() {
if ( App.isShutdown() && App.getDB().Loaded() ) {
setResult(KeePass.EXIT_LOCK);
finish();
}
}
/* (non-Javadoc) Workaround for HTC Linkify issues
* @see android.app.Activity#startActivity(android.content.Intent)
*/

View File

@@ -20,23 +20,76 @@
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 {
@Override
protected void onPause() {
super.onPause();
TimeoutHelper.pause(this);
}
private ScreenReceiver screenReceiver;
@Override
@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();
}
}
}
}
}
}

View File

@@ -20,8 +20,6 @@
package com.keepassdroid;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
@@ -41,65 +39,68 @@ 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.kunzisoft.keepass.KeePass;
import com.kunzisoft.keepass.R;
import com.keepassdroid.app.App;
import com.keepassdroid.compat.BackupManagerCompat;
import com.keepassdroid.compat.ClipDataCompat;
import com.keepassdroid.compat.EditorCompat;
import com.keepassdroid.compat.StorageAF;
import com.keepassdroid.database.edit.LoadDB;
import com.keepassdroid.database.edit.OnFinish;
import com.keepassdroid.dialog.PasswordEncodingDialogHelper;
import com.keepassdroid.fileselect.BrowserDialog;
import com.keepassdroid.fingerprint.FingerPrintAnimatedVector;
import com.keepassdroid.fingerprint.FingerPrintHelper;
import com.keepassdroid.intents.Intents;
import com.keepassdroid.settings.PrefsUtil;
import com.keepassdroid.utils.EmptyUtils;
import com.keepassdroid.utils.Interaction;
import com.keepassdroid.utils.MenuUtil;
import com.keepassdroid.utils.UriUtil;
import com.keepassdroid.utils.Util;
import com.keepassdroid.view.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 class PasswordActivity extends LockingActivity
implements FingerPrintHelper.FingerPrintCallback, UriIntentInitTaskCallback {
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 static final int FILE_BROWSE = 256;
public static final int GET_CONTENT = 257;
private static final int OPEN_DOC = 258;
private Uri mDbUri = null;
private Uri mKeyUri = null;
private boolean mRememberKeyfile;
SharedPreferences prefs;
SharedPreferences prefsNoBackup;
private FingerPrintHelper fingerPrintHelper;
private boolean fingerprintMustBeConfigured = true;
private boolean mRememberKeyfile;
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 fingerprintView;
private TextView confirmationView;
private View fingerprintContainerView;
private View fingerprintImageView;
private FingerPrintAnimatedVector fingerPrintAnimatedVector;
private TextView fingerprintTextView;
private TextView filenameView;
private EditText passwordView;
private Button confirmButton;
private EditText keyFileView;
private Button confirmButtonView;
private CompoundButton checkboxPasswordView;
private CompoundButton checkboxKeyfileView;
private CompoundButton checkboxDefaultDatabaseView;
private KeyFileHelper keyFileHelper;
public static void Launch(
Activity act,
@@ -116,6 +117,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
}
Uri uri = UriUtil.parseDefaultFile(fileName);
assert uri != null;
String scheme = uri.getScheme();
if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
@@ -126,8 +128,8 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
}
Intent i = new Intent(act, PasswordActivity.class);
i.putExtra(KEY_FILENAME, fileName);
i.putExtra(KEY_KEYFILE, keyFile);
i.putExtra(UriIntentInitTask.KEY_FILENAME, fileName);
i.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile);
act.startActivityForResult(i, 0);
@@ -140,48 +142,30 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (keyFileHelper != null) {
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:
setEditText(R.id.password, "");
setEmptyViews();
App.getDB().clear();
break;
case KeePass.EXIT_LOCK:
setResult(KeePass.EXIT_LOCK);
setEditText(R.id.password, "");
setEmptyViews();
finish();
App.getDB().clear();
break;
case FILE_BROWSE:
if (resultCode == RESULT_OK) {
String filename = data.getDataString();
if (filename != null) {
EditText fn = (EditText) findViewById(R.id.pass_keyfile);
fn.setText(filename);
mKeyUri = UriUtil.parseDefaultFile(filename);
}
}
break;
case GET_CONTENT:
case OPEN_DOC:
if (resultCode == RESULT_OK) {
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
if (requestCode == GET_CONTENT) {
uri = UriUtil.translate(this, uri);
}
String path = uri.toString();
if (path != null) {
EditText fn = (EditText) findViewById(R.id.pass_keyfile);
fn.setText(path);
}
mKeyUri = uri;
}
}
}
break;
}
}
@@ -189,12 +173,12 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent i = getIntent();
prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefsNoBackup = getSharedPreferences("nobackup", Context.MODE_PRIVATE);
prefsNoBackup = PrefsUtil.getNoBackupSharedPreferences(getApplicationContext());
mRememberKeyfile = prefs.getBoolean(getString(R.string.keyfile_key),
getResources().getBoolean(R.bool.keyfile_default));
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);
@@ -204,12 +188,114 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
confirmButton = (Button) findViewById(R.id.pass_ok);
fingerprintView = findViewById(R.id.fingerprint);
confirmationView = (TextView) findViewById(R.id.fingerprint_label);
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 = (CompoundButton) findViewById(R.id.password_checkbox);
checkboxKeyfileView = (CompoundButton) findViewById(R.id.keyfile_checkox);
checkboxDefaultDatabaseView = (CompoundButton) findViewById(R.id.default_database);
new InitTask().execute(i);
View browseView = findViewById(R.id.browse_button);
keyFileHelper = new KeyFileHelper(PasswordActivity.this);
browseView.setOnClickListener(keyFileHelper.getOpenFileOnClickViewListener());
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);
}
});
new UriIntentInitTask(this, mRememberKeyfile)
.execute(getIntent());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
fingerPrintAnimatedVector = new FingerPrintAnimatedVector(this,
(ImageView) fingerprintImageView);
}
}
@Override
public void onPostInitTask(Uri dbUri, Uri keyFileUri, Integer errorStringId) {
mDbUri = dbUri;
mKeyUri = keyFileUri;
Intent intent = getIntent();
String password = intent.getStringExtra(KEY_PASSWORD);
boolean launch_immediately = intent.getBooleanExtra(KEY_LAUNCH_IMMEDIATELY, false);
if (errorStringId != null) {
Toast.makeText(PasswordActivity.this, errorStringId, Toast.LENGTH_LONG).show();
finish();
return;
}
populateView();
confirmButtonView.setOnClickListener(new OkClickHandler());
if (password != null) {
passwordView.setText(password);
}
checkboxDefaultDatabaseView.setOnCheckedChangeListener(new DefaultCheckChange());
retrieveSettings();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkFingerprintAvailability();
}
if (launch_immediately) {
verifyCheckboxesAndLoadDatabase(password, mKeyUri);
}
}
private void retrieveSettings() {
String defaultFilename = prefs.getString(KEY_DEFAULT_FILENAME, "");
if (mDbUri!=null
&& !EmptyUtils.isNullOrEmpty(mDbUri.getPath())
&& UriUtil.equalsDefaultfile(mDbUri, defaultFilename)) {
checkboxDefaultDatabaseView.setChecked(true);
}
}
private void populateView() {
String db = (mDbUri == null) ? "" : mDbUri.toString();
if (!db.isEmpty()) {
if (PrefsUtil.isFullFilePathEnable(this))
filenameView.setText(db);
else
filenameView.setText(new File(mDbUri.getPath()).getName()); // TODO Encapsulate
}
String key = (mKeyUri == null) ? "" : mKeyUri.toString();
if (!key.isEmpty() && mRememberKeyfile) { // Bug KeepassDX #18
keyFileView.setText(key);
}
}
@Override
@@ -219,8 +305,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
// If the application was shutdown make sure to clear the password field, if it
// was saved in the instance state
if (App.isShutdown()) {
TextView password = (TextView) findViewById(R.id.password);
password.setText("");
setEmptyViews();
}
// Clear the shutdown flag
@@ -229,15 +314,20 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
// checks if fingerprint is available, will also start listening for fingerprints when available
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
initForFingerprint();
checkAvailability();
checkFingerprintAvailability();
if (fingerPrintAnimatedVector != null) {
fingerPrintAnimatedVector.startScan();
}
}
}
private void retrieveSettings() {
String defaultFilename = prefs.getString(KEY_DEFAULT_FILENAME, "");
if (!EmptyUtils.isNullOrEmpty(mDbUri.getPath()) && UriUtil.equalsDefaultfile(mDbUri, defaultFilename)) {
CheckBox checkbox = (CheckBox) findViewById(R.id.default_database);
checkbox.setChecked(true);
private void setEmptyViews() {
passwordView.setText("");
checkboxPasswordView.setChecked(false);
// Bug KeepassDX #18
if (!mRememberKeyfile) {
keyFileView.setText("");
checkboxKeyfileView.setChecked(false);
}
}
@@ -249,21 +339,11 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
}
}
private void populateView() {
String db = (mDbUri == null) ? "" : mDbUri.toString();
setEditText(R.id.filename, db);
String key = (mKeyUri == null) ? "" : mKeyUri.toString();
setEditText(R.id.pass_keyfile, key);
}
private void errorMessage(int resId) {
Toast.makeText(this, resId, Toast.LENGTH_LONG).show();
}
// fingerprint related code here
@RequiresApi(api = Build.VERSION_CODES.M)
private void initForFingerprint() {
mode = -1;
fingerPrintHelper = new FingerPrintHelper(this, this);
// when text entered we can enable the logon/purchase button and if required update encryption/decryption mode
@@ -287,7 +367,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
if ( !fingerprintMustBeConfigured ) {
final boolean validInput = s.length() > 0;
// encrypt or decrypt mode based on how much input or not
confirmationView.setText(validInput ? R.string.store_with_fingerprint : R.string.scanning_fingerprint);
setFingerPrintTextView(validInput ? R.string.store_with_fingerprint : R.string.scanning_fingerprint);
mode = validInput ? toggleMode(Cipher.ENCRYPT_MODE) : toggleMode(Cipher.DECRYPT_MODE);
}
}
@@ -304,7 +384,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
// errorCode = 5
// errString = "Fingerprint operation canceled."
//onFingerprintException();
//confirmationView.setText(errString);
//fingerprintTextView.setText(errString);
// true false fingerprint readings are handled otherwise with the toast messages, see below in code
}
@@ -313,8 +393,9 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
final int helpCode,
final CharSequence helpString) {
onFingerprintException(new Exception("onAuthenticationHelp"));
confirmationView.setText(helpString);
showError(helpString);
checkFingerprintAvailability();
fingerprintTextView.setText(helpString);
}
@Override
@@ -338,7 +419,8 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
@Override
public void onAuthenticationFailed() {
onFingerprintException(new Exception("onAuthenticationFailed"));
showError(R.string.fingerprint_not_recognized);
checkFingerprintAvailability();
}
});
}
@@ -353,16 +435,19 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
}
@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;
private synchronized int toggleMode(final int newMode) {
if(newMode != mode) {
mode = newMode;
switch (mode) {
case Cipher.ENCRYPT_MODE:
fingerPrintHelper.initEncryptData();
break;
case Cipher.DECRYPT_MODE:
final String ivSpecValue = prefsNoBackup.getString(getPreferenceKeyIvSpec(), null);
if (ivSpecValue != null)
fingerPrintHelper.initDecryptData(ivSpecValue);
break;
}
}
return newMode;
}
@@ -370,49 +455,88 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
@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) {
fingerprintView.setVisibility(vis);
confirmationView.setVisibility(vis);
private void setFingerPrintVisibility(final int vis) {
runOnUiThread(new Runnable() {
@Override
public void run() {
fingerprintContainerView.setVisibility(vis);
}
});
}
private void setFingerPrintTextView(final int textId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
fingerprintTextView.setText(textId);
}
});
}
private void setFingerPrintAlphaImageView(final float alpha) {
runOnUiThread(new Runnable() {
@Override
public void run() {
fingerprintImageView.setAlpha(alpha);
}
});
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void checkAvailability() {
private synchronized void checkFingerprintAvailability() {
// fingerprint not supported (by API level or hardware) so keep option hidden
if (!fingerPrintHelper.isFingerprintSupported(FingerprintManagerCompat.from(this))) {
// 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 if (!fingerPrintHelper.hasEnrolledFingerprints()) {
setFingerPrintVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
fingerprintView.setAlpha(0.3f);
}
// This happens when no fingerprints are registered. Listening won't start
confirmationView.setText(R.string.configure_fingerprint);
}
// finally fingerprint available and configured so we can use it
else {
fingerprintMustBeConfigured = false;
// 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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
fingerprintView.setAlpha(1f);
if (!fingerPrintHelper.hasEnrolledFingerprints()) {
setFingerPrintAlphaImageView(0.3f);
// This happens when no fingerprints are registered. Listening won't start
setFingerPrintTextView(R.string.configure_fingerprint);
}
// fingerprint available but no stored password found yet for this DB so show info don't listen
if (prefsNoBackup.getString(getPreferenceKeyValue(), null) == null) {
confirmationView.setText(R.string.no_password_stored);
}
// all is set here so we can confirm to user and start listening for fingerprints
// finally fingerprint available and configured so we can use it
else {
confirmationView.setText(R.string.scanning_fingerprint);
// listen for decryption by default
toggleMode(Cipher.DECRYPT_MODE);
fingerprintMustBeConfigured = false;
setFingerPrintAlphaImageView(1f);
// fingerprint available but no stored password found yet for this DB so show info don't listen
if (!prefsNoBackup.contains(getPreferenceKeyValue())) {
setFingerPrintTextView(R.string.no_password_stored);
// listen for encryption
toggleMode(Cipher.ENCRYPT_MODE);
}
// all is set here so we can confirm to user and start listening for fingerprints
else {
setFingerPrintTextView(R.string.scanning_fingerprint);
// listen for decryption
toggleMode(Cipher.DECRYPT_MODE);
}
}
}
}
@@ -421,36 +545,58 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
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
confirmButton.performClick();
confirmationView.setText(R.string.encrypted_value_stored);
confirmButtonView.performClick();
setFingerPrintTextView(R.string.encrypted_value_stored);
}
@Override
public void handleDecryptedResult(final String value) {
// on decrypt enter it for the purchase/login action
passwordView.setText(value);
confirmButton.performClick();
public void handleDecryptedResult(final String passwordValue) {
// Load database directly
String key = keyFileView.getText().toString();
loadDatabase(passwordValue, key);
}
@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();
public void onInvalidKeyException(Exception e) {
showError(R.string.fingerprint_invalid_key);
prefsNoBackup.edit()
.remove(getPreferenceKeyValue())
.remove(getPreferenceKeyIvSpec())
.apply();
e.printStackTrace();
checkFingerprintAvailability(); // restarts listening
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onFingerPrintException(Exception e) {
showError(R.string.fingerprint_error);
e.printStackTrace();
checkFingerprintAvailability();
}
private void showError(final int messageId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), messageId, Toast.LENGTH_SHORT).show();
}
});
}
private void showError(final CharSequence message) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
});
}
private class DefaultCheckChange implements CompoundButton.OnCheckedChangeListener {
@@ -482,12 +628,30 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
private class OkClickHandler implements View.OnClickListener {
public void onClick(View view) {
String pass = getEditText(R.id.password);
String key = getEditText(R.id.pass_keyfile);
loadDatabase(pass, key);
String pass = passwordView.getText().toString();
String key = keyFileView.getText().toString();
verifyCheckboxesAndLoadDatabase(pass, key);
}
}
private void verifyCheckboxesAndLoadDatabase(
String pass,
String keyfile) {
verifyCheckboxesAndLoadDatabase(pass, UriUtil.parseDefaultFile(keyfile));
}
private void verifyCheckboxesAndLoadDatabase(
String pass,
Uri keyfile) {
if (!checkboxPasswordView.isChecked()) {
pass = "";
}
if (!checkboxKeyfileView.isChecked()) {
keyfile = null;
}
loadDatabase(pass, keyfile);
}
private void loadDatabase(
String pass,
String keyfile) {
@@ -497,10 +661,6 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
private void loadDatabase(
String pass,
Uri keyfile) {
if (pass.length() == 0 && (keyfile == null || keyfile.toString().length() == 0)) {
errorMessage(R.string.error_nopass);
return;
}
// Clear before we load
Database db = App.getDB();
@@ -515,19 +675,6 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
pt.run();
}
private String getEditText(int resId) {
return Util.getEditText(this, resId);
}
private void setEditText(
int resId,
String str) {
TextView te = (TextView) findViewById(resId);
if (te != null) {
te.setText(str);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
@@ -554,7 +701,7 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
private Database db;
public AfterLoad(
AfterLoad(
Handler handler,
Database db) {
super(handler);
@@ -584,20 +731,30 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
}
}
private class InitTask extends AsyncTask<Intent, Void, Integer> {
private static class UriIntentInitTask extends AsyncTask<Intent, Void, Integer> {
String password = "";
boolean launch_immediately = false;
static final String KEY_FILENAME = "fileName";
static final String KEY_KEYFILE = "keyFile";
private static final String VIEW_INTENT = "android.intent.action.VIEW";
private UriIntentInitTaskCallback uriIntentInitTaskCallback;
private boolean isKeyFileNeeded;
private Uri databaseUri;
private Uri keyFileUri;
UriIntentInitTask(UriIntentInitTaskCallback uriIntentInitTaskCallback, boolean isKeyFileNeeded) {
this.uriIntentInitTaskCallback = uriIntentInitTaskCallback;
this.isKeyFileNeeded = isKeyFileNeeded;
}
@Override
protected Integer doInBackground(Intent... args) {
Intent i = args[0];
String action = i.getAction();
Intent intent = args[0];
String action = intent.getAction();
if (action != null && action.equals(VIEW_INTENT)) {
Uri incoming = i.getData();
mDbUri = incoming;
mKeyUri = ClipDataCompat.getUriFromIntent(i, KEY_KEYFILE);
Uri incoming = intent.getData();
databaseUri = incoming;
keyFileUri = ClipDataCompat.getUriFromIntent(intent, KEY_KEYFILE);
if (incoming == null) {
return R.string.error_can_not_handle_uri;
@@ -606,122 +763,47 @@ public class PasswordActivity extends LockingActivity implements FingerPrintHelp
if (fileName.length() == 0) {
// No file name
return R.string.FileNotFound;
return R.string.file_not_found;
}
File dbFile = new File(fileName);
if (!dbFile.exists()) {
// File does not exist
return R.string.FileNotFound;
return R.string.file_not_found;
}
if (mKeyUri == null) {
mKeyUri = getKeyFile(mDbUri);
if (keyFileUri == null) {
keyFileUri = getKeyFile(databaseUri);
}
} else if (incoming.getScheme().equals("content")) {
if (mKeyUri == null) {
mKeyUri = getKeyFile(mDbUri);
if (keyFileUri == null) {
keyFileUri = getKeyFile(databaseUri);
}
} 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);
databaseUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_FILENAME));
keyFileUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_KEYFILE));
if (mKeyUri == null || mKeyUri.toString().length() == 0) {
mKeyUri = getKeyFile(mDbUri);
if (keyFileUri == null || keyFileUri.toString().length() == 0) {
keyFileUri = getKeyFile(databaseUri);
}
}
return null;
}
public void onPostExecute(Integer result) {
if (result != null) {
Toast.makeText(PasswordActivity.this, result, Toast.LENGTH_LONG).show();
finish();
return;
}
uriIntentInitTaskCallback.onPostInitTask(databaseUri, keyFileUri, result);
}
populateView();
Button confirmButton = (Button) findViewById(R.id.pass_ok);
confirmButton.setOnClickListener(new OkClickHandler());
if (password != null) {
TextView tv_password = (TextView) findViewById(R.id.password);
tv_password.setText(password);
}
CheckBox defaultCheck = (CheckBox) findViewById(R.id.default_database);
defaultCheck.setOnCheckedChangeListener(new DefaultCheckChange());
View browse = findViewById(R.id.browse_button);
browse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (StorageAF.useStorageFramework(PasswordActivity.this)) {
Intent i = new Intent(StorageAF.ACTION_OPEN_DOCUMENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
startActivityForResult(i, OPEN_DOC);
} else {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
try {
startActivityForResult(i, GET_CONTENT);
} catch (ActivityNotFoundException e) {
lookForOpenIntentsFilePicker();
}
}
}
private void lookForOpenIntentsFilePicker() {
if (Interaction.isIntentAvailable(PasswordActivity.this, Intents.OPEN_INTENTS_FILE_BROWSE)) {
Intent i = new Intent(Intents.OPEN_INTENTS_FILE_BROWSE);
// Get file path parent if possible
try {
if (mDbUri != null && mDbUri.toString().length() > 0) {
if (mDbUri.getScheme().equals("file")) {
File keyfile = new File(mDbUri.getPath());
File parent = keyfile.getParentFile();
if (parent != null) {
i.setData(Uri.parse("file://" + parent.getAbsolutePath()));
}
}
}
} catch (Exception e) {
// Ignore
}
try {
startActivityForResult(i, FILE_BROWSE);
} catch (ActivityNotFoundException e) {
showBrowserDialog();
}
} else {
showBrowserDialog();
}
}
private void showBrowserDialog() {
BrowserDialog browserDialog = new BrowserDialog(PasswordActivity.this);
browserDialog.show();
}
});
retrieveSettings();
if (launch_immediately) {
loadDatabase(password, mKeyUri);
private Uri getKeyFile(Uri dbUri) {
if (isKeyFileNeeded) {
return App.getFileHistory().getFileByName(dbUri);
} else {
return null;
}
}
}

View File

@@ -1,156 +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.Dialog;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.kunzisoft.keepass.R;
import com.keepassdroid.app.App;
import com.keepassdroid.database.edit.FileOnFinish;
import com.keepassdroid.database.edit.OnFinish;
import com.keepassdroid.database.edit.SetPassword;
import com.keepassdroid.utils.EmptyUtils;
import com.keepassdroid.utils.UriUtil;
public class SetPasswordDialog extends DialogFragment {
private final static String FINISH_TAG = "FINISH_TAG";
private byte[] masterKey;
private Uri mKeyfile;
private FileOnFinish mFinish;
private View rootView;
public byte[] getKey() {
return masterKey;
}
public Uri keyfile() {
return mKeyfile;
}
public static SetPasswordDialog newInstance(FileOnFinish finish) {
SetPasswordDialog setPasswordDialog = new SetPasswordDialog();
Bundle args = new Bundle();
args.putSerializable(FINISH_TAG, finish);
setPasswordDialog.setArguments(args);
return setPasswordDialog;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
if(getArguments() != null && getArguments().containsKey(FINISH_TAG)) {
mFinish = (FileOnFinish) getArguments().getSerializable(FINISH_TAG);
}
rootView = inflater.inflate(R.layout.set_password, null);
builder.setView(rootView)
// Add action buttons
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
TextView passView = (TextView) rootView.findViewById(R.id.pass_password);
String pass = passView.getText().toString();
TextView passConfView = (TextView) rootView.findViewById(R.id.pass_conf_password);
String confpass = passConfView.getText().toString();
// Verify that passwords match
if ( ! pass.equals(confpass) ) {
// Passwords do not match
Toast.makeText(getContext(), R.string.error_pass_match, Toast.LENGTH_LONG).show();
return;
}
TextView keyfileView = (TextView) rootView.findViewById(R.id.pass_keyfile);
Uri keyfile = UriUtil.parseDefaultFile(keyfileView.getText().toString());
mKeyfile = keyfile;
// Verify that a password or keyfile is set
if ( pass.length() == 0 && EmptyUtils.isNullOrEmpty(keyfile)) {
Toast.makeText(getContext(), R.string.error_nopass, Toast.LENGTH_LONG).show();
return;
}
SetPassword sp = new SetPassword(getContext(), App.getDB(), pass, keyfile, new AfterSave(mFinish, new Handler()));
final ProgressTask pt = new ProgressTask(getContext(), sp, R.string.saving_database);
boolean valid = sp.validatePassword(getContext(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
pt.run();
}
});
if (valid) {
pt.run();
}
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
SetPasswordDialog.this.getDialog().cancel();
if ( mFinish != null ) {
mFinish.run();
}
}
});
return builder.create();
}
private class AfterSave extends OnFinish {
private FileOnFinish mFinish;
public AfterSave(FileOnFinish finish, Handler handler) {
super(finish, handler);
mFinish = finish;
}
@Override
public void run() {
if ( mSuccess ) {
if ( mFinish != null ) {
mFinish.setFilename(mKeyfile);
}
dismiss();
} else {
displayMessage(getContext());
}
super.run();
}
}
}

View File

@@ -0,0 +1,48 @@
package com.keepassdroid;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import com.kunzisoft.keepass.R;
public class UnavailableFeatureDialog extends DialogFragment {
private static final String MIN_REQUIRED_VERSION_ARG = "MIN_REQUIRED_VERSION_ARG";
private int minVersionRequired = Build.VERSION_CODES.M;
public static UnavailableFeatureDialog getInstance(int minVersionRequired) {
UnavailableFeatureDialog fragment = new UnavailableFeatureDialog();
Bundle args = new Bundle();
args.putInt(MIN_REQUIRED_VERSION_ARG, minVersionRequired);
fragment.setArguments(args);
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (getArguments() != null && getArguments().containsKey(MIN_REQUIRED_VERSION_ARG))
minVersionRequired = getArguments().getInt(MIN_REQUIRED_VERSION_ARG);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
String message = getString(R.string.unavailable_feature_text).concat("\n");
if(Build.VERSION.SDK_INT <= minVersionRequired)
message = message.concat(getString(R.string.unavailable_feature_version,
Build.VERSION.SDK_INT,
minVersionRequired));
else
message = message.concat(getString(R.string.unavailable_feature_hardware));
builder.setMessage(message)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { }
});
return builder.create();
}
}

View File

@@ -0,0 +1,7 @@
package com.keepassdroid;
import android.net.Uri;
interface UriIntentInitTaskCallback {
void onPostInitTask(Uri dbUri, Uri keyFileUri, Integer errorStringId);
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.compat;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.security.spec.AlgorithmParameterSpec;
public class KeyGenParameterSpecCompat {
private static Class builder;
private static Constructor buildConst;
private static Method builderBuild;
private static Method setBlockModes;
private static Method setUserAuthReq;
private static Method setEncPad;
private static boolean available;
static {
try {
builder = Class.forName("android.security.keystore.KeyGenParameterSpec$Builder");
buildConst = builder.getConstructor(String.class, int.class);
builderBuild = builder.getMethod("build", (Class [])null);
setBlockModes = builder.getMethod("setBlockModes", String[].class);
setUserAuthReq = builder.getMethod("setUserAuthenticationRequired", new Class []{boolean.class});
setEncPad = builder.getMethod("setEncryptionPaddings", String[].class);
available = true;
} catch (Exception e) {
available = false;
}
}
public static AlgorithmParameterSpec build(String keystoreAlias, int purpose, String blockMode,
boolean userAuthReq, String encPadding) {
if (!available) {
return null;
}
try {
Object inst = buildConst.newInstance(keystoreAlias, purpose);
inst = setBlockModes.invoke(inst, new Object[] {new String[] {blockMode}});
inst = setUserAuthReq.invoke(inst, userAuthReq);
inst = setEncPad.invoke(inst, new Object[] {new String[] {encPadding}});
return (AlgorithmParameterSpec) builderBuild.invoke(inst, null);
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.compat;
import android.app.KeyguardManager;
import java.lang.reflect.Method;
public class KeyguardManagerCompat {
private static Method isKeyguardSecure;
private static boolean available;
static {
try {
isKeyguardSecure = KeyguardManager.class.getMethod("isKeyguardSecure", (Class[]) null);
available = true;
} catch (Exception e) {
available = false;
}
}
public static boolean isKeyguardSecure(KeyguardManager inst) {
if (!available) {
return false;
}
try {
return (boolean) isKeyguardSecure.invoke(inst, null);
} catch (Exception e) {
return false;
}
}
}

View File

@@ -19,6 +19,7 @@
*/
package com.keepassdroid.database;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
@@ -49,6 +50,14 @@ public class BinaryPool {
public Set<Entry<Integer, ProtectedBinary>> entrySet() {
return pool.entrySet();
}
public void clear() {
pool.clear();
}
public Collection<ProtectedBinary> binaries() {
return pool.values();
}
private class AddBinaries extends EntryHandler<PwEntryV4> {
@@ -72,7 +81,7 @@ public class BinaryPool {
}
private void poolAdd(ProtectedBinary pb) {
public void poolAdd(ProtectedBinary pb) {
assert(pb != null);
if (poolFind(pb) != -1) return;

View File

@@ -19,12 +19,29 @@
*/
package com.keepassdroid.database;
import java.io.FileInputStream;
import android.webkit.URLUtil;
import com.keepassdroid.collections.VariantDictionary;
import com.keepassdroid.crypto.CryptoUtil;
import com.keepassdroid.crypto.engine.AesEngine;
import com.keepassdroid.crypto.engine.CipherEngine;
import com.keepassdroid.crypto.keyDerivation.AesKdf;
import com.keepassdroid.crypto.keyDerivation.KdfEngine;
import com.keepassdroid.crypto.keyDerivation.KdfFactory;
import com.keepassdroid.crypto.keyDerivation.KdfParameters;
import com.keepassdroid.database.exception.InvalidKeyFileException;
import com.keepassdroid.utils.EmptyUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.acl.Group;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -36,29 +53,8 @@ import java.util.UUID;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.spongycastle.crypto.engines.AESEngine;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import android.webkit.URLUtil;
import biz.source_code.base64Coder.Base64Coder;
import com.keepassdroid.collections.VariantDictionary;
import com.keepassdroid.crypto.CipherFactory;
import com.keepassdroid.crypto.CryptoUtil;
import com.keepassdroid.crypto.PwStreamCipherFactory;
import com.keepassdroid.crypto.engine.AesEngine;
import com.keepassdroid.crypto.engine.CipherEngine;
import com.keepassdroid.crypto.keyDerivation.AesKdf;
import com.keepassdroid.crypto.keyDerivation.KdfEngine;
import com.keepassdroid.crypto.keyDerivation.KdfFactory;
import com.keepassdroid.crypto.keyDerivation.KdfParameters;
import com.keepassdroid.database.exception.InvalidKeyFileException;
import com.keepassdroid.utils.EmptyUtils;
public class PwDatabaseV4 extends PwDatabase {
@@ -73,6 +69,7 @@ public class PwDatabaseV4 extends PwDatabase {
public UUID dataCipher = AesEngine.CIPHER_UUID;
public CipherEngine dataEngine = new AesEngine();
public PwCompressionAlgorithm compressionAlgorithm = PwCompressionAlgorithm.Gzip;
// TODO: Refactor me away to get directly from kdfParameters
public long numKeyEncRounds = 6000;
public Date nameChanged = DEFAULT_NOW;
public Date settingsChanged = DEFAULT_NOW;
@@ -103,7 +100,8 @@ public class PwDatabaseV4 extends PwDatabase {
public Map<String, String> customData = new HashMap<String, String>();
public KdfParameters kdfParameters = KdfFactory.getDefaultParameters();
public VariantDictionary publicCustomData = new VariantDictionary();
public BinaryPool binPool = new BinaryPool();
public String localizedAppName = "KeePassDroid";
public class MemoryProtectionConfig {
@@ -131,7 +129,7 @@ public class PwDatabaseV4 extends PwDatabase {
throws InvalidKeyFileException, IOException {
assert(key != null);
byte[] fKey;
byte[] fKey = new byte[]{};
if ( key.length() > 0 && keyInputStream != null) {
return getCompositeKey(key, keyInputStream);
@@ -139,8 +137,6 @@ public class PwDatabaseV4 extends PwDatabase {
fKey = getPasswordKey(key);
} else if ( keyInputStream != null) {
fKey = getFileKey(keyInputStream);
} else {
throw new IllegalArgumentException( "Key cannot be empty." );
}
MessageDigest md;
@@ -175,13 +171,24 @@ public class PwDatabaseV4 extends PwDatabase {
Arrays.fill(cmpKey, (byte)0);
}
}
public void makeFinalKey(byte[] masterSeed, KdfParameters kdfP) throws IOException {
makeFinalKey(masterSeed, kdfP, 0);
}
public void makeFinalKey(byte[] masterSeed, KdfParameters kdfP, long roundsFix)
throws IOException {
KdfEngine kdfEngine = KdfFactory.get(kdfP.kdfUUID);
if (kdfEngine == null) {
throw new IOException("Unknown key derivation function");
}
// Set to 6000 rounds to open corrupted database
if (roundsFix > 0 && kdfP.kdfUUID.equals(AesKdf.CIPHER_UUID)) {
kdfP.setUInt32(AesKdf.ParamRounds, roundsFix);
numKeyEncRounds = roundsFix;
}
byte[] transformedMasterKey = kdfEngine.transform(masterKey, kdfP);
if (transformedMasterKey.length != 32) {
transformedMasterKey = CryptoUtil.hashSha256(transformedMasterKey);

View File

@@ -23,16 +23,8 @@ import android.annotation.SuppressLint;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
@SuppressLint("SimpleDateFormat")
public class PwDatabaseV4XML {
public static final SimpleDateFormat dateFormat;
static {
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
public static final String ElemDocNode = "KeePassFile";
public static final String ElemMeta = "Meta";
public static final String ElemRoot = "Root";
@@ -134,4 +126,15 @@ public class PwDatabaseV4XML {
public static final String ElemCustomData = "CustomData";
public static final String ElemStringDictExItem = "Item";
public static final ThreadLocal<SimpleDateFormat> dateFormatter =
new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat dateFormat;
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat;
}
};
}

View File

@@ -95,7 +95,6 @@ public class PwDbHeaderV4 extends PwDbHeader {
public byte[] streamStartBytes = new byte[32];
public CrsAlgorithm innerRandomStream;
public long version;
public List<ProtectedBinary> binaries = new ArrayList<ProtectedBinary>();
public PwDbHeaderV4(PwDatabaseV4 d) {
db = d;
@@ -194,7 +193,9 @@ public class PwDbHeaderV4 extends PwDbHeader {
if (!db.kdfParameters.kdfUUID.equals(kdfR.uuid)) {
db.kdfParameters = kdfR.getDefaultParameters();
}
db.kdfParameters.setUInt64(AesKdf.ParamRounds, LEDataInputStream.readLong(fieldData, 0));
long rounds = LEDataInputStream.readLong(fieldData, 0);
db.kdfParameters.setUInt64(AesKdf.ParamRounds, rounds);
db.numKeyEncRounds = rounds;
break;
case PwDbHeaderV4Fields.EncryptionIV:

View File

@@ -19,20 +19,14 @@
*/
package com.keepassdroid.database.edit;
import android.content.Context;
import android.net.Uri;
import com.keepassdroid.Database;
import com.keepassdroid.app.App;
import com.keepassdroid.database.PwDatabase;
import com.keepassdroid.database.PwDatabaseV3;
import com.keepassdroid.database.PwEncryptionAlgorithm;
import com.keepassdroid.utils.UriUtil;
public class CreateDB extends RunnableOnFinish {
private final int DEFAULT_ENCRYPTION_ROUNDS = 300;
private String mFilename;
private boolean mDontSave;
@@ -57,7 +51,6 @@ public class CreateDB extends RunnableOnFinish {
// Set Database state
db.pm = pm;
Uri.Builder b = new Uri.Builder();
db.mUri = UriUtil.parseDefaultFile(mFilename);
db.setLoaded();
App.clearShutdown();

View File

@@ -78,7 +78,7 @@ public class LoadDB extends RunnableOnFinish {
finish(false, mCtx.getString(R.string.file_not_found_content));
return;
} catch (FileNotFoundException e) {
finish(false, mCtx.getString(R.string.FileNotFound));
finish(false, mCtx.getString(R.string.file_not_found));
return;
} catch (IOException e) {
finish(false, e.getMessage());

View File

@@ -19,9 +19,6 @@
*/
package com.keepassdroid.database.edit;
import java.io.IOException;
import java.io.InputStream;
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
@@ -32,6 +29,9 @@ import com.keepassdroid.database.exception.InvalidKeyFileException;
import com.keepassdroid.dialog.PasswordEncodingDialogHelper;
import com.keepassdroid.utils.UriUtil;
import java.io.IOException;
import java.io.InputStream;
public class SetPassword extends RunnableOnFinish {
private String mPassword;

View File

@@ -33,7 +33,7 @@ public abstract class Importer {
public abstract PwDatabase openDatabase( InputStream inStream, String password, InputStream keyInputStream)
throws IOException, InvalidDBException;
public abstract PwDatabase openDatabase( InputStream inStream, String password, InputStream keyInputStream, UpdateStatus status )
public abstract PwDatabase openDatabase( InputStream inStream, String password, InputStream keyInputStream, UpdateStatus status, long roundsFix)
throws IOException, InvalidDBException;

View File

@@ -125,10 +125,10 @@ public class ImporterV3 extends Importer {
public PwDatabaseV3 openDatabase( InputStream inStream, String password, InputStream kfIs)
throws IOException, InvalidDBException
{
return openDatabase(inStream, password, kfIs, new UpdateStatus());
return openDatabase(inStream, password, kfIs, new UpdateStatus(), 0);
}
public PwDatabaseV3 openDatabase( InputStream inStream, String password, InputStream kfIs, UpdateStatus status )
public PwDatabaseV3 openDatabase( InputStream inStream, String password, InputStream kfIs, UpdateStatus status, long roundsFix)
throws IOException, InvalidDBException
{
PwDatabaseV3 newManager;

View File

@@ -35,9 +35,10 @@ public class ImporterV3Debug extends ImporterV3 {
@Override
public PwDatabaseV3Debug openDatabase(InputStream inStream, String password,
InputStream keyInputStream, UpdateStatus status) throws IOException,
InputStream keyInputStream, UpdateStatus status, long roundsFix) throws IOException,
InvalidDBException {
return (PwDatabaseV3Debug) super.openDatabase(inStream, password, keyInputStream, status);
return (PwDatabaseV3Debug) super.openDatabase(inStream, password, keyInputStream, status,
roundsFix);
}

View File

@@ -78,11 +78,11 @@ public class ImporterV4 extends Importer {
private StreamCipher randomStream;
private PwDatabaseV4 db;
private BinaryPool binPool = new BinaryPool();
private byte[] hashOfHeader = null;
private byte[] pbHeader = null;
private long version;
private int binNum = 0;
Calendar utcCal;
public ImporterV4() {
@@ -98,18 +98,17 @@ public class ImporterV4 extends Importer {
public PwDatabaseV4 openDatabase(InputStream inStream, String password,
InputStream keyInputStream) throws IOException, InvalidDBException {
return openDatabase(inStream, password, keyInputStream, new UpdateStatus());
return openDatabase(inStream, password, keyInputStream, new UpdateStatus(), 0);
}
@Override
public PwDatabaseV4 openDatabase(InputStream inStream, String password,
InputStream keyInputStream, UpdateStatus status) throws IOException,
InvalidDBException {
public PwDatabaseV4 openDatabase(InputStream inStream, String password,
InputStream keyInputStream, UpdateStatus status, long roundsFix) throws IOException,
InvalidDBException {
db = createDB();
PwDbHeaderV4 header = new PwDbHeaderV4(db);
header.binaries.clear();
db.binPool.clear();
PwDbHeaderV4.HeaderAndHash hh = header.loadFromFile(inStream);
version = header.version;
@@ -118,7 +117,7 @@ public class ImporterV4 extends Importer {
pbHeader = hh.header;
db.setMasterKey(password, keyInputStream);
db.makeFinalKey(header.masterSeed, db.kdfParameters);
db.makeFinalKey(header.masterSeed, db.kdfParameters, roundsFix);
CipherEngine engine;
Cipher cipher;
@@ -253,6 +252,7 @@ public class ImporterV4 extends Importer {
byte[] bin = new byte[data.length - 1];
System.arraycopy(data, 1, bin, 0, data.length-1);
ProtectedBinary pb = new ProtectedBinary(prot, bin);
db.binPool.poolAdd(pb);
if (prot) {
Arrays.fill(data, (byte)0);
@@ -511,7 +511,7 @@ public class ImporterV4 extends Importer {
if ( key != null ) {
ProtectedBinary pbData = ReadProtectedBinary(xpp);
int id = Integer.parseInt(key);
binPool.put(id, pbData);
db.binPool.put(id, pbData);
} else {
ReadUnknown(xpp);
}
@@ -922,7 +922,7 @@ public class ImporterV4 extends Importer {
byte[] buf = Base64Coder.decode(sDate);
if (buf.length != 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;
}
@@ -932,7 +932,7 @@ public class ImporterV4 extends Importer {
} else {
try {
utcDate = PwDatabaseV4XML.dateFormat.parse(sDate);
utcDate = PwDatabaseV4XML.dateFormatter.get().parse(sDate);
} catch (ParseException e) {
// Catch with null test below
}
@@ -1061,7 +1061,7 @@ public class ImporterV4 extends Importer {
xpp.next(); // Consume end tag
int id = Integer.parseInt(ref);
return binPool.get(id);
return db.binPool.get(id);
}
boolean compressed = false;

View File

@@ -35,9 +35,10 @@ public class ImporterV4Debug extends ImporterV4 {
@Override
public PwDatabaseV4Debug openDatabase(InputStream inStream, String password,
InputStream keyInputFile, UpdateStatus status) throws IOException,
InputStream keyInputFile, UpdateStatus status, long roundsFix) throws IOException,
InvalidDBException {
return (PwDatabaseV4Debug) super.openDatabase(inStream, password, keyInputFile, status);
return (PwDatabaseV4Debug) super.openDatabase(inStream, password, keyInputFile, status,
roundsFix);
}
}

View File

@@ -52,7 +52,7 @@ public class PwDbInnerHeaderOutputV4 {
los.writeInt(streamKeySize);
los.write(header.innerRandomStreamKey);
for (ProtectedBinary bin : header.binaries) {
for (ProtectedBinary bin : db.binPool.binaries()) {
byte flag = KdbxBinaryFlags.None;
if (bin.isProtected()) {
flag |= KdbxBinaryFlags.Protected;

View File

@@ -19,36 +19,13 @@
*/
package com.keepassdroid.database.save;
import static com.keepassdroid.database.PwDatabaseV4XML.*;
import java.io.IOException;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import java.util.UUID;
import java.util.zip.GZIPOutputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import org.joda.time.DateTime;
import org.spongycastle.crypto.StreamCipher;
import org.xmlpull.v1.XmlSerializer;
import android.util.Xml;
import biz.source_code.base64Coder.Base64Coder;
import com.keepassdroid.crypto.CipherFactory;
import com.keepassdroid.crypto.PwStreamCipherFactory;
import com.keepassdroid.crypto.engine.CipherEngine;
import com.keepassdroid.crypto.keyDerivation.KdfEngine;
import com.keepassdroid.crypto.keyDerivation.KdfFactory;
import com.keepassdroid.database.BinaryPool;
import com.keepassdroid.database.CrsAlgorithm;
import com.keepassdroid.database.EntryHandler;
import com.keepassdroid.database.GroupHandler;
@@ -78,11 +55,115 @@ import com.keepassdroid.utils.EmptyUtils;
import com.keepassdroid.utils.MemUtil;
import com.keepassdroid.utils.Types;
import org.joda.time.DateTime;
import org.spongycastle.crypto.StreamCipher;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import java.util.UUID;
import java.util.zip.GZIPOutputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import biz.source_code.base64Coder.Base64Coder;
import static com.keepassdroid.database.PwDatabaseV4XML.AttrCompressed;
import static com.keepassdroid.database.PwDatabaseV4XML.AttrId;
import static com.keepassdroid.database.PwDatabaseV4XML.AttrProtected;
import static com.keepassdroid.database.PwDatabaseV4XML.AttrRef;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoType;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoTypeDefaultSeq;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoTypeEnabled;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoTypeItem;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemAutoTypeObfuscation;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemBgColor;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemBinaries;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemBinary;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCreationTime;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomData;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIconID;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIconItem;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIconItemData;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIconItemID;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemCustomIcons;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbColor;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbDefaultUser;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbDefaultUserChanged;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbDesc;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbDescChanged;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbKeyChangeForce;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbKeyChangeRec;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbKeyChanged;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbMntncHistoryDays;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbName;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDbNameChanged;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDeletedObject;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDeletedObjects;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDeletionTime;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemDocNode;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEnableAutoType;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEnableSearching;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEntry;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEntryTemplatesGroup;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemEntryTemplatesGroupChanged;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemExpires;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemExpiryTime;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemFgColor;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemGenerator;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemGroup;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemGroupDefaultAutoTypeSeq;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemHeaderHash;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemHistory;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemHistoryMaxItems;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemHistoryMaxSize;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemIcon;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemIsExpanded;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemKey;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemKeystrokeSequence;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastAccessTime;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastModTime;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastSelectedGroup;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastTopVisibleEntry;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLastTopVisibleGroup;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemLocationChanged;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemMemoryProt;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemMeta;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemName;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemNotes;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemOverrideUrl;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtNotes;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtPassword;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtTitle;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtURL;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemProtUserName;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemRecycleBinChanged;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemRecycleBinEnabled;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemRecycleBinUuid;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemRoot;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemString;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemStringDictExItem;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemTags;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemTimes;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemUsageCount;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemUuid;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemValue;
import static com.keepassdroid.database.PwDatabaseV4XML.ElemWindow;
import static com.keepassdroid.database.PwDatabaseV4XML.ValFalse;
import static com.keepassdroid.database.PwDatabaseV4XML.ValTrue;
public class PwDbV4Output extends PwDbOutput {
PwDatabaseV4 mPM;
private StreamCipher randomStream;
private BinaryPool binPool;
private XmlSerializer xml;
private PwDbHeaderV4 header;
private byte[] hashOfHeader;
@@ -201,8 +282,7 @@ public class PwDbV4Output extends PwDbOutput {
}
private void outputDatabase(OutputStream os) throws IllegalArgumentException, IllegalStateException, IOException {
binPool = new BinaryPool((PwGroupV4)mPM.rootGroup);
xml = Xml.newSerializer();
xml.setOutput(os, "UTF-8");
@@ -420,7 +500,7 @@ public class PwDbV4Output extends PwDbOutput {
xml.startTag(null, ElemValue);
String strRef = null;
if (allowRef) {
int ref = binPool.poolFind(value);
int ref = mPM.binPool.poolFind(value);
strRef = Integer.toString(ref);
}
@@ -480,7 +560,7 @@ public class PwDbV4Output extends PwDbOutput {
private void writeObject(String name, Date value) throws IllegalArgumentException, IllegalStateException, IOException {
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
writeObject(name, PwDatabaseV4XML.dateFormat.format(value));
writeObject(name, PwDatabaseV4XML.dateFormatter.get().format(value));
} else {
DateTime dt = new DateTime(value);
long seconds = DateUtil.convertDateToKDBX4Time(dt);
@@ -720,7 +800,7 @@ public class PwDbV4Output extends PwDbOutput {
private void writeBinPool() throws IllegalArgumentException, IllegalStateException, IOException {
xml.startTag(null, ElemBinaries);
for (Entry<Integer, ProtectedBinary> pair : binPool.entrySet()) {
for (Entry<Integer, ProtectedBinary> pair : mPM.binPool.entrySet()) {
xml.startTag(null, ElemBinary);
xml.attribute(null, AttrId, Integer.toString(pair.getKey()));

View File

@@ -0,0 +1,52 @@
/*
* 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.fileselect;
import android.os.AsyncTask;
class DeleteFileHistoryAsyncTask extends AsyncTask<FileSelectBean, Void, Void> {
private AfterDeleteFileHistoryListener afterDeleteFileHistoryListener;
private RecentFileHistory fileHistory;
private FileSelectAdapter adapter;
DeleteFileHistoryAsyncTask(AfterDeleteFileHistoryListener afterDeleteFileHistoryListener, RecentFileHistory fileHistory, FileSelectAdapter adapter) {
this.afterDeleteFileHistoryListener = afterDeleteFileHistoryListener;
this.fileHistory = fileHistory;
this.adapter = adapter;
}
protected Void doInBackground(FileSelectBean... args) {
fileHistory.deleteFile(args[0].getFileUri());
return null;
}
protected void onPostExecute(Void v) {
adapter.notifyDataSetChanged();
if (adapter.getItemCount() == 0) {
if(afterDeleteFileHistoryListener != null)
afterDeleteFileHistoryListener.afterDeleteFile();
}
}
public interface AfterDeleteFileHistoryListener {
void afterDeleteFile();
}
}

View File

@@ -0,0 +1,90 @@
/*
* 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.fileselect;
import android.app.Dialog;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import com.kunzisoft.keepass.R;
import java.text.DateFormat;
public class FileInformationDialogFragment extends DialogFragment {
private static final String FILE_SELECT_BEEN_ARG = "FILE_SELECT_BEEN_ARG";
public static FileInformationDialogFragment newInstance(FileSelectBean fileSelectBean) {
FileInformationDialogFragment fileInformationDialogFragment =
new FileInformationDialogFragment();
Bundle args = new Bundle();
args.putSerializable(FILE_SELECT_BEEN_ARG, fileSelectBean);
fileInformationDialogFragment.setArguments(args);
return fileInformationDialogFragment;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View root = inflater.inflate(R.layout.file_selection_information, null);
if (getArguments() != null && getArguments().containsKey(FILE_SELECT_BEEN_ARG)) {
FileSelectBean fileSelectBean = (FileSelectBean) getArguments().getSerializable(FILE_SELECT_BEEN_ARG);
TextView fileWarningView = (TextView) root.findViewById(R.id.file_warning);
if(fileSelectBean != null) {
TextView fileNameView = (TextView) root.findViewById(R.id.file_filename);
TextView filePathView = (TextView) root.findViewById(R.id.file_path);
TextView fileSizeView = (TextView) root.findViewById(R.id.file_size);
TextView fileModificationView = (TextView) root.findViewById(R.id.file_modification);
fileWarningView.setVisibility(View.GONE);
fileNameView.setText(fileSelectBean.getFileName());
filePathView.setText(Uri.decode(fileSelectBean.getFileUri().toString()));
fileSizeView.setText(String.valueOf(fileSelectBean.getSize()));
fileModificationView.setText(DateFormat.getDateTimeInstance()
.format(fileSelectBean.getLastModification()));
if(fileSelectBean.notFound())
showFileNotFound(fileWarningView);
} else
showFileNotFound(fileWarningView);
}
builder.setView(root);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
return builder.create();
}
private void showFileNotFound(TextView fileWarningView) {
fileWarningView.setVisibility(View.VISIBLE);
fileWarningView.setText(R.string.file_not_found);
}
}

View File

@@ -19,7 +19,6 @@
*/
package com.keepassdroid.fileselect;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
@@ -27,35 +26,26 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.kunzisoft.keepass.R;
import com.keepassdroid.AssignMasterKeyDialog;
import com.keepassdroid.CreateFileDialog;
import com.keepassdroid.GroupActivity;
import com.keepassdroid.PasswordActivity;
import com.keepassdroid.ProgressTask;
import com.keepassdroid.SetPasswordDialog;
import com.keepassdroid.app.App;
import com.keepassdroid.compat.ContentResolverCompat;
import com.keepassdroid.compat.StorageAF;
@@ -69,20 +59,27 @@ import com.keepassdroid.utils.Interaction;
import com.keepassdroid.utils.MenuUtil;
import com.keepassdroid.utils.UriUtil;
import com.keepassdroid.utils.Util;
import com.keepassdroid.view.AssignPasswordHelper;
import com.keepassdroid.view.FileNameView;
import com.kunzisoft.keepass.R;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URLDecoder;
public class FileSelectActivity extends StylishActivity {
public class FileSelectActivity extends StylishActivity implements
CreateFileDialog.DefinePathDialogListener ,
AssignMasterKeyDialog.AssignPasswordDialogListener,
FileSelectAdapter.FileSelectClearListener,
FileSelectAdapter.FileInformationShowListener {
private static final String TAG = "FileSelectActivity";
private static final int MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE = 111;
private ListView mList;
private ListAdapter mAdapter;
private static final int CMENU_CLEAR = Menu.FIRST;
private RecyclerView mListFiles;
private FileSelectAdapter mAdapter;
private View fileListTitle;
public static final int FILE_BROWSE = 1;
public static final int GET_CONTENT = 2;
@@ -92,42 +89,37 @@ public class FileSelectActivity extends StylishActivity {
private boolean recentMode = false;
private EditText openFileNameView;
private AssignPasswordHelper assignPasswordHelper;
private Uri databaseUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fileHistory = App.getFileHistory();
setContentView(R.layout.file_selection);
fileListTitle = findViewById(R.id.file_list_title);
if (fileHistory.hasRecentFiles()) {
recentMode = true;
setContentView(R.layout.file_selection);
} else {
setContentView(R.layout.file_selection_no_recent);
}
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(getString(R.string.app_name));
setSupportActionBar(toolbar);
mList = (ListView)findViewById(R.id.file_list);
mList.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
{
onListItemClick((ListView)parent, v, position, id);
}
}
);
mListFiles = (RecyclerView) findViewById(R.id.file_list);
mListFiles.setLayoutManager(new LinearLayoutManager(this));
// Open button
Button openButton = (Button) findViewById(R.id.open);
View openButton = findViewById(R.id.open_database);
openButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String fileName = Util.getEditText(FileSelectActivity.this,
R.id.file_filename);
try {
PasswordActivity.Launch(FileSelectActivity.this, fileName);
}
@@ -137,82 +129,18 @@ public class FileSelectActivity extends StylishActivity {
}
catch (FileNotFoundException e) {
Toast.makeText(FileSelectActivity.this,
R.string.FileNotFound, Toast.LENGTH_LONG).show();
R.string.file_not_found, Toast.LENGTH_LONG).show();
}
}
});
// Create button
Button createButton = (Button) findViewById(R.id.create);
View createButton = findViewById(R.id.create_database);
createButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String filename = Util.getEditText(FileSelectActivity.this,
R.id.file_filename);
// Make sure file name exists
if (filename.length() == 0) {
Toast
.makeText(FileSelectActivity.this,
R.string.error_filename_required,
Toast.LENGTH_LONG).show();
return;
}
// Try to create the file
File file = new File(filename);
try {
if (file.exists()) {
Toast.makeText(FileSelectActivity.this,
R.string.error_database_exists,
Toast.LENGTH_LONG).show();
return;
}
File parent = file.getParentFile();
if ( parent == null || (parent.exists() && ! parent.isDirectory()) ) {
Toast.makeText(FileSelectActivity.this,
R.string.error_invalid_path,
Toast.LENGTH_LONG).show();
return;
}
if ( ! parent.exists() ) {
// Create parent dircetory
if ( ! parent.mkdirs() ) {
Toast.makeText(FileSelectActivity.this,
R.string.error_could_not_create_parent,
Toast.LENGTH_LONG).show();
return;
}
}
file.createNewFile();
} catch (IOException e) {
Toast.makeText(
FileSelectActivity.this,
getText(R.string.error_file_not_create) + " "
+ e.getLocalizedMessage(),
Toast.LENGTH_LONG).show();
return;
}
// Prep an object to collect a password once the database has
// been created
CollectPassword password = new CollectPassword(
new LaunchGroupActivity(filename));
// Create the new database
CreateDB create = new CreateDB(FileSelectActivity.this, filename, password, true);
ProgressTask createTask = new ProgressTask(
FileSelectActivity.this, create,
R.string.progress_create);
createTask.run();
CreateFileDialog createFileDialog = new CreateFileDialog();
createFileDialog.show(getSupportFragmentManager(), "createFileDialog");
}
});
View browseButton = findViewById(R.id.browse_button);
@@ -223,16 +151,14 @@ public class FileSelectActivity extends StylishActivity {
Intent i = new Intent(StorageAF.ACTION_OPEN_DOCUMENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION|
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
startActivityForResult(i, OPEN_DOC);
}
else {
Intent i;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
i = new Intent(Intent.ACTION_OPEN_DOCUMENT);
} else {
i = new Intent(Intent.ACTION_GET_CONTENT);
}
i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
@@ -247,7 +173,6 @@ public class FileSelectActivity extends StylishActivity {
}
private void lookForOpenIntentsFilePicker() {
if (Interaction.isIntentAvailable(FileSelectActivity.this, Intents.OPEN_INTENTS_FILE_BROWSE)) {
Intent i = new Intent(Intents.OPEN_INTENTS_FILE_BROWSE);
i.setData(Uri.parse("file://" + Util.getEditText(FileSelectActivity.this, R.id.file_filename)));
@@ -256,7 +181,6 @@ public class FileSelectActivity extends StylishActivity {
} catch (ActivityNotFoundException e) {
showBrowserDialog();
}
} else {
showBrowserDialog();
}
@@ -268,17 +192,25 @@ public class FileSelectActivity extends StylishActivity {
}
});
// Set the initial value of the filename
openFileNameView = (EditText) findViewById(R.id.file_filename);
String defaultPath = Environment.getExternalStorageDirectory().getAbsolutePath()
+ getString(R.string.database_file_path_default)
+ getString(R.string.database_file_name_default)
+ getString(R.string.database_file_extension_default);
openFileNameView.setText(defaultPath);
fillData();
registerForContextMenu(mList);
// Load default database
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, "");
if (fileName.length() > 0) {
Uri dbUri = UriUtil.parseDefaultFile(fileName);
String scheme = dbUri.getScheme();
String scheme = null;
if (dbUri!=null)
scheme = dbUri.getScheme();
if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
String path = dbUri.getPath();
@@ -302,12 +234,142 @@ public class FileSelectActivity extends StylishActivity {
}
}
private void updateTitleFileListView() {
if(mAdapter.getItemCount() == 0)
fileListTitle.setVisibility(View.INVISIBLE);
else
fileListTitle.setVisibility(View.VISIBLE);
}
/**
* Create file for database
* @return If not created, return false
*/
private boolean createDatabaseFile(Uri path) {
String pathString = URLDecoder.decode(path.getPath());
// Make sure file name exists
if (pathString.length() == 0) {
Log.e(TAG, getString(R.string.error_filename_required));
Toast.makeText(FileSelectActivity.this,
R.string.error_filename_required,
Toast.LENGTH_LONG).show();
return false;
}
// Try to create the file
File file = new File(pathString);
try {
if (file.exists()) {
Log.e(TAG, getString(R.string.error_database_exists) + " " + file);
Toast.makeText(FileSelectActivity.this,
R.string.error_database_exists,
Toast.LENGTH_LONG).show();
return false;
}
File parent = file.getParentFile();
if ( parent == null || (parent.exists() && ! parent.isDirectory()) ) {
Log.e(TAG, getString(R.string.error_invalid_path) + " " + file);
Toast.makeText(FileSelectActivity.this,
R.string.error_invalid_path,
Toast.LENGTH_LONG).show();
return false;
}
if ( ! parent.exists() ) {
// Create parent directory
if ( ! parent.mkdirs() ) {
Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + parent);
Toast.makeText(FileSelectActivity.this,
R.string.error_could_not_create_parent,
Toast.LENGTH_LONG).show();
return false;
}
}
return file.createNewFile();
} catch (IOException e) {
Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + e.getLocalizedMessage());
e.printStackTrace();
Toast.makeText(
FileSelectActivity.this,
getText(R.string.error_file_not_create) + " "
+ e.getLocalizedMessage(),
Toast.LENGTH_LONG).show();
return false;
}
}
@Override
public boolean onDefinePathDialogPositiveClick(Uri pathFile) {
databaseUri = pathFile;
if(createDatabaseFile(pathFile)) {
AssignMasterKeyDialog assignMasterKeyDialog = new AssignMasterKeyDialog();
assignMasterKeyDialog.show(getSupportFragmentManager(), "passwordDialog");
return true;
} else
return false;
}
@Override
public boolean onDefinePathDialogNegativeClick(Uri pathFile) {
return true;
}
@Override
public void onAssignKeyDialogPositiveClick(
boolean masterPasswordChecked, String masterPassword,
boolean keyFileChecked, Uri keyFile) {
String databaseFilename = databaseUri.getPath();
// Prep an object to collect a password once the database has
// been created
FileOnFinish launchActivityOnFinish = new FileOnFinish(
new LaunchGroupActivity(databaseFilename));
AssignPasswordOnFinish assignPasswordOnFinish =
new AssignPasswordOnFinish(launchActivityOnFinish);
// Create the new database
CreateDB create = new CreateDB(FileSelectActivity.this,
databaseFilename, assignPasswordOnFinish, true);
ProgressTask createTask = new ProgressTask(
FileSelectActivity.this, create,
R.string.progress_create);
createTask.run();
assignPasswordHelper =
new AssignPasswordHelper(this,
masterPassword, keyFile);
}
@Override
public void onAssignKeyDialogNegativeClick(
boolean masterPasswordChecked, String masterPassword,
boolean keyFileChecked, Uri keyFile) {
}
private class AssignPasswordOnFinish extends FileOnFinish {
AssignPasswordOnFinish(FileOnFinish fileOnFinish) {
super(fileOnFinish);
}
@Override
public void run() {
if (mSuccess) {
assignPasswordHelper.assignPasswordInDatabase(mOnFinish);
}
}
}
private class LaunchGroupActivity extends FileOnFinish {
private Uri mUri;
public LaunchGroupActivity(String filename) {
LaunchGroupActivity(String filename) {
super(null);
mUri = UriUtil.parseDefaultFile(filename);
}
@@ -316,62 +378,66 @@ public class FileSelectActivity extends StylishActivity {
if (mSuccess) {
// Add to recent files
fileHistory.createFile(mUri, getFilename());
mAdapter.notifyDataSetChanged();
updateTitleFileListView();
GroupActivity.Launch(FileSelectActivity.this);
}
}
}
private class CollectPassword extends FileOnFinish {
public CollectPassword(FileOnFinish finish) {
super(finish);
}
@Override
public void run() {
SetPasswordDialog dialog = SetPasswordDialog.newInstance(mOnFinish);
dialog.show(getSupportFragmentManager(), "passwordDialog");
}
}
private void fillData() {
// Set the initial value of the filename
EditText filename = (EditText) findViewById(R.id.file_filename);
filename.setText(Environment.getExternalStorageDirectory().getAbsolutePath() + getString(R.string.default_file_path));
mAdapter = new ArrayAdapter<String>(this, R.layout.file_row, R.id.file_filename, fileHistory.getDbList());
mList.setAdapter(mAdapter);
mAdapter = new FileSelectAdapter(FileSelectActivity.this, fileHistory.getDbList());
mAdapter.setOnItemClickListener(
new View.OnClickListener() {
public void onClick(View view) {
int itemPosition = mListFiles.getChildLayoutPosition(view);
new OpenFileHistoryAsyncTask(new OpenFileHistoryAsyncTask.AfterOpenFileHistoryListener() {
@Override
public void afterOpenFile(String fileName, String keyFile) {
try {
PasswordActivity.Launch(FileSelectActivity.this,
fileName, keyFile);
} catch (ContentFileNotFoundException e) {
Toast.makeText(FileSelectActivity.this,
R.string.file_not_found_content, Toast.LENGTH_LONG)
.show();
} catch (FileNotFoundException e) {
Toast.makeText(FileSelectActivity.this,
R.string.file_not_found, Toast.LENGTH_LONG)
.show();
}
updateTitleFileListView();
}
}, fileHistory).execute(itemPosition);
}
}
);
mAdapter.setFileSelectClearListener(this);
mAdapter.setFileInformationShowListener(this);
mListFiles.setAdapter(mAdapter);
}
protected void onListItemClick(ListView l, View v, int position, long id) {
@Override
public void onClickFileInformation(FileSelectBean fileSelectBean) {
if (fileSelectBean != null) {
FileInformationDialogFragment fileInformationDialogFragment =
FileInformationDialogFragment.newInstance(fileSelectBean);
fileInformationDialogFragment.show(getSupportFragmentManager(), "fileInformation");
}
}
new AsyncTask<Integer, Void, Void>() {
String fileName;
String keyFile;
protected Void doInBackground(Integer... args) {
int position = args[0];
fileName = fileHistory.getDatabaseAt(position);
keyFile = fileHistory.getKeyfileAt(position);
return null;
}
protected void onPostExecute(Void v) {
try {
PasswordActivity.Launch(FileSelectActivity.this, fileName, keyFile);
}
catch (ContentFileNotFoundException e) {
Toast.makeText(FileSelectActivity.this, R.string.file_not_found_content, Toast.LENGTH_LONG)
.show();
}
catch (FileNotFoundException e) {
Toast.makeText(FileSelectActivity.this, R.string.FileNotFound, Toast.LENGTH_LONG)
.show();
}
}
}.execute(position);
}
@Override
public boolean onFileSelectClearListener(final FileSelectBean fileSelectBean) {
new DeleteFileHistoryAsyncTask(new DeleteFileHistoryAsyncTask.AfterDeleteFileHistoryListener() {
@Override
public void afterDeleteFile() {
fileHistory.deleteFile(fileSelectBean.getFileUri());
mAdapter.notifyDataSetChanged();
updateTitleFileListView();
}
}, fileHistory, mAdapter).execute(fileSelectBean);
return true;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
@@ -414,8 +480,7 @@ public class FileSelectActivity extends StylishActivity {
}
if (filename != null) {
EditText fn = (EditText) findViewById(R.id.file_filename);
fn.setText(filename);
openFileNameView.setText(filename);
}
}
@@ -436,6 +501,8 @@ public class FileSelectActivity extends StylishActivity {
FileNameView fnv = (FileNameView) findViewById(R.id.file_select);
fnv.updateExternalStorageWarning();
updateTitleFileListView();
}
private void checkStoragePermission() {
@@ -505,42 +572,4 @@ public class FileSelectActivity extends StylishActivity {
&& super.onOptionsItemSelected(item);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, CMENU_CLEAR, 0, R.string.remove_from_filelist);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
super.onContextItemSelected(item);
if ( item.getItemId() == CMENU_CLEAR ) {
AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) item.getMenuInfo();
TextView tv = (TextView) acmi.targetView;
String filename = tv.getText().toString();
new AsyncTask<String, Void, Void>() {
protected java.lang.Void doInBackground(String... args) {
String filename = args[0];
fileHistory.deleteFile(Uri.parse(args[0]));
return null;
}
protected void onPostExecute(Void v) {
refreshList();
}
}.execute(filename);
return true;
}
return false;
}
private void refreshList() {
((BaseAdapter) mAdapter).notifyDataSetChanged();
}
}

View File

@@ -0,0 +1,160 @@
/*
* 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.fileselect;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.support.annotation.ColorInt;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
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.settings.PrefsUtil;
import com.kunzisoft.keepass.R;
import java.util.List;
public class FileSelectAdapter extends RecyclerView.Adapter<FileSelectViewHolder> {
private static final int MENU_CLEAR = 1;
private Context context;
private LayoutInflater inflater;
private List<String> listFiles;
private View.OnClickListener mOnClickListener;
private FileSelectClearListener fileSelectClearListener;
private FileInformationShowListener fileInformationShowListener;
private @ColorInt
int warningColor;
FileSelectAdapter(Context context, List<String> listFiles) {
inflater = LayoutInflater.from(context);
this.context = context;
this.listFiles = listFiles;
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
theme.resolveAttribute(R.attr.colorAccentCompat, typedValue, true);
warningColor = typedValue.data;
}
@Override
public FileSelectViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.file_row, parent, false);
view.setOnClickListener(mOnClickListener);
return new FileSelectViewHolder(view);
}
@Override
public void onBindViewHolder(FileSelectViewHolder holder, int position) {
FileSelectBean fileSelectBean = new FileSelectBean(context, listFiles.get(position));
holder.fileContainer.setOnCreateContextMenuListener(new ContextMenuBuilder(fileSelectBean));
if (PrefsUtil.isFullFilePathEnable(context))
holder.fileName.setText(Uri.decode(fileSelectBean.getFileUri().toString()));
else
holder.fileName.setText(fileSelectBean.getFileName());
holder.fileName.setTextSize(PrefsUtil.getListTextSize(context));
if(fileSelectBean.notFound()) {
holder.fileInformation.setColorFilter(
warningColor,
android.graphics.PorterDuff.Mode.MULTIPLY);
}
if (fileInformationShowListener != null)
holder.fileInformation.setOnClickListener(new FileInformationClickListener(fileSelectBean));
}
@Override
public int getItemCount() {
return listFiles.size();
}
void setOnItemClickListener(View.OnClickListener onItemClickListener) {
this.mOnClickListener = onItemClickListener;
}
void setFileSelectClearListener(FileSelectClearListener fileSelectClearListener) {
this.fileSelectClearListener = fileSelectClearListener;
}
void setFileInformationShowListener(FileInformationShowListener fileInformationShowListener) {
this.fileInformationShowListener = fileInformationShowListener;
}
public interface FileInformationShowListener {
void onClickFileInformation(FileSelectBean fileSelectBean);
}
public interface FileSelectClearListener {
boolean onFileSelectClearListener(FileSelectBean fileSelectBean);
}
private class FileInformationClickListener implements View.OnClickListener {
private FileSelectBean fileSelectBean;
FileInformationClickListener(FileSelectBean fileSelectBean) {
this.fileSelectBean = fileSelectBean;
}
@Override
public void onClick(View view) {
fileInformationShowListener.onClickFileInformation(fileSelectBean);
}
}
private class ContextMenuBuilder implements View.OnCreateContextMenuListener {
private FileSelectBean fileSelectBean;
public ContextMenuBuilder(FileSelectBean fileSelectBean) {
this.fileSelectBean = fileSelectBean;
}
@Override
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
MenuItem clearMenu = contextMenu.add(Menu.NONE, MENU_CLEAR, Menu.NONE, R.string.remove_from_filelist);
clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener);
}
private MenuItem.OnMenuItemClickListener mOnMyActionClickListener = new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == MENU_CLEAR) {
return fileSelectClearListener.onFileSelectClearListener(fileSelectBean);
}
return false;
}
};
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.fileselect;
import android.content.Context;
import android.net.Uri;
import android.support.v4.provider.DocumentFile;
import java.io.File;
import java.io.Serializable;
import java.util.Date;
public class FileSelectBean implements Serializable {
private static final String EXTERNAL_STORAGE_AUTHORITY = "com.android.externalstorage.documents";
private String fileName = "";
private Uri fileUri;
private Date lastModification = new Date();
private long size = 0;
public FileSelectBean(Context context, String pathFile) {
fileUri = Uri.parse(pathFile);
if (EXTERNAL_STORAGE_AUTHORITY.equals(fileUri.getAuthority())) {
DocumentFile file = DocumentFile.fromSingleUri(context, fileUri);
size = file.length();
fileName = file.getName();
lastModification = new Date(file.lastModified());
} else {
File file = new File(fileUri.getPath());
size = file.length();
fileName = file.getName();
lastModification = new Date(file.lastModified());
}
}
public boolean notFound() {
return getSize() == 0;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Uri getFileUri() {
return fileUri;
}
public void setFileUri(Uri fileUri) {
this.fileUri = fileUri;
}
public Date getLastModification() {
return lastModification;
}
public void setLastModification(Date lastModification) {
this.lastModification = lastModification;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* Copyright 2018 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX is free software: you can redistribute it and/or modify
@@ -17,26 +17,25 @@
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid;
package com.keepassdroid.fileselect;
import android.app.Dialog;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
public class CancelDialog extends Dialog {
import com.kunzisoft.keepass.R;
private boolean mCanceled = false;
public CancelDialog(Context context) {
super(context);
}
public boolean canceled() {
return mCanceled;
}
class FileSelectViewHolder extends RecyclerView.ViewHolder {
@Override
public void cancel() {
super.cancel();
mCanceled = true;
}
View fileContainer;
TextView fileName;
ImageView fileInformation;
FileSelectViewHolder(View itemView) {
super(itemView);
fileContainer = itemView.findViewById(R.id.file_container);
fileName = (TextView) itemView.findViewById(R.id.file_filename);
fileInformation = (ImageView) itemView.findViewById(R.id.file_information);
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.fileselect;
import android.os.AsyncTask;
class OpenFileHistoryAsyncTask extends AsyncTask<Integer, Void, Void> {
private AfterOpenFileHistoryListener afterOpenFileHistoryListener;
private RecentFileHistory fileHistory;
private String fileName;
private String keyFile;
OpenFileHistoryAsyncTask(AfterOpenFileHistoryListener afterOpenFileHistoryListener, RecentFileHistory fileHistory) {
this.afterOpenFileHistoryListener = afterOpenFileHistoryListener;
this.fileHistory = fileHistory;
}
protected Void doInBackground(Integer... args) {
int position = args[0];
fileName = fileHistory.getDatabaseAt(position);
keyFile = fileHistory.getKeyfileAt(position);
return null;
}
protected void onPostExecute(Void v) {
afterOpenFileHistoryListener.afterOpenFile(fileName, keyFile);
}
public interface AfterOpenFileHistoryListener {
void afterOpenFile(String fileName, String keyFile);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2017 Brian 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.fingerprint;
import android.content.Context;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.widget.ImageView;
import com.kunzisoft.keepass.R;
@RequiresApi(api = Build.VERSION_CODES.M)
public class FingerPrintAnimatedVector {
private AnimatedVectorDrawable scanFingerprint;
public FingerPrintAnimatedVector(Context context, ImageView imageView) {
scanFingerprint = (AnimatedVectorDrawable) context.getDrawable(R.drawable.scan_fingerprint);
imageView.setImageDrawable(scanFingerprint);
}
public void startScan() {
scanFingerprint.registerAnimationCallback(new Animatable2.AnimationCallback() {
public void onAnimationEnd(Drawable drawable) {
scanFingerprint.start();
}
});
scanFingerprint.start();
}
public void stopScan() {
scanFingerprint.stop();
}
}

View File

@@ -34,8 +34,14 @@ import android.util.Base64;
import com.keepassdroid.compat.BuildCompat;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
@@ -43,7 +49,7 @@ import javax.crypto.spec.IvParameterSpec;
public class FingerPrintHelper {
private static final String ALIAS_KEY = "example-key";
private static final String FINGERPRINT_KEYSTORE_KEY = "example-key";
private FingerprintManagerCompat fingerprintManager;
private KeyStore keyStore = null;
@@ -74,7 +80,7 @@ public class FingerPrintHelper {
}
public void stopListening() {
if (!isFingerprintInitialized()) {
if (!isFingerprintInitialized(false)) {
return;
}
if (cancellationSignal != null) {
@@ -83,13 +89,6 @@ public class FingerPrintHelper {
}
}
public interface FingerPrintCallback {
void handleEncryptedResult(String value, String ivSpec);
void handleDecryptedResult(String value);
void onInvalidKeyException();
void onFingerprintException(Exception e);
}
@TargetApi(BuildCompat.VERSION_CODE_M)
public FingerPrintHelper(
final Context context,
@@ -118,20 +117,26 @@ public class FingerPrintHelper {
setInitOk(true);
} catch (final Exception e) {
setInitOk(false);
fingerPrintCallback.onFingerprintException(e);
fingerPrintCallback.onFingerPrintException(e);
}
}
}
public boolean isFingerprintSupported(FingerprintManagerCompat fingerprintManager) {
public static boolean isFingerprintSupported(FingerprintManagerCompat fingerprintManager) {
return Build.VERSION.SDK_INT >= BuildCompat.VERSION_CODE_M
&& fingerprintManager != null
&& fingerprintManager.isHardwareDetected();
}
public boolean isFingerprintInitialized() {
return isFingerprintInitialized(true);
}
public boolean isFingerprintInitialized(boolean throwException) {
boolean isFingerprintInit = hasEnrolledFingerprints() && initOk;
if (!isFingerprintInit && fingerPrintCallback != null) {
fingerPrintCallback.onFingerprintException(new Exception("FingerPrint not initialized"));
if(throwException)
fingerPrintCallback.onFingerPrintException(new Exception("FingerPrint not initialized"));
}
return isFingerprintInit;
}
@@ -144,16 +149,17 @@ public class FingerPrintHelper {
try {
createNewKeyIfNeeded(false); // no need to keep deleting existing keys
keyStore.load(null);
final SecretKey key = (SecretKey) keyStore.getKey(ALIAS_KEY, null);
final SecretKey key = (SecretKey) keyStore.getKey(FINGERPRINT_KEYSTORE_KEY, null);
cipher.init(Cipher.ENCRYPT_MODE, key);
stopListening();
startListening();
} catch (final UnrecoverableKeyException unrecoverableKeyException) {
deleteEntryKey();
} catch (final KeyPermanentlyInvalidatedException invalidKeyException) {
fingerPrintCallback.onInvalidKeyException();
fingerPrintCallback.onInvalidKeyException(invalidKeyException);
} catch (final Exception e) {
fingerPrintCallback.onFingerprintException(e);
fingerPrintCallback.onFingerPrintException(e);
}
}
@@ -164,7 +170,7 @@ public class FingerPrintHelper {
try {
// actual do encryption here
byte[] encrypted = cipher.doFinal(value.getBytes());
final String encryptedValue = Base64.encodeToString(encrypted, 0 /* flags */);
final String encryptedValue = Base64.encodeToString(encrypted, Base64.DEFAULT);
// passes updated iv spec on to callback so this can be stored for decryption
final IvParameterSpec spec = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
@@ -172,7 +178,7 @@ public class FingerPrintHelper {
fingerPrintCallback.handleEncryptedResult(encryptedValue, ivSpecValue);
} catch (final Exception e) {
fingerPrintCallback.onFingerprintException(e);
fingerPrintCallback.onFingerPrintException(e);
}
}
@@ -184,7 +190,7 @@ public class FingerPrintHelper {
try {
createNewKeyIfNeeded(false);
keyStore.load(null);
final SecretKey key = (SecretKey) keyStore.getKey(ALIAS_KEY, null);
final SecretKey key = (SecretKey) keyStore.getKey(FINGERPRINT_KEYSTORE_KEY, null);
// important to restore spec here that was used for decryption
final byte[] iv = Base64.decode(ivSpecValue, Base64.DEFAULT);
@@ -195,9 +201,11 @@ public class FingerPrintHelper {
startListening();
} catch (final KeyPermanentlyInvalidatedException invalidKeyException) {
fingerPrintCallback.onInvalidKeyException();
fingerPrintCallback.onInvalidKeyException(invalidKeyException);
} catch (final UnrecoverableKeyException unrecoverableKeyException) {
deleteEntryKey();
} catch (final Exception e) {
fingerPrintCallback.onFingerprintException(e);
fingerPrintCallback.onFingerPrintException(e);
}
}
@@ -207,14 +215,16 @@ public class FingerPrintHelper {
}
try {
// actual decryption here
final byte[] encrypted = Base64.decode(encryptedValue, 0);
final byte[] encrypted = Base64.decode(encryptedValue, Base64.DEFAULT);
byte[] decrypted = cipher.doFinal(encrypted);
final String decryptedString = new String(decrypted);
//final String encryptedString = Base64.encodeToString(encrypted, 0 /* flags */);
fingerPrintCallback.handleDecryptedResult(decryptedString);
} catch (final BadPaddingException badPaddingException) {
fingerPrintCallback.onInvalidKeyException(badPaddingException);
} catch (final Exception e) {
fingerPrintCallback.onFingerprintException(e);
fingerPrintCallback.onFingerPrintException(e);
}
}
@@ -226,18 +236,17 @@ public class FingerPrintHelper {
try {
keyStore.load(null);
if (allowDeleteExisting
&& keyStore.containsAlias(ALIAS_KEY)) {
keyStore.deleteEntry(ALIAS_KEY);
&& keyStore.containsAlias(FINGERPRINT_KEYSTORE_KEY)) {
keyStore.deleteEntry(FINGERPRINT_KEYSTORE_KEY);
}
// Create new key if needed
if (!keyStore.containsAlias(ALIAS_KEY)) {
if (!keyStore.containsAlias(FINGERPRINT_KEYSTORE_KEY)) {
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
keyGenerator.init(
new KeyGenParameterSpec.Builder(
ALIAS_KEY,
FINGERPRINT_KEYSTORE_KEY,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
@@ -249,7 +258,19 @@ public class FingerPrintHelper {
keyGenerator.generateKey();
}
} catch (final Exception e) {
fingerPrintCallback.onFingerprintException(e);
fingerPrintCallback.onFingerPrintException(e);
}
}
public void deleteEntryKey() {
try {
keyStore.load(null);
keyStore.deleteEntry(FINGERPRINT_KEYSTORE_KEY);
} catch (KeyStoreException
| CertificateException
| NoSuchAlgorithmException
| IOException e) {
fingerPrintCallback.onFingerPrintException(e);
}
}
@@ -257,8 +278,6 @@ public class FingerPrintHelper {
public boolean hasEnrolledFingerprints() {
// fingerprint hardware supported and api level OK
return isFingerprintSupported(fingerprintManager)
&& fingerprintManager != null
&& fingerprintManager.isHardwareDetected()
// fingerprints enrolled
&& fingerprintManager.hasEnrolledFingerprints()
// and lockscreen configured
@@ -269,4 +288,40 @@ public class FingerPrintHelper {
this.initOk = initOk;
}
/**
* Remove entry key in keystore
*/
public static void deleteEntryKeyInKeystoreForFingerprints(final Context context,
final FingerPrintErrorCallback fingerPrintCallback) {
FingerPrintHelper fingerPrintHelper = new FingerPrintHelper(
context, new FingerPrintCallback() {
@Override
public void handleEncryptedResult(String value, String ivSpec) {}
@Override
public void handleDecryptedResult(String value) {}
@Override
public void onInvalidKeyException(Exception e) {
fingerPrintCallback.onInvalidKeyException(e);
}
@Override
public void onFingerPrintException(Exception e) {
fingerPrintCallback.onFingerPrintException(e);
}
});
fingerPrintHelper.deleteEntryKey();
}
public interface FingerPrintErrorCallback {
void onInvalidKeyException(Exception e);
void onFingerPrintException(Exception e);
}
public interface FingerPrintCallback extends FingerPrintErrorCallback {
void handleEncryptedResult(String value, String ivSpec);
void handleDecryptedResult(String value);
}
}

View File

@@ -2,12 +2,13 @@ package com.keepassdroid.settings;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import com.kunzisoft.keepass.R;
import com.keepassdroid.Database;
import com.keepassdroid.app.App;
import com.kunzisoft.keepass.R;
public class MainPreferenceFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener {
@@ -33,12 +34,25 @@ public class MainPreferenceFragment extends PreferenceFragmentCompat implements
preference.setOnPreferenceClickListener(this);
preference = findPreference(getString(R.string.db_key));
preference.setOnPreferenceClickListener(this);
Database db = App.getDB();
if (!(db.Loaded() && db.pm.appSettingsEnabled())) {
Preference dbSettings = findPreference(getString(R.string.db_key));
dbSettings.setEnabled(false);
} else {
preference.setOnPreferenceClickListener(this);
if (!(db.Loaded())) {
preference.setEnabled(false);
}
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
// Try if the preference is one of our custom Preferences
if (preference instanceof RoundsPreference) {
DialogFragment dialogFragment = RoundsFixPreferenceDialogFragmentCompat.newInstance(preference.getKey());
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(getFragmentManager(), null);
}
// Could not be handled here. Try with the super method.
else {
super.onDisplayPreferenceDialog(preference);
}
}

View File

@@ -19,17 +19,26 @@
*/
package com.keepassdroid.settings;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.support.v14.preference.SwitchPreference;
import android.support.v4.app.DialogFragment;
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.util.Log;
import android.widget.Toast;
import com.kunzisoft.keepass.R;
import com.keepassdroid.Database;
import com.keepassdroid.UnavailableFeatureDialog;
import com.keepassdroid.app.App;
import com.keepassdroid.database.PwEncryptionAlgorithm;
import com.keepassdroid.fingerprint.FingerPrintHelper;
import com.keepassdroid.stylish.Stylish;
import com.kunzisoft.keepass.R;
public class NestedSettingsFragment extends PreferenceFragmentCompat {
@@ -98,28 +107,84 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat {
}
});
SwitchPreference fingerprintEnablePreference = (SwitchPreference) findPreference(getString(R.string.fingerprint_enable_key));
if (!FingerPrintHelper.isFingerprintSupported(FingerprintManagerCompat.from(getContext()))) {
// False if under Marshmallow
fingerprintEnablePreference.setDefaultValue(false);
fingerprintEnablePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
((SwitchPreference) preference).setChecked(false);
UnavailableFeatureDialog.getInstance(Build.VERSION_CODES.M)
.show(getFragmentManager(), "unavailableFeatureDialog");
return false;
}
});
}
Preference deleteKeysFingerprints = findPreference(getString(R.string.fingerprint_delete_all_key));
deleteKeysFingerprints.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
new AlertDialog.Builder(getContext())
.setMessage(getResources().getString(R.string.fingerprint_delete_all_warning))
.setIcon(getResources().getDrawable(
android.R.drawable.ic_dialog_alert))
.setPositiveButton(
getResources().getString(android.R.string.yes),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
FingerPrintHelper.deleteEntryKeyInKeystoreForFingerprints(
getContext(),
new FingerPrintHelper.FingerPrintErrorCallback() {
@Override
public void onInvalidKeyException(Exception e) {}
@Override
public void onFingerPrintException(Exception e) {
Toast.makeText(getContext(), R.string.fingerprint_error, Toast.LENGTH_SHORT).show();
}
});
PrefsUtil.deleteAllValuesFromNoBackupPreferences(getContext());
}
})
.setNegativeButton(
getResources().getString(android.R.string.no),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
}
}).show();
return false;
}
});
break;
case NESTED_SCREEN_DB_KEY:
setPreferencesFromResource(R.xml.db_preferences, rootKey);
Database db = App.getDB();
Preference algorithmPref = findPreference(getString(R.string.algorithm_key));
Preference roundPref = findPreference(getString(R.string.rounds_key));
if (!(db.Loaded() && db.pm.appSettingsEnabled())) {
algorithmPref.setEnabled(false);
roundPref.setEnabled(false);
}
if (db.Loaded() && db.pm.appSettingsEnabled()) {
Preference rounds = findPreference(getString(R.string.rounds_key));
rounds.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
roundPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
setRounds(App.getDB(), preference);
return true;
}
});
setRounds(db, rounds);
Preference algorithm = findPreference(getString(R.string.algorithm_key));
setAlgorithm(db, algorithm);
setRounds(db, roundPref);
setAlgorithm(db, algorithmPref);
} else {
Log.e(getClass().getName(), "Database isn't ready");
}
@@ -131,6 +196,20 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat {
}
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
// Try if the preference is one of our custom Preferences
if (preference instanceof RoundsPreference) {
DialogFragment dialogFragment = RoundsPreferenceDialogFragmentCompat.newInstance(preference.getKey());
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(getFragmentManager(), null);
}
// Could not be handled here. Try with the super method.
else {
super.onDisplayPreferenceDialog(preference);
}
}
private void setRounds(Database db, Preference rounds) {
rounds.setSummary(Long.toString(db.pm.getNumRounds()));
}

View File

@@ -25,10 +25,67 @@ import android.preference.PreferenceManager;
import com.kunzisoft.keepass.R;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class PrefsUtil {
private static final String NO_BACKUP_PREFERENCE_FILE_NAME = "nobackup";
public static SharedPreferences getNoBackupSharedPreferences(Context ctx) {
return ctx.getSharedPreferences(
PrefsUtil.NO_BACKUP_PREFERENCE_FILE_NAME,
Context.MODE_PRIVATE);
}
public static void deleteAllValuesFromNoBackupPreferences(Context ctx) {
SharedPreferences prefsNoBackup = getNoBackupSharedPreferences(ctx);
SharedPreferences.Editor sharedPreferencesEditor = prefsNoBackup.edit();
sharedPreferencesEditor.clear();
sharedPreferencesEditor.apply();
}
public static float getListTextSize(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return Float.parseFloat(prefs.getString(ctx.getString(R.string.list_size_key), ctx.getString(R.string.list_size_default)));
}
public static int getDefaultPasswordLength(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getInt(ctx.getString(R.string.password_length_key),
Integer.parseInt(ctx.getString(R.string.default_password_length)));
}
public static Set<String> getDefaultPasswordCharacters(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getStringSet(ctx.getString(R.string.list_password_generator_options_key),
new HashSet<>(Arrays.asList(
ctx.getResources()
.getStringArray(R.array.list_password_generator_options_default_values))));
}
public static boolean isClipboardNotificationsEnable(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getBoolean(ctx.getString(R.string.clipboard_notifications_key),
ctx.getResources().getBoolean(R.bool.clipboard_notifications_default));
}
public static boolean isLockDatabaseWhenScreenShutOffEnable(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getBoolean(ctx.getString(R.string.lock_database_screen_off_key),
ctx.getResources().getBoolean(R.bool.lock_database_screen_off_default));
}
public static boolean isFingerprintEnable(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getBoolean(ctx.getString(R.string.fingerprint_enable_key),
ctx.getResources().getBoolean(R.bool.fingerprint_enable_default));
}
public static boolean isFullFilePathEnable(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getBoolean(ctx.getString(R.string.full_file_path_enable_key),
ctx.getResources().getBoolean(R.bool.full_file_path_enable_default));
}
}

View File

@@ -0,0 +1,65 @@
package com.keepassdroid.settings;
import android.os.Bundle;
import android.support.v7.preference.DialogPreference;
import android.support.v7.preference.PreferenceDialogFragmentCompat;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.kunzisoft.keepass.R;
public class RoundsFixPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
private TextView mRoundsView;
public static RoundsFixPreferenceDialogFragmentCompat newInstance(
String key) {
final RoundsFixPreferenceDialogFragmentCompat
fragment = new RoundsFixPreferenceDialogFragmentCompat();
final Bundle b = new Bundle(1);
b.putString(ARG_KEY, key);
fragment.setArguments(b);
return fragment;
}
@Override
public void onDialogClosed(boolean positiveResult) {
if ( positiveResult ) {
long rounds;
try {
String strRounds = mRoundsView.getText().toString();
rounds = Long.valueOf(strRounds);
} catch (NumberFormatException e) {
Toast.makeText(getContext(), R.string.error_rounds_not_number, Toast.LENGTH_LONG).show();
return;
}
DialogPreference preference = getPreference();
if (preference instanceof RoundsPreference) {
RoundsPreference roundsPreference = (RoundsPreference) preference;
// This allows the client to ignore the user value.
if (roundsPreference.callChangeListener(rounds)) {
// Save the value
roundsPreference.setRounds(rounds);
}
}
}
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
TextView textDescriptionView = (TextView) view.findViewById(R.id.rounds_explanation);
mRoundsView = (TextView) view.findViewById(R.id.rounds);
DialogPreference preference = getPreference();
if (preference instanceof RoundsPreference) {
textDescriptionView.setText(((RoundsPreference) preference).getExplanations());
long numRounds = ((RoundsPreference) preference).getRounds();
mRoundsView.setText(String.valueOf(numRounds));
}
}
}

View File

@@ -19,112 +19,91 @@
*/
package com.keepassdroid.settings;
import android.content.Context;
import android.os.Handler;
import android.preference.DialogPreference;
import android.content.res.TypedArray;
import android.support.v7.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.kunzisoft.keepass.R;
import com.keepassdroid.Database;
import com.keepassdroid.ProgressTask;
import com.keepassdroid.app.App;
import com.keepassdroid.database.PwDatabase;
import com.keepassdroid.database.edit.OnFinish;
import com.keepassdroid.database.edit.SaveDB;
public class RoundsPreference extends DialogPreference {
private PwDatabase mPM;
private TextView mRoundsView;
@Override
protected View onCreateDialogView() {
View view = super.onCreateDialogView();
mRoundsView = (TextView) view.findViewById(R.id.rounds);
Database db = App.getDB();
mPM = db.pm;
long numRounds = mPM.getNumRounds();
mRoundsView.setText(Long.toString(numRounds));
return view;
}
private long mRounds;
private String explanations;
public RoundsPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundsPreference(Context context) {
this(context, null);
}
public RoundsPreference(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.dialogPreferenceStyle);
}
public RoundsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, defStyleAttr);
}
public RoundsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.RoundsDialog,
0, 0);
try {
explanations = a.getString(R.styleable.RoundsDialog_description);
} finally {
a.recycle();
}
}
public RoundsPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public int getDialogLayoutResource() {
return R.layout.pref_dialog_rounds;
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
public String getExplanations() {
return explanations;
}
if ( positiveResult ) {
int rounds;
try {
String strRounds = mRoundsView.getText().toString();
rounds = Integer.parseInt(strRounds);
} catch (NumberFormatException e) {
Toast.makeText(getContext(), R.string.error_rounds_not_number, Toast.LENGTH_LONG).show();
return;
}
if ( rounds < 1 ) {
rounds = 1;
}
long oldRounds = mPM.getNumRounds();
try {
mPM.setNumRounds(rounds);
} catch (NumberFormatException e) {
Toast.makeText(getContext(), R.string.error_rounds_too_large, Toast.LENGTH_LONG).show();
mPM.setNumRounds(Integer.MAX_VALUE);
}
Handler handler = new Handler();
SaveDB save = new SaveDB(getContext(), App.getDB(), new AfterSave(getContext(), handler, oldRounds));
ProgressTask pt = new ProgressTask(getContext(), save, R.string.saving_database);
pt.run();
}
public void setExplanations(String explanations) {
this.explanations = explanations;
}
}
private class AfterSave extends OnFinish {
private long mOldRounds;
private Context mCtx;
public AfterSave(Context ctx, Handler handler, long oldRounds) {
super(handler);
mCtx = ctx;
mOldRounds = oldRounds;
}
public long getRounds() {
return mRounds;
}
@Override
public void run() {
if ( mSuccess ) {
OnPreferenceChangeListener listner = getOnPreferenceChangeListener();
if ( listner != null ) {
listner.onPreferenceChange(RoundsPreference.this, null);
}
} else {
displayMessage(mCtx);
mPM.setNumRounds(mOldRounds);
}
super.run();
}
}
public void setRounds(long rounds) {
this.mRounds = rounds;
// Save to Shared Preferences
persistLong(rounds);
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
// Default value from attribute. Fallback value is set to 0.
return a.getInt(index, 0);
}
@Override
protected void onSetInitialValue(boolean restorePersistedValue,
Object defaultValue) {
// Read the value. Use the default value if it is not possible.
long rounds;
if (!restorePersistedValue) {
rounds = 100000;
if (defaultValue instanceof String) {
rounds = Long.parseLong((String) defaultValue);
}
if (defaultValue instanceof Integer) {
rounds = (Integer) defaultValue;
}
try {
rounds = (long) defaultValue;
} catch (Exception e) {
e.printStackTrace();
}
} else {
rounds = getPersistedLong(mRounds);
}
setRounds(rounds);
}
}

View File

@@ -0,0 +1,111 @@
package com.keepassdroid.settings;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.preference.DialogPreference;
import android.support.v7.preference.PreferenceDialogFragmentCompat;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.keepassdroid.Database;
import com.keepassdroid.ProgressTask;
import com.keepassdroid.app.App;
import com.keepassdroid.database.PwDatabase;
import com.keepassdroid.database.edit.OnFinish;
import com.keepassdroid.database.edit.SaveDB;
import com.kunzisoft.keepass.R;
public class RoundsPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
private PwDatabase mPM;
private TextView mRoundsView;
public static RoundsPreferenceDialogFragmentCompat newInstance(
String key) {
final RoundsPreferenceDialogFragmentCompat
fragment = new RoundsPreferenceDialogFragmentCompat();
final Bundle b = new Bundle(1);
b.putString(ARG_KEY, key);
fragment.setArguments(b);
return fragment;
}
@Override
public void onDialogClosed(boolean positiveResult) {
if ( positiveResult ) {
long rounds;
try {
String strRounds = mRoundsView.getText().toString();
rounds = Long.parseLong(strRounds);
} catch (NumberFormatException e) {
Toast.makeText(getContext(), R.string.error_rounds_not_number, Toast.LENGTH_LONG).show();
return;
}
if ( rounds < 1 ) {
rounds = 1;
}
long oldRounds = mPM.getNumRounds();
try {
mPM.setNumRounds(rounds);
} catch (NumberFormatException e) {
Toast.makeText(getContext(), R.string.error_rounds_too_large, Toast.LENGTH_LONG).show();
mPM.setNumRounds(Integer.MAX_VALUE);
}
Handler handler = new Handler();
SaveDB save = new SaveDB(getContext(), App.getDB(), new AfterSave(getContext(), handler, oldRounds));
ProgressTask pt = new ProgressTask(getContext(), save, R.string.saving_database);
pt.run();
}
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
mRoundsView = (TextView) view.findViewById(R.id.rounds);
// Get the time from the related Preference
Database db = App.getDB();
mPM = db.pm;
long numRounds = mPM.getNumRounds();
DialogPreference preference = getPreference();
if (preference instanceof RoundsPreference) {
numRounds = ((RoundsPreference) preference).getRounds();
}
mRoundsView.setText(String.valueOf(numRounds));
}
private class AfterSave extends OnFinish {
private long mOldRounds;
private Context mCtx;
public AfterSave(Context ctx, Handler handler, long oldRounds) {
super(handler);
mCtx = ctx;
mOldRounds = oldRounds;
}
@Override
public void run() {
if ( mSuccess ) {
} else {
displayMessage(mCtx);
mPM.setNumRounds(mOldRounds);
}
super.run();
}
}
}

View File

@@ -97,8 +97,8 @@ public class SettingsActivity extends StylishActivity implements MainPreferenceF
super.onBackPressed();
} else {
getFragmentManager().popBackStack();
toolbar.setTitle(R.string.settings);
}
toolbar.setTitle(R.string.settings);
}
@Override

View File

@@ -9,9 +9,9 @@ import com.kunzisoft.keepass.R;
public class Stylish {
private static String stylishPrefKey;
protected static String stylishPrefKey;
private static String themeString;
protected static String themeString;
public static void init(Context context) {
stylishPrefKey = context.getString(R.string.setting_style_key);
@@ -23,7 +23,7 @@ public class Stylish {
themeString = styleString;
}
static @StyleRes int getThemeId(Context context) {
public static @StyleRes int getThemeId(Context context) {
if (themeString.equals(context.getString(R.string.list_style_name_night)))
return R.style.KeepassDXStyle_Night;

View File

@@ -21,17 +21,37 @@ package com.keepassdroid.utils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Seconds;
import java.util.Date;
public class DateUtil {
private static final DateTime dotNetEpoch = new DateTime(1, 1, 1, 0, 0, 0, DateTimeZone.UTC);
private static final DateTime javaEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeZone.UTC);
private static final long epochOffset;
static {
Date dotNet = dotNetEpoch.toDate();
Date java = javaEpoch.toDate();
epochOffset = (javaEpoch.getMillis() - dotNetEpoch.getMillis()) / 1000L;
}
public static Date convertKDBX4Time(long seconds) {
return dotNetEpoch.plus(seconds).toDate();
DateTime dt = dotNetEpoch.plus(seconds * 1000L);
// Switch corrupted dates to a more recent date that won't cause issues on the client
if (dt.isBefore(javaEpoch)) {
return javaEpoch.toDate();
}
return dt.toDate();
}
public static long convertDateToKDBX4Time(DateTime dt) {
return (dt.getMillis() / 1000) - (dotNetEpoch.getMillis() / 1000);
Seconds secs = Seconds.secondsBetween(javaEpoch, dt);
return secs.getSeconds() + epochOffset;
}
}

View File

@@ -19,12 +19,6 @@
*/
package com.keepassdroid.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.keepassdroid.database.exception.SamsungClipboardException;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -33,6 +27,12 @@ import android.net.Uri;
import android.text.ClipboardManager;
import android.widget.TextView;
import com.keepassdroid.database.exception.SamsungClipboardException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Util {
public static String getClipboard(Context context) {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
@@ -75,14 +75,6 @@ public class Util {
}
}
public static void setEditText(Activity act, int resId, String str) {
TextView te = (TextView) act.findViewById(resId);
if (te != null) {
te.setText(str);
}
}
public static void copyStream(InputStream in, OutputStream out) throws IOException {
byte[] buf = new byte[1024];
int read;

View File

@@ -0,0 +1,67 @@
package com.keepassdroid.view;
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.Handler;
import com.keepassdroid.ProgressTask;
import com.keepassdroid.app.App;
import com.keepassdroid.database.edit.FileOnFinish;
import com.keepassdroid.database.edit.OnFinish;
import com.keepassdroid.database.edit.SetPassword;
import com.kunzisoft.keepass.R;
public class AssignPasswordHelper {
private Context context;
private String masterPassword;
private Uri keyfile;
public AssignPasswordHelper(Context context,
String masterPassword,
Uri keyfile) {
this.context = context;
this.masterPassword = masterPassword;
this.keyfile = keyfile;
}
public void assignPasswordInDatabase(FileOnFinish fileOnFinish) {
SetPassword sp = new SetPassword(context, App.getDB(), masterPassword, keyfile, new AfterSave(fileOnFinish, new Handler()));
final ProgressTask pt = new ProgressTask(context, sp, R.string.saving_database);
boolean valid = sp.validatePassword(context, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
pt.run();
}
});
if (valid) {
pt.run();
}
}
private class AfterSave extends OnFinish {
private FileOnFinish mFinish;
public AfterSave(FileOnFinish finish, Handler handler) {
super(finish, handler);
mFinish = finish;
}
@Override
public void run() {
if ( mSuccess ) {
if ( mFinish != null ) {
mFinish.setFilename(keyfile);
}
} else {
displayMessage(context);
}
super.run();
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* KeePass DX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.view;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import com.keepassdroid.fingerprint.FingerPrintAnimatedVector;
import com.kunzisoft.keepass.R;
@RequiresApi(api = Build.VERSION_CODES.M)
public class FingerPrintDialog extends DialogFragment {
private FingerPrintAnimatedVector fingerPrintAnimatedVector;
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View rootView = inflater.inflate(R.layout.fingerprint_dialog, null);
View fingerprintSettingWayTextView = rootView.findViewById(R.id.fingerprint_setting_way_text);
fingerprintSettingWayTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS));
}
});
fingerPrintAnimatedVector =
new FingerPrintAnimatedVector(getContext(),
(ImageView) rootView.findViewById(R.id.fingerprint_image));
builder.setView(rootView)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
}
});
return builder.create();
}
@Override
public void onResume() {
super.onResume();
fingerPrintAnimatedVector.startScan();
}
@Override
public void onPause() {
super.onPause();
fingerPrintAnimatedVector.stopScan();
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2017 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* KeePass DX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.view;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.app.Fragment;
import android.view.View;
import com.keepassdroid.compat.StorageAF;
import com.keepassdroid.fileselect.BrowserDialog;
import com.keepassdroid.intents.Intents;
import com.keepassdroid.utils.Interaction;
import com.keepassdroid.utils.UriUtil;
import java.io.File;
import static android.app.Activity.RESULT_OK;
public class KeyFileHelper {
public static final int GET_CONTENT = 25745;
private static final int OPEN_DOC = 25845;
private static final int FILE_BROWSE = 25645;
private Activity activity;
private Fragment fragment;
private Uri mDbUri;
public KeyFileHelper(Activity context) {
this(context, null);
}
public KeyFileHelper(Activity context, Uri mDbUri) {
this.activity = context;
this.fragment = null;
this.mDbUri = mDbUri;
}
public KeyFileHelper(Fragment context) {
this(context, null);
}
public KeyFileHelper(Fragment context, Uri mDbUri) {
this.activity = context.getActivity();
this.fragment = context;
this.mDbUri = mDbUri;
}
public View.OnClickListener getOpenFileOnClickViewListener() {
return new View.OnClickListener() {
public void onClick(View v) {
if (StorageAF.useStorageFramework(activity)) {
Intent i = new Intent(StorageAF.ACTION_OPEN_DOCUMENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
if(fragment != null)
fragment.startActivityForResult(i, OPEN_DOC);
else
activity.startActivityForResult(i, OPEN_DOC);
} else {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
try {
if(fragment != null)
fragment.startActivityForResult(i, GET_CONTENT);
else
activity.startActivityForResult(i, GET_CONTENT);
} catch (ActivityNotFoundException e) {
lookForOpenIntentsFilePicker();
}
}
}
};
}
private void lookForOpenIntentsFilePicker() {
if (Interaction.isIntentAvailable(activity, Intents.OPEN_INTENTS_FILE_BROWSE)) {
Intent i = new Intent(Intents.OPEN_INTENTS_FILE_BROWSE);
// Get file path parent if possible
try {
if (mDbUri != null && mDbUri.toString().length() > 0) {
if (mDbUri.getScheme().equals("file")) {
File keyfile = new File(mDbUri.getPath());
File parent = keyfile.getParentFile();
if (parent != null) {
i.setData(Uri.parse("file://" + parent.getAbsolutePath()));
}
}
}
} catch (Exception e) {
// Ignore
}
try {
if(fragment != null)
fragment.startActivityForResult(i, FILE_BROWSE);
else
activity.startActivityForResult(i, FILE_BROWSE);
} catch (ActivityNotFoundException e) {
showBrowserDialog();
}
} else {
showBrowserDialog();
}
}
private void showBrowserDialog() {
BrowserDialog browserDialog = new BrowserDialog(activity);
browserDialog.show();
}
public void onActivityResultCallback(
int requestCode,
int resultCode,
Intent data,
KeyFileCallback keyFileCallback) {
switch (requestCode) {
case FILE_BROWSE:
if (resultCode == RESULT_OK) {
String filename = data.getDataString();
Uri keyUri = null;
if (filename != null) {
keyUri = UriUtil.parseDefaultFile(filename);
}
if (keyFileCallback != null)
keyFileCallback.onKeyFileResultCallback(keyUri);
}
break;
case GET_CONTENT:
case OPEN_DOC:
if (resultCode == RESULT_OK) {
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
if (requestCode == GET_CONTENT) {
uri = UriUtil.translate(activity, uri);
}
if (keyFileCallback != null)
keyFileCallback.onKeyFileResultCallback(uri);
}
}
}
break;
}
}
public interface KeyFileCallback {
void onKeyFileResultCallback(Uri uri);
}
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">
<objectAnimator
android:propertyName="pathData"
android:valueFrom="@string/clip_path_scan_top"
android:valueTo="@string/clip_path_scan_bottom"
android:valueType="pathType"
android:duration="800"
android:interpolator="@android:interpolator/fast_out_slow_in" />
<objectAnimator
android:propertyName="pathData"
android:valueFrom="@string/clip_path_scan_bottom"
android:valueTo="@string/clip_path_scan_top"
android:valueType="pathType"
android:startOffset="50"
android:duration="500"
android:interpolator="@android:interpolator/fast_out_slow_in" />
</set>

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/fingerprint_width"
android:height="@dimen/fingerprint_height"
android:viewportWidth="@integer/fingerprint_viewport_width"
android:viewportHeight="@integer/fingerprint_viewport_height">
<group
android:name="fingerprint"
android:pivotX="@integer/fingerprint_viewport_center"
android:pivotY="@integer/fingerprint_viewport_center">
<path
android:name="ridge_1"
android:pathData="@string/path_ridge_1"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_2"
android:pathData="@string/path_ridge_2"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_3"
android:pathData="@string/path_ridge_3"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_4"
android:pathData="@string/path_ridge_4"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_5"
android:pathData="@string/path_ridge_5"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
</group>
</vector>

View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/fingerprint_width"
android:height="@dimen/fingerprint_height"
android:viewportWidth="@integer/fingerprint_viewport_width"
android:viewportHeight="@integer/fingerprint_viewport_height">
<group
android:name="fingerprint">
<path
android:name="ridge_1"
android:pathData="@string/path_ridge_1"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_2"
android:pathData="@string/path_ridge_2"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_3"
android:pathData="@string/path_ridge_3"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_4"
android:pathData="@string/path_ridge_4"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_5"
android:pathData="@string/path_ridge_5"
android:strokeColor="@color/fingerprint_ridge"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
</group>
<!-- we overlay the above with a duplicate of the fingerprint which is colored differently
and uses a clip to only reveal portions at a time. -->
<group
android:name="fingerprint_scan">
<clip-path
android:name="scan_clip"
android:pathData="@string/clip_path_scan_top" />
<path
android:name="ridge_1"
android:pathData="@string/path_ridge_1"
android:strokeColor="@color/fingerprint_ridge_scan"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_2"
android:pathData="@string/path_ridge_2"
android:strokeColor="@color/fingerprint_ridge_scan"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_3"
android:pathData="@string/path_ridge_3"
android:strokeColor="@color/fingerprint_ridge_scan"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_4"
android:pathData="@string/path_ridge_4"
android:strokeColor="@color/fingerprint_ridge_scan"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
<path
android:name="ridge_5"
android:pathData="@string/path_ridge_5"
android:strokeColor="@color/fingerprint_ridge_scan"
android:strokeLineCap="round"
android:strokeWidth="@integer/fingerprint_stroke_width" />
</group>
</vector>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/circle"
/>
<item
android:drawable="@drawable/ic_lock_open_white_24dp"
android:bottom="12dp"
android:left="12dp"
android:right="12dp"
android:top="12dp"/>
</layer-list>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/fingerprint_scan">
<target
android:name="scan_clip"
android:animation="@animator/scan" />
</animated-vector>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="@color/green"/>
</shape>

View File

@@ -0,0 +1,10 @@
<!-- drawable/database_plus.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M9,3C4.58,3 1,4.79 1,7C1,9.21 4.58,11 9,11C13.42,11 17,9.21 17,7C17,4.79 13.42,3 9,3M1,9V12C1,14.21 4.58,16 9,16C13.42,16 17,14.21 17,12V9C17,11.21 13.42,13 9,13C4.58,13 1,11.21 1,9M1,14V17C1,19.21 4.58,21 9,21C10.41,21 11.79,20.81 13,20.46V17.46C11.79,17.81 10.41,18 9,18C4.58,18 1,16.21 1,14M18,14V17H15V19H18V22H20V19H23V17H20V14" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/iconPreferenceColor"
android:pathData="M11,4.07L11,2.05c-2.01,0.2 -3.84,1 -5.32,2.21L7.1,5.69c1.11,-0.86 2.44,-1.44 3.9,-1.62zM18.32,4.26C16.84,3.05 15.01,2.25 13,2.05v2.02c1.46,0.18 2.79,0.76 3.9,1.62l1.42,-1.43zM19.93,11h2.02c-0.2,-2.01 -1,-3.84 -2.21,-5.32L18.31,7.1c0.86,1.11 1.44,2.44 1.62,3.9zM5.69,7.1L4.26,5.68C3.05,7.16 2.25,8.99 2.05,11h2.02c0.18,-1.46 0.76,-2.79 1.62,-3.9zM4.07,13L2.05,13c0.2,2.01 1,3.84 2.21,5.32l1.43,-1.43c-0.86,-1.1 -1.44,-2.43 -1.62,-3.89zM15,12c0,-1.66 -1.34,-3 -3,-3s-3,1.34 -3,3 1.34,3 3,3 3,-1.34 3,-3zM18.31,16.9l1.43,1.43c1.21,-1.48 2.01,-3.32 2.21,-5.32h-2.02c-0.18,1.45 -0.76,2.78 -1.62,3.89zM13,19.93v2.02c2.01,-0.2 3.84,-1 5.32,-2.21l-1.43,-1.43c-1.1,0.86 -2.43,1.44 -3.89,1.62zM5.68,19.74C7.16,20.95 9,21.75 11,21.95v-2.02c-1.46,-0.18 -2.79,-0.76 -3.9,-1.62l-1.42,1.43z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<!-- drawable/folder_open.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M19,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10L12,6H19A2,2 0 0,1 21,8H21L4,8V18L6.14,10H23.21L20.93,18.5C20.7,19.37 19.92,20 19,20Z" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/default_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Title"
android:text="@string/fingerprint_quick_unlock_title"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="20dp"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.WarningTextStyle"
android:text="@string/chapter_1"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/fingerprint_setting_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
style="@style/KeepassDXStyle.TextAppearance.TinyText"
android:text="@string/fingerprint_setting_text"/>
<TextView
android:id="@+id/fingerprint_setting_way_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.TinyText"
android:textColor="?attr/colorAccent"
android:text="@string/fingerprint_setting_way_text"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="12dp">
<TextView
android:layout_width="20dp"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.WarningTextStyle"
android:text="@string/chapter_2"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.TinyText"
android:padding="5dp"
android:text="@string/fingerprint_type_password_text"/>
<android.support.v7.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="38dp"
android:src="@drawable/type_assword"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="12dp">
<TextView
android:layout_width="20dp"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.WarningTextStyle"
android:text="@string/chapter_3" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.TinyText"
android:padding="5dp"
android:text="@string/fingerprint_scan_to_store"/>
</LinearLayout>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/fingerprint_image"
android:layout_gravity="center"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
android:elevation="4dp"
android:src="@drawable/fingerprint"
android:background="@drawable/circle"
android:backgroundTint="?attr/colorAccent"
tools:targetApi="lollipop" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Title"
android:text="@string/usage" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.TinyText"
android:padding="5dp"
android:gravity="center"
android:text="@string/fingerprint_scan_to_open"/>
<android.support.v7.widget.AppCompatImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:elevation="4dp"
android:layout_marginBottom="8dp"
android:layout_gravity="center"
android:src="@drawable/lock_open"
android:background="@drawable/circle"
android:backgroundTint="?attr/colorPrimary"
tools:targetApi="lollipop" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fingerprint_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:visibility="gone">
<!-- added these 2 fingerprint related views -->
<android.support.v7.widget.AppCompatImageView
android:id="@+id/fingerprint_image"
android:layout_width="38dp"
android:layout_height="38dp"
android:layout_margin="4dp"
android:layout_alignParentEnd="true"
android:elevation="4dp"
android:src="@drawable/fingerprint"
android:background="@drawable/circle"
android:backgroundTint="?attr/colorAccent" />
<TextView
android:id="@+id/fingerprint_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="@+id/fingerprint_image"
android:gravity="center_vertical|end"
android:text="@string/entry_and_or"
android:textColor="?attr/colorAccent" />
</RelativeLayout>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/default_margin">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/browse_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="6dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignBottom="@+id/folder_path_input_layout"
android:src="@drawable/ic_folder_white_24dp"
android:tint="?attr/colorAccentCompat" />
<android.support.design.widget.TextInputLayout
android:id="@+id/folder_path_input_layout"
android:layout_toLeftOf="@id/browse_button"
android:layout_toStartOf="@id/browse_button"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:id="@+id/folder_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/path"
android:inputType="textUri"
android:maxLines="1"
android:singleLine="true"/>
</android.support.design.widget.TextInputLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputLayout
android:id="@+id/filename_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/file_types"
android:layout_toStartOf="@+id/file_types">
<android.support.design.widget.TextInputEditText
android:id="@+id/filename"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/file_name"
android:text="@string/database_file_name_default"
android:inputType="textUri"
android:maxLines="1"
android:singleLine="true"/>
</android.support.design.widget.TextInputLayout>
<android.support.v7.widget.AppCompatSpinner
android:id="@+id/file_types"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/filename_input_layout"
android:layout_marginBottom="8dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -17,11 +17,35 @@
You should have received a copy of the GNU General Public License
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/file_filename"
android:layout_width="wrap_content"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/file_container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingLeft="18dp"
android:paddingRight="18dp"
android:paddingBottom="8dp" />
android:background="?android:attr/selectableItemBackground">
<android.support.v7.widget.AppCompatTextView
android:id="@+id/file_filename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/file_information"
android:layout_toStartOf="@+id/file_information"
android:paddingBottom="18dp"
android:paddingEnd="18dp"
android:paddingLeft="18dp"
android:paddingRight="18dp"
android:paddingStart="18dp"
android:paddingTop="18dp" />
<android.support.v7.widget.AppCompatImageView
android:id="@+id/file_information"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:padding="18dp"
android:src="@drawable/ic_info_white_24dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:tint="?attr/colorPrimary"/>
</RelativeLayout>

View File

@@ -17,32 +17,100 @@
You should have received a copy of the GNU General Public License
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_height="match_parent">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar_default" />
<com.keepassdroid.view.FileNameView android:id="@+id/file_select"
android:layout_marginBottom="@dimen/default_margin"
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="match_parent">
<TextView android:id="@+id/file_listtop"
android:layout_marginTop="@dimen/default_margin"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin"
android:layout_marginEnd="@dimen/default_margin"
android:layout_width="match_parent"
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:titleEnabled="false"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways|snap">
<com.keepassdroid.view.FileNameView android:id="@+id/file_select"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:theme="?attr/toolbarAppearance"
app:popupTheme="?attr/toolbarPopupAppearance"
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/open_database"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="48dp"
android:paddingTop="-20dp"
app:fabSize="mini"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|end"
android:src="@drawable/ic_open_folder_white_24dp" />
<android.support.v4.widget.NestedScrollView
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView android:id="@+id/file_list_title"
android:layout_marginTop="@dimen/default_margin"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin"
android:layout_marginEnd="@dimen/default_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Title"
android:text="@string/open_recent" />
<android.support.v7.widget.RecyclerView
android:id="@+id/file_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/create_database"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Title"
android:text="@string/open_recent" />
<ListView android:id="@+id/file_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:src="@drawable/ic_database_plus_white_24dp"
style="@style/KeepassDXStyle.Fab"/>
</RelativeLayout>

View File

@@ -17,62 +17,63 @@
You should have received a copy of the GNU General Public License
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_margin="@dimen/default_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView android:id="@+id/label_warning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:visibility="invisible" />
<TextView android:id="@+id/label_open_by_filename"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Title"
android:text="@string/enter_filename"/>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/browse_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/file_filename"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_below="@id/label_open_by_filename"
android:padding="6dp"
android:src="@drawable/ic_folder_white_24dp"
android:tint="?attr/colorAccentCompat" />
<android.support.v7.widget.AppCompatEditText
android:id="@+id/file_filename"
<android.support.v7.widget.CardView
android:id="@+id/filename_container"
app:cardBackgroundColor="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/label_open_by_filename"
android:inputType="textUri"
android:layout_toLeftOf="@+id/browse_button"
android:layout_toStartOf="@+id/browse_button"
android:maxLines="1" />
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatButton android:id="@+id/open"
android:layout_margin="@dimen/button_margin"
android:text="@string/menu_open"
android:layout_below="@id/file_filename"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_toLeftOf="@+id/create"
android:layout_toStartOf="@+id/create"
android:width="100sp"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/default_margin"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin"
android:layout_marginEnd="@dimen/default_margin"
android:layout_marginBottom="22dp">
<android.support.v7.widget.AppCompatButton android:id="@+id/create"
android:layout_margin="@dimen/button_margin"
android:text="@string/menu_create"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_below="@id/file_filename"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:width="100sp"/>
</RelativeLayout>
<TextView android:id="@+id/label_warning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:visibility="invisible" />
<TextView android:id="@+id/label_open_by_filename"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Inverse"
android:text="@string/enter_filename"/>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/browse_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/file_filename"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_below="@id/label_open_by_filename"
android:padding="6dp"
android:src="@drawable/ic_folder_white_24dp"
android:tint="?attr/colorAccentCompat" />
<android.support.v7.widget.AppCompatEditText
android:id="@+id/file_filename"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/label_open_by_filename"
android:inputType="textUri"
android:textColor="?attr/textColorInverse"
android:layout_toLeftOf="@+id/browse_button"
android:layout_toStartOf="@+id/browse_button"
android:maxLines="1" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</android.support.design.widget.CoordinatorLayout>

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/default_margin">
<android.support.v7.widget.AppCompatTextView
android:id="@+id/file_filename"
android:layout_margin="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.LargeTitle"/>
<android.support.v7.widget.AppCompatTextView
android:id="@+id/file_path"
android:layout_margin="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:textStyle="italic"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end">
<android.support.v7.widget.AppCompatTextView
android:id="@+id/file_modification_label"
android:layout_margin="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/entry_modified"/>
<android.support.v7.widget.AppCompatTextView
android:id="@+id/file_modification"
android:layout_margin="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<android.support.v7.widget.AppCompatTextView
android:id="@+id/file_size"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/file_size_unit"
android:layout_toStartOf="@+id/file_size_unit"
android:gravity="end" />
<android.support.v7.widget.AppCompatTextView
android:id="@+id/file_size_unit"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bytes"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
<android.support.v7.widget.AppCompatTextView
android:id="@+id/file_warning"
android:layout_margin="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
style="@style/KeepassDXStyle.TextAppearance.WarningTextStyle"/>
</LinearLayout>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fingerprint_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:visibility="gone" />

View File

@@ -20,6 +20,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -35,7 +36,8 @@
android:layout_height="wrap_content"
android:ems="10"
android:maxLines="3"
android:hint="@string/hint_generated_password" />
android:hint="@string/hint_generated_password"
tools:ignore="TextFields" />
<Button android:id="@+id/generate_password_button"
android:layout_margin="@dimen/button_margin"
@@ -71,7 +73,7 @@
android:maxLines="1"
android:maxLength="3"
android:inputType="number"
android:text="@integer/default_password_length"
android:text="@string/default_password_length"
android:hint="@string/hint_length"/>
<android.support.v7.widget.AppCompatSeekBar android:id="@+id/seekbar_length"
@@ -83,59 +85,176 @@
android:layout_alignTop="@+id/length"
android:layout_toEndOf="@+id/length"
android:layout_toRightOf="@+id/length"
app:min="1"
android:progress="@integer/default_password_length"
android:max="64"/>
app:min="@string/min_password_length"
android:progress="@string/default_password_length"
android:max="@string/max_password_length"/>
</RelativeLayout>
<LinearLayout android:id="@+id/RelativeLayout"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
android:orientation="vertical"
android:layout_marginRight="20dp"
android:layout_marginEnd="20dp">
<CheckBox android:id="@+id/cb_uppercase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/uppercase"
android:checked="true" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/cb_uppercase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/uppercase"
android:checked="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="end"
android:layout_toEndOf="@+id/cb_uppercase"
android:layout_toRightOf="@+id/cb_uppercase"
android:text="@string/visual_uppercase" />
</RelativeLayout>
<CheckBox android:id="@+id/cb_lowercase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lowercase"
android:checked="true" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/cb_lowercase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lowercase"
android:checked="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="end"
android:layout_toEndOf="@+id/cb_lowercase"
android:layout_toRightOf="@+id/cb_lowercase"
android:text="@string/visual_lowercase" />
</RelativeLayout>
<CheckBox android:id="@+id/cb_digits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/digits"
android:checked="true" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/cb_digits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/digits"
android:checked="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="end"
android:layout_toEndOf="@+id/cb_digits"
android:layout_toRightOf="@+id/cb_digits"
android:text="@string/visual_digits" />
</RelativeLayout>
<CheckBox android:id="@+id/cb_minus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/minus" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/cb_minus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/minus" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="end"
android:layout_toEndOf="@+id/cb_minus"
android:layout_toRightOf="@+id/cb_minus"
android:text="@string/visual_minus" />
</RelativeLayout>
<CheckBox android:id="@+id/cb_underline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/underline" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/cb_underline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/underline" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="end"
android:layout_toEndOf="@+id/cb_underline"
android:layout_toRightOf="@+id/cb_underline"
android:text="@string/visual_underline" />
</RelativeLayout>
<CheckBox android:id="@+id/cb_space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/space" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/cb_space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/space" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="end"
android:layout_toEndOf="@+id/cb_space"
android:layout_toRightOf="@+id/cb_space"
android:text="@string/visual_space" />
</RelativeLayout>
<CheckBox android:id="@+id/cb_specials"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/special" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/cb_specials"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/special" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="end"
android:layout_toEndOf="@+id/cb_specials"
android:layout_toRightOf="@+id/cb_specials"
android:text="@string/visual_special" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/cb_brackets"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/brackets"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="end"
android:layout_toEndOf="@+id/cb_brackets"
android:layout_toRightOf="@+id/cb_brackets"
android:text="@string/visual_brackets" />
</RelativeLayout>
<CheckBox android:id="@+id/cb_brackets"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/brackets" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -24,13 +24,13 @@
android:id="@+id/group_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ListView android:id="@android:id/list"
<ListView android:id="@+id/group_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/group_header"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@android:id/list"
android:layout_below="@id/group_list"
android:text="@string/no_results"/>
</RelativeLayout>

View File

@@ -59,60 +59,44 @@
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/toolbar"
android:layout_above="@+id/pass_ok">
android:layout_below="@+id/toolbar">
<RelativeLayout
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/default_margin">
<TextView
android:id="@+id/password_label"
style="@style/KeepassDXStyle.TextAppearance.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/entry_and_or" />
<include
layout="@layout/fingerprint_show" />
<!-- Password Input -->
<RelativeLayout
android:id="@+id/password_container"
android:layout_width="match_parent"
android:id="@+id/password_input_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/password_label"
style="@style/KeepassDXStyle.TextAppearance.Title"
<android.support.v7.widget.AppCompatCheckBox
android:id="@+id/password_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/entry_and_or" />
android:layout_alignParentBottom="true"
android:paddingBottom="20dp"
android:gravity="center_vertical" />
<!-- added these 2 fingerprint related views -->
<android.support.v7.widget.AppCompatImageView
android:id="@+id/fingerprint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_small"
android:layout_marginRight="@dimen/margin_small"
android:layout_below="@id/password_label"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:src="@drawable/ic_fp_40px"
android:visibility="gone"
tools:visibility="visible"
/>
<TextView
android:id="@+id/fingerprint_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/fingerprint"
android:layout_below="@+id/password_label"
android:layout_toLeftOf="@id/fingerprint"
android:layout_toStartOf="@id/fingerprint"
android:gravity="center_vertical|end"
android:text="@string/entry_and_or"
android:visibility="gone"
tools:visibility="visible" />
<!-- Password Input -->
<android.support.design.widget.TextInputLayout
android:id="@+id/password_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/fingerprint_label"
android:layout_toRightOf="@+id/password_checkbox"
android:layout_toEndOf="@+id/password_checkbox"
app:passwordToggleEnabled="true"
app:passwordToggleTint="?attr/colorAccent">
@@ -120,12 +104,25 @@
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_login_pass"
android:hint="@string/password"
android:inputType="textPassword"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
</RelativeLayout>
<!-- File Input -->
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatCheckBox
android:id="@+id/keyfile_checkox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:paddingBottom="20dp"
android:gravity="center_vertical" />
<!-- File Input -->
<android.support.v7.widget.AppCompatImageView
android:id="@+id/browse_button"
android:layout_width="wrap_content"
@@ -133,36 +130,37 @@
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/password_input_layout"
android:padding="12dp"
android:src="@drawable/ic_folder_white_24dp"
android:tint="?attr/colorAccentCompat" />
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:id="@+id/input_entry_keyfile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/password_input_layout"
android:layout_toLeftOf="@id/browse_button"
android:layout_toStartOf="@id/browse_button" >
android:layout_toEndOf="@+id/keyfile_checkox"
android:layout_toRightOf="@+id/keyfile_checkox"
android:layout_toLeftOf="@+id/browse_button"
android:layout_toStartOf="@+id/browse_button">
<android.support.v7.widget.AppCompatEditText
android:id="@+id/pass_keyfile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:hint="@string/entry_keyfile"
android:inputType="text"
android:hint="@string/entry_keyfile" />
android:maxLines="1" />
</android.support.design.widget.TextInputLayout>
</RelativeLayout>
<android.support.v7.widget.AppCompatCheckBox
<android.support.v7.widget.SwitchCompat
android:id="@+id/default_database"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/password_container"
android:layout_marginTop="8dp"
android:layout_margin="16dp"
android:text="@string/default_checkbox" />
</RelativeLayout>
</LinearLayout>
</ScrollView>

View File

@@ -17,21 +17,25 @@
You should have received a copy of the GNU General Public License
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/edit"
android:padding="20dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar_default" />
<com.keepassdroid.view.FileNameView android:id="@+id/file_select"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar" />
<!-- Small hack because I need to include a list since this is a list activity -->
<ListView android:id="@+id/file_list"
android:layout_width="0sp"
android:layout_height="0sp" />
</RelativeLayout>
<android.support.v7.widget.AppCompatTextView
android:layout_marginBottom="8dp"
android:id="@+id/rounds_explanation"
android:gravity="center"
android:text="@string/rounds_explaination"
style="@style/KeepassDXStyle.TextAppearance.SmallTitle"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<android.support.v7.widget.AppCompatEditText
android:id="@+id/rounds"
android:hint="@string/rounds_hint"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:digits="0123456789"
android:inputType="number"/>
</LinearLayout>

View File

@@ -17,27 +17,109 @@
You should have received a copy of the GNU General Public License
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="@dimen/default_margin"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText android:id="@+id/pass_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:maxLines="1"
android:hint="@string/hint_pass"/>
<EditText android:id="@+id/pass_conf_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/pass_password"
android:inputType="textPassword"
android:maxLines="1"
android:hint="@string/hint_conf_pass"/>
<EditText android:id="@+id/pass_keyfile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/pass_conf_password"
android:maxLines="1"
android:hint="@string/hint_keyfile"/>
</RelativeLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="@dimen/default_margin"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:id="@+id/card_view_master_password"
android:layout_margin="4dp"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:orientation="vertical">
<android.support.v7.widget.AppCompatCheckBox
android:id="@+id/password_checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/password"/>
<!-- Password Input -->
<android.support.design.widget.TextInputLayout
android:id="@+id/password_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true"
app:passwordToggleTint="?attr/colorAccent">
<android.support.design.widget.TextInputEditText
android:id="@+id/pass_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:maxLines="1"
android:hint="@string/hint_pass"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/password_repeat_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true"
app:passwordToggleTint="?attr/colorAccent">
<EditText android:id="@+id/pass_conf_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:maxLines="1"
android:hint="@string/hint_conf_pass"/>
</android.support.design.widget.TextInputLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/card_view_key_file"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
app:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:orientation="vertical">
<android.support.v7.widget.AppCompatCheckBox
android:id="@+id/keyfile_checkox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/entry_keyfile"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/browse_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:padding="12dp"
android:src="@drawable/ic_folder_white_24dp"
android:tint="?attr/colorAccentCompat" />
<android.support.v7.widget.AppCompatEditText
android:id="@+id/pass_keyfile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/browse_button"
android:layout_toStartOf="@+id/browse_button"
android:hint="@string/hint_keyfile"
android:maxLines="1"
android:singleLine="true"/>
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>

View File

@@ -51,7 +51,7 @@
<string name="decrypting_entry">Desencriptant entrada</string>
<string name="default_checkbox">Utilitza aquesta com a base de dades per defecte</string>
<string name="digits">Dígits</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft ve SENSE CAP MENA DE GARANTIA; Això és programari lliure, i pots redistribuïr-lo sota els termes de la llicència GPL versió 2 o posterior.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin ve SENSE CAP MENA DE GARANTIA; Això és programari lliure, i pots redistribuïr-lo sota els termes de la llicència GPL versió 2 o posterior.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Introdueix el nom de la base de dades:</string>
<string name="entry_accessed">Accedida: </string>
@@ -61,7 +61,7 @@
<string name="entry_confpassword">Confirma contrasenya:</string>
<string name="entry_created">Creada: </string>
<string name="entry_expires">Expira: </string>
<string name="entry_keyfile">Arxiu clau (opcional)</string>
<string name="entry_keyfile">Arxiu clau</string>
<string name="entry_modified">Modificada: </string>
<string name="entry_password">Contrasenya:</string>
<string name="entry_save">Guarda</string>
@@ -80,7 +80,7 @@
<string name="error_invalid_db">Base de dades invàlida.</string>
<string name="error_invalid_path">Camí invàlid.</string>
<string name="error_no_name">És necessari un nom.</string>
<string name="error_nopass">És necessaria una contrasenya o un arxiu clau.</string>
<string name="error_nokeyfile">És necessaria una contrasenya o un arxiu clau.</string>
<string name="error_out_of_memory">El telèfon sha quedat sense memòria processant la teva base de dades. Potser és massa gran pel teu telèfon.</string>
<string name="error_pass_gen_type">Has de seleccionar almenys un tipus de generador de contrasenyes</string>
<string name="error_pass_match">Les contrasenyes no coincideixen.</string>
@@ -88,7 +88,7 @@
<string name="error_rounds_too_large">Massa passades. Establint a 2147483648.</string>
<string name="error_title_required">És necessari un títol.</string>
<string name="error_wrong_length">Insereix un enter positiu al camp longitud</string>
<string name="FileNotFound">Arxiu no trobat.</string>
<string name="file_not_found">Arxiu no trobat.</string>
<string name="file_browser">Explorador d\'arxius</string>
<string name="generate_password">Generar contrasenya</string>
<string name="group">Grup</string>
@@ -99,7 +99,7 @@
<string name="hint_keyfile">arxiu clau</string>
<string name="hint_length">longitud</string>
<string name="hint_pass">contrasenya</string>
<string name="hint_login_pass">Contrasenya</string>
<string name="password">Contrasenya</string>
<string name="hint_title">nom</string>
<string name="hint_url">url</string>
<string name="hint_username">usuari</string>
@@ -138,7 +138,7 @@
<string name="no_keys">Sense entrades a la base de dades o grup.</string>
<string name="no_results">Cap resultat de cerca</string>
<string name="no_url_handler">Sense gestor per aquesta url.</string>
<string name="open_recent">Obre base de dades recent (clica per obrir):</string>
<string name="open_recent">Obre base de dades recent :</string>
<string name="omitbackup_title">No cerquis entrades a còpia de seguretat ni paperera</string>
<string name="omitbackup_summary">Omet els grups \'Còpia de seguretat\' i paperera dels resultats de cerca</string>
<string name="pass_filename">Nom de base de dades KeePass:</string>

View File

@@ -56,7 +56,7 @@
<string name="decrypting_entry">Dešifruji záznam</string>
<string name="default_checkbox">Použít jako výchozí databázi</string>
<string name="digits">Čísla</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft dodáván BEZ JAKÉKOLIV ZÁRUKY; Toto je free software zdarma, a je možná jeho redistribuce pod podmínkou licence GPL verze 2 nebo novější.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin dodáván BEZ JAKÉKOLIV ZÁRUKY; Toto je free software zdarma, a je možná jeho redistribuce pod podmínkou licence GPL verze 2 nebo novější.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Zadejte názvev souboru databáze:</string>
<string name="entry_accessed">Poslední přístup: </string>
@@ -66,7 +66,7 @@
<string name="entry_confpassword">Potvrďte heslo:</string>
<string name="entry_created">Vytvořeno: </string>
<string name="entry_expires">Vyprší: </string>
<string name="entry_keyfile">Klíčový soubor (nepovinné)</string>
<string name="entry_keyfile">Klíčový soubor</string>
<string name="entry_modified">Změněno: </string>
<string name="entry_not_found">Vstupní data nenalezena.</string>
<string name="entry_password">Heslo:</string>
@@ -86,7 +86,7 @@
<string name="error_invalid_db">Chybná databáze.</string>
<string name="error_invalid_path">Chybná cesta.</string>
<string name="error_no_name">Jméno je povinné.</string>
<string name="error_nopass">Heslo nebo klíčový soubor jsou povinné.</string>
<string name="error_nokeyfile">Heslo nebo klíčový soubor jsou povinné.</string>
<string name="error_out_of_memory">Přístroj má málo paměti pro zpracování databáze. Možná je příliš velká pro Váš přístroj.</string>
<string name="error_pass_gen_type">Minimálně jeden typ generování hesla musí být zvolen</string>
<string name="error_pass_match">Hesla se neshodují.</string>
@@ -97,7 +97,7 @@
<string name="error_wrong_length">Zadejte celé kladné číslo do délky pole</string>
<string name="field_name">Název pole</string>
<string name="field_value">Hodnota pole</string>
<string name="FileNotFound">Soubor nenalezen.</string>
<string name="file_not_found">Soubor nenalezen.</string>
<string name="file_browser">Správce souborů</string>
<string name="generate_password">Generovat heslo</string>
<string name="group">Skupina</string>
@@ -108,7 +108,7 @@
<string name="hint_keyfile">klíčový soubor</string>
<string name="hint_length">délka</string>
<string name="hint_pass">heslo</string>
<string name="hint_login_pass">Heslo</string>
<string name="password">Heslo</string>
<string name="hint_title">název</string>
<string name="hint_url">url</string>
<string name="hint_username">uživatelské jméno</string>
@@ -150,7 +150,7 @@
<string name="no_keys">Žádný záznam v databázi nebo ve skupině.</string>
<string name="no_results">žádný výsledek hledání</string>
<string name="no_url_handler">Žádný handler pro toto url.</string>
<string name="open_recent">Otevřít poslední databázi (kliknout pro otevření):</string>
<string name="open_recent">Otevřít poslední databázi :</string>
<string name="omitbackup_title">Neprohledávat zálohy a koš</string>
<string name="omitbackup_summary">Vynechat skupiny Záloha a Koš ve výsledcích hledání</string>
<string name="pass_filename">Název souboru KeePass databáze:</string>

View File

@@ -55,7 +55,7 @@
<string name="decrypting_entry">Dekrypterer post</string>
<string name="default_checkbox">Brug denne database som standard</string>
<string name="digits">Cifre</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under the conditions of the GPL version 2 or later.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under the conditions of the GPL version 2 or later.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Angiv navn på databasefil:</string>
<string name="entry_accessed">Senest åbnet: </string>
@@ -65,7 +65,7 @@
<string name="entry_confpassword">Bekræft adgangskode:</string>
<string name="entry_created">Oprettet: </string>
<string name="entry_expires">Udløber: </string>
<string name="entry_keyfile">Nøglefil (valgfri)</string>
<string name="entry_keyfile">Nøglefil</string>
<string name="entry_modified">Ændret: </string>
<string name="entry_not_found">Data for posten blev ikke fundet.</string>
<string name="entry_password">Adgangskode:</string>
@@ -85,7 +85,7 @@
<string name="error_invalid_db">Ugyldig database.</string>
<string name="error_invalid_path">Ugyldig sti.</string>
<string name="error_no_name">Et navn er påkrævet.</string>
<string name="error_nopass">En adgangskode eller nøglefil er påkrævet.</string>
<string name="error_nokeyfile">En adgangskode eller nøglefil er påkrævet.</string>
<string name="error_out_of_memory">Telefonen løb tør for hukommelse under analysen af din database. Den kan være for stor til, at din telefon kan håndtere den.</string>
<string name="error_pass_gen_type">Mindst én adgangskode-genererings-type skal vælges</string>
<string name="error_pass_match">Adgangskoder matcher ikke.</string>
@@ -96,7 +96,7 @@
<string name="error_wrong_length">Angiv et positivt helt tal i feltet</string>
<string name="field_name">Felt-navn</string>
<string name="field_value">Felt-værdi</string>
<string name="FileNotFound">Filen blev ikke fundet.</string>
<string name="file_not_found">Filen blev ikke fundet.</string>
<string name="file_browser">Fil-browser</string>
<string name="generate_password">Generer adgangskode</string>
<string name="group">Gruppe</string>
@@ -107,7 +107,7 @@
<string name="hint_keyfile">nøglefil</string>
<string name="hint_length">længde</string>
<string name="hint_pass">adgangskode</string>
<string name="hint_login_pass">Adgangskode</string>
<string name="password">Adgangskode</string>
<string name="hint_title">navn</string>
<string name="hint_url">url</string>
<string name="hint_username">brugernavn</string>
@@ -149,7 +149,7 @@
<string name="no_keys">Ingen poster i databasen eller gruppen.</string>
<string name="no_results">Ingen søgeresultater</string>
<string name="no_url_handler">Kan ikke håndtere denne url.</string>
<string name="open_recent">Åbn seneste database (klik for at åbne):</string>
<string name="open_recent">Åbn seneste database :</string>
<string name="omitbackup_title">Søg ikke efter backup poster og poster i papirkurven</string>
<string name="omitbackup_summary">Udelad \'Backup\' og papirkurvs-gruppen i søgeresultater</string>
<string name="pass_filename">KeePass database filnavn:</string>

View File

@@ -42,7 +42,7 @@
<string name="brackets">Klammern</string>
<string name="browser_intall_text">Um diese Funktion nutzen zu können, benötigen Sie den OI File Manager, den Sie über Google Play bzw. direkt von der Entwicklerwebseite herunterladen können. Aufgrund einiger Eigenheiten des Dateimanagers, kann es bei der ersten Benutzung zu Einschränkungen kommen.</string>
<string name="building_search_idx">Erstelle den Suchindex\u2026</string>
<string name="cancel">Abbr.</string>
<string name="cancel">Abbrechen</string>
<string name="ClearClipboard">Zwischenablage geleert</string>
<string name="clipboard_error_title">Zwischenablagefehler</string>
<string name="clipboard_error">Die Implementierung der Zwischenablagefunktion in einigen Samsung Android-Smartphones ist offenbar fehlerhaft, wodurch das Kopieren von Daten aus der Anwendung heraus fehlschlägt. Weitere Informationen dazu finden Sie unter:</string>
@@ -59,7 +59,7 @@
<string name="decrypting_entry">Eintrag entschlüsselt</string>
<string name="default_checkbox">Als Standard-Datenbank benutzen</string>
<string name="digits">Zahlen</string>
<string name="disclaimer_formal">KeePass DX \u00A9 2009&#8211;2013 Brian Pellin. Alle Rechte vorbehalten. Die Nutzung der Software erfolgt auf eigene Verantwortung und ohne jegliche Garantie. Die Applikation ist kostenlos und wird unter den Bedingungen der GNU GPL Version 2 (oder später) verbreitet und lizenziert.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin. Alle Rechte vorbehalten. Die Nutzung der Software erfolgt auf eigene Verantwortung und ohne jegliche Garantie. Die Applikation ist kostenlos und wird unter den Bedingungen der GNU GPL Version 2 (oder später) verbreitet und lizenziert.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Dateinamen der Datenbank eingeben:</string>
<string name="entry_accessed">Letzter Zugriff:</string>
@@ -69,7 +69,7 @@
<string name="entry_confpassword">Passwort wiederholen:</string>
<string name="entry_created">Erstelldatum:</string>
<string name="entry_expires">Ablaufdatum:</string>
<string name="entry_keyfile">Schlüsseldatei (optional)</string>
<string name="entry_keyfile">Schlüsseldatei</string>
<string name="entry_modified">Änderungsdatum:</string>
<string name="entry_not_found">Eintrag wurde nicht gefunden.</string>
<string name="entry_password">Passwort:</string>
@@ -89,7 +89,7 @@
<string name="error_invalid_db">Ungültige Datenbank.</string>
<string name="error_invalid_path">Ungültiger Pfad.</string>
<string name="error_no_name">Ein Name wird benötigt.</string>
<string name="error_nopass">Ein Passwort oder eine Schlüsseldatei wird benötigt.</string>
<string name="error_nokeyfile">Ein Passwort oder eine Schlüsseldatei wird benötigt.</string>
<string name="error_out_of_memory">Der Speicher Ihres Smartphones reicht nicht aus, um die Datenbank zu analysieren. Sie ist wahrscheinlich zu groß.</string>
<string name="error_pass_gen_type">Mindestens ein Passwort-Generierungstyp muss ausgewählt werden.</string>
<string name="error_pass_match">Die Passwörter stimmen nicht überein.</string>
@@ -100,7 +100,7 @@
<string name="error_wrong_length">Geben Sie die erforderliche Länge als positive Ganzzahl in das Feld ein.</string>
<string name="field_name">Feldname</string>
<string name="field_value">Feldwert</string>
<string name="FileNotFound">Datei nicht gefunden.</string>
<string name="file_not_found">Datei nicht gefunden.</string>
<string name="file_not_found_content">Datei nicht gefunden. Versuche es von ihrem Dienstanbieter erneut zu öffnen.</string>
<string name="file_browser">Dateimanager</string>
<string name="generate_password">Passwort generieren</string>
@@ -112,7 +112,7 @@
<string name="hint_keyfile">Schlüsseldatei</string>
<string name="hint_length">Länge</string>
<string name="hint_pass">Passwort</string>
<string name="hint_login_pass">Passwort</string>
<string name="password">Passwort</string>
<string name="hint_title">Name</string>
<string name="hint_url">URL-Adresse</string>
<string name="hint_username">Benutzername</string>
@@ -154,7 +154,7 @@
<string name="no_keys">Keine Einträge in dieser Datenbank oder Gruppe.</string>
<string name="no_results">Keine Suchergebnisse</string>
<string name="no_url_handler">Kein Handler zum Öffnen der URL vorhanden.</string>
<string name="open_recent">Zuletzt geöffnete Datenbank (Klicken zum Öffnen):</string>
<string name="open_recent">Zuletzt geöffnete Datenbank :</string>
<string name="omitbackup_title">Papierkorb/Sicherungen nicht durchsuchen</string>
<string name="omitbackup_summary">Papierkorb und Sicherungseinträge werden bei der Suche nicht berücksichtigt (nur bei kdb Dateien).</string>
<string name="pass_filename">KeePass Datenbankdatei:</string>
@@ -175,6 +175,9 @@
<string name="rounds">Schlüsseltransformationen</string>
<string name="rounds_explaination">Je höher die Anzahl der Schlüsseltransformationen, desto besser ist der Schutz gegen Wörterbuch- oder Brute-Force-Angriffe. Allerdings dauert dann auch das Laden und Speichern der Datenbank entsprechend länger.</string>
<string name="rounds_hint">Schlüsseltransformationen</string>
<string name="rounds_fix_title">Datenbank reparieren</string>
<string name="rounds_fix">Schlüsseltransformationen vor Datenbankkorruption</string>
<string name="rounds_fix_explanation">Falls ihre Datenbank von KeepassDroid version 2.2.0.0 bis 2.2.0.6 beschädigt wurde, können Sie die Anzahl der Schlüsseltransformationen welche verwendet wurden eingeben um ihre Datenbank wieder zu öffnen.</string>
<string name="saving_database">Speichere Datenbank\u2026</string>
<string name="space">Leerzeichen</string>
<string name="search_label">Suchen</string>
@@ -207,4 +210,41 @@
<item>Mittel</item>
<item>Groß</item>
</string-array>
<string name="warning_empty_password">Sind Sie sicher, dass sie ein leeres Passwort verwenden wollen ?</string>
<string name="warning_no_encryption_key">Sind Sie sicher, dass sie keinen Verschlüsselungsschlüssel verwenden wollen ?</string>
<string name="appearance">Aussehen</string>
<string name="password_size_title">Passwortgröße</string>
<string name="password_size_summary">Standartlänge des generierten Passworts ändern</string>
<string name="clipboard_notifications_title">Zwischenablagenbenachrichtigungen</string>
<string name="clipboard_notifications_summary">Zwischenablagenbenachrichtigungen zum kopieren von Benutzername und Passwort einschalten</string>
<string name="lock_database_screen_off_title">Bildschirmsperre</string>
<string name="lock_database_screen_off_summary">Datenbank sperren, wenn der Bildschirm ausgeschaltet wird</string>
<string name="create_keepass_file">Keepass Datei erstellen</string>
<string name="style_choose_title">Thema auswählen</string>
<string name="assign_master_key">Hauptschlüssel zuweisen</string>
<string name="path">Pfad</string>
<string name="file_name">Dateiname</string>
<string name="unavailable_feature_text">Dieses Feature wird nicht von ihrer Android version unterstützt</string>
<string name="fingerprint_enable_summary">Datenbanköffnung mit Fingerabdruck einschalten</string>
<string name="fingerprint">Fingerabdruck</string>
<string name="fingerprint_enable_title">Fingerabruckscanner</string>
<string name="fingerprint_scan_to_open">Fingerabruck scannen während Passwortfeld leer ist, um Datenbank zu öffnen</string>
<string name="fingerprint_scan_to_store">Fingerabruck scannen, um Masterpasswort zu speichern</string>
<string name="fingerprint_type_password_text">Geben Sie ihr Passwort in Keepass DX ein</string>
<string name="lock">Sperre</string>
<string name="list_password_generator_options_summary">Standartzeichen für Passwortgenerator setzen</string>
<string name="list_password_generator_options_title">Passwortzeichen</string>
<string name="configure_fingerprint">Fingerabruck unterstützt, jedoch nicht für dieses Gerät konfiguriert</string>
<string name="scanning_fingerprint">Fingerabdruck wird gescannt</string>
<string name="encrypted_value_stored">Verschlüsseltes Passwort wurde gespeichert</string>
<string name="history">Verlauf</string>
<string name="fingerprint_quick_unlock_title">Wie richte ich den Fingerabdruckscanner ein ?</string>
<string name="fingerprint_setting_text">Speichern sie ihren persönlichen Fingerabruck in</string>
<string name="fingerprint_setting_way_text">Einstellungen -> Fingerabdruck</string>
<string name="usage">Verwendung</string>
<string name="general">Allgemein</string>
<string name="store_with_fingerprint">Fingerabruck verwenden um dieses Passwort zu speichern</string>
<string name="no_password_stored">Noch kein Passwort für diese Datenbank gespeichert</string>
<string name="style_choose_summary">Thema durch verändern der Farben ändern</string>
</resources>

View File

@@ -53,7 +53,7 @@
<string name="decrypting_entry">Αποκρυπτογράφηση εγγραφής</string>
<string name="default_checkbox">Χρήση αυτής της βάσης ως προεπιλεγμένη</string>
<string name="digits">Ψηφία</string>
<string name="disclaimer_formal">KeePass DX Πνευματική Ιδιοκτησία 2009&#8211;2012 Brian Pellin χωρίς ΚΑΜΙΑ ΑΠΟΛΥΤΩΣ ΕΓΓΥΗΣΗ. Το παρόν είναι δωρεάν λογισμικό και είστε ευπρόσδεκτοι να το διαμοιράσετε υπό τις συνθήκες της ΙΕΛ έκδοσης 2 ή μεταγενέστερης.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin χωρίς ΚΑΜΙΑ ΑΠΟΛΥΤΩΣ ΕΓΓΥΗΣΗ. Το παρόν είναι δωρεάν λογισμικό και είστε ευπρόσδεκτοι να το διαμοιράσετε υπό τις συνθήκες της ΙΕΛ έκδοσης 2 ή μεταγενέστερης.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Εισαγωγή ονόματος βάσης δεδομένων:</string>
<string name="entry_accessed">Προσπελάσθηκε: </string>
@@ -63,7 +63,7 @@
<string name="entry_confpassword">Επιβεβαίωση κωδικού πρόσβασης:</string>
<string name="entry_created">Δημιουργήθηκε: </string>
<string name="entry_expires">Λήγει: </string>
<string name="entry_keyfile">Αρχείο Κλειδιού (προαιρετικό)</string>
<string name="entry_keyfile">Αρχείο Κλειδιού</string>
<string name="entry_modified">Τροποποιήθηκε: </string>
<string name="entry_not_found">Δεν βρέθηκαν δεδομένα εγγραφών.</string>
<string name="entry_password">Κωδικός Πρόσβασης:</string>
@@ -83,7 +83,7 @@
<string name="error_invalid_db">Μη έγκυρη βάση δεδομένων.</string>
<string name="error_invalid_path">Μη έγκυρη διαδρομή.</string>
<string name="error_no_name">Απαιτείται ένα όνομα.</string>
<string name="error_nopass">Απαιτείται ο κωδικός πρόσβασης ή ένα αρχείο κλειδιού.</string>
<string name="error_nokeyfile">Απαιτείται ο κωδικός πρόσβασης ή ένα αρχείο κλειδιού.</string>
<string name="error_out_of_memory">Το τηλέφωνο ξέμεινε από μνήμη κατά τη διάρκεια προσπέλασης της βάσης δεδομένων σας. Μπορεί να είναι πολύ μεγάλη για το τηλέφωνο σας.</string>
<string name="error_pass_gen_type">Πρέπει να επιλεγεί τουλάχιστον ένας τύπος δημιουργίας κωδικού πρόσβασης</string>
<string name="error_pass_match">Οι κωδικοί δεν ταιριάζουν.</string>
@@ -94,7 +94,7 @@
<string name="error_wrong_length">Εισάγετε έναν θετικό ακέραιο αριθμό στο πεδίο μήκους</string>
<string name="field_name">Όνομα Πεδίου</string>
<string name="field_value">Τιμή πεδίου</string>
<string name="FileNotFound">Το αρχείο δεν βρέθηκε.</string>
<string name="file_not_found">Το αρχείο δεν βρέθηκε.</string>
<string name="file_browser">Διαχείριση Αρχείων</string>
<string name="generate_password">Δημιουργία Κωδικού</string>
<string name="group">Ομάδα</string>
@@ -105,7 +105,7 @@
<string name="hint_keyfile">αρχείο κλειδιού</string>
<string name="hint_length">μήκος</string>
<string name="hint_pass">κωδικός</string>
<string name="hint_login_pass">Κωδικός Πρόσβασης</string>
<string name="password">Κωδικός Πρόσβασης</string>
<string name="hint_title">όνομα</string>
<string name="hint_url">διεύθυνση url</string>
<string name="hint_username">όνομα χρήστη</string>
@@ -147,7 +147,7 @@
<string name="no_keys">Δεν υπάρχουν εγγραφές στη βάση ή στην ομάδα.</string>
<string name="no_results">Δε βρέθηκαν αποτελέσματα αναζήτησης</string>
<string name="no_url_handler">Χωρίς χειριστή για τη διεύθυνση url.</string>
<string name="open_recent">Άνοιγμα πρόσφατης βάσης (πατήστε για άνοιγμα):</string>
<string name="open_recent">Άνοιγμα πρόσφατης βάσης :</string>
<string name="omitbackup_title">Να μην γίνει αναζήτηση σε αντίγραφο ασφαλείας εγγραφών</string>
<string name="omitbackup_summary">Παράληψη ομάδας \'Αντίγραφο Ασφαλείας\' από τα αποτελέσματα αναζήτησης (εφαρμόζεται μόνο σε αρχεία .kdb)</string>
<string name="pass_filename">Όνομα βάσης δεδομένων KeePass:</string>

View File

@@ -50,7 +50,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201
<string name="decrypting_entry">Descrifrando la entrada</string>
<string name="default_checkbox">Utilice esto como base de datos por defecto</string>
<string name="digits">Dígitos</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft NO TIENE TOTAL GARANTÍA; Este es software libre, y puedes redristribuirlo bajo las condiciones de la licencia GPL version 2 o posterior.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin NO TIENE TOTAL GARANTÍA; Este es software libre, y puedes redristribuirlo bajo las condiciones de la licencia GPL version 2 o posterior.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Introduzca el nombre del archivo de base de datos:</string>
<string name="entry_accessed">Acceso: </string>
@@ -60,7 +60,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201
<string name="entry_confpassword">Confirmar contraseña:</string>
<string name="entry_created">Creación: </string>
<string name="entry_expires">Caducidad: </string>
<string name="entry_keyfile">Archivo de clave (opcional)</string>
<string name="entry_keyfile">Archivo de clave</string>
<string name="entry_modified">Modificación: </string>
<string name="entry_password">Contraseña:</string>
<string name="entry_save">Guardar</string>
@@ -79,7 +79,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201
<string name="error_invalid_db">Base de datos no válida.</string>
<string name="error_invalid_path">Ruta no válida.</string>
<string name="error_no_name">Se necesita un nombre.</string>
<string name="error_nopass">Se necesita una contraseña o un archivo de clave.</string>
<string name="error_nokeyfile">Se necesita una contraseña o un archivo de clave.</string>
<string name="error_out_of_memory">El dispositivo se quedó sin memory mientras analizada la base de datos. Puede ser demasiado grande para este dispositivo.</string>
<string name="error_pass_gen_type">Debe seleccionar al menos un tipo de generación de contraseñas</string>
<string name="error_pass_match">Las contraseñas no coinciden.</string>
@@ -87,7 +87,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201
<string name="error_rounds_too_large">Pasadas demasiado grande. Establecido a 2147483648.</string>
<string name="error_title_required">Se necesita un título.</string>
<string name="error_wrong_length">Introduzca un entero positivo en el campo longitud</string>
<string name="FileNotFound">Archivo no encontrado.</string>
<string name="file_not_found">Archivo no encontrado.</string>
<string name="file_browser">Explorador de Archivos</string>
<string name="generate_password">Generar Contraseña</string>
<string name="group">Grupo</string>
@@ -97,7 +97,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201
<string name="hint_group_name">Nombre de grupo</string>
<string name="hint_keyfile">archivo clave</string>
<string name="hint_length">longitud</string>
<string name="hint_login_pass">Contraseña</string>
<string name="password">Contraseña</string>
<string name="hint_pass">contraseña</string>
<string name="hint_title">nombre</string>
<string name="hint_url">url</string>
@@ -139,7 +139,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201
<string name="no_url_handler">Sin manejador para esta url.</string>
<string name="recentfile_title">Historial de archivos recientes</string>
<string name="recentfile_summary">Recordar nombres de archivos usados recientemente</string>
<string name="open_recent">Abrir base de datos reciente (clic para abrir):</string>
<string name="open_recent">Abrir base de datos reciente :</string>
<string name="omitbackup_title">No buscar en las entradas de copia de seguridad o papelera de reciclaje</string>
<string name="omitbackup_summary">Omitir \'Backup\' y grupo de Papelera de Reciclaje en los resultados de búsqueda</string>
<string name="pass_filename">Nombre de archivo de base de datos de KeePass:</string>

View File

@@ -55,7 +55,7 @@
<string name="decrypting_entry">Sarrera desenkriptatzen</string>
<string name="default_checkbox">Hau erabili modu lehenetsitako datubase gisa</string>
<string name="digits">Zenbakiak</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft ez dakar inolako bermerik; Lan hau software librea da; banatu edo/eta aldatu egin dezakezu GNU General Public License bigarren bertsioaren baldintzapean.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin ez dakar inolako bermerik; Lan hau software librea da; banatu edo/eta aldatu egin dezakezu GNU General Public License bigarren bertsioaren baldintzapean.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Datubasearen fitxategiaren izena sartu:</string>
<string name="entry_accessed">Akzesoa: </string>
@@ -65,7 +65,7 @@
<string name="entry_confpassword">Pasahitza berretsi:</string>
<string name="entry_created">Sortua: </string>
<string name="entry_expires">Iraungitzen da: </string>
<string name="entry_keyfile">Gako fitxategia (aukerazkoa)</string>
<string name="entry_keyfile">Gako fitxategia</string>
<string name="entry_modified">Aldatua: </string>
<string name="entry_not_found">Sarreraren datuak ez aurkituak.</string>
<string name="entry_password">Pasahitza:</string>
@@ -85,7 +85,7 @@
<string name="error_invalid_db">Datubase baliogabea.</string>
<string name="error_invalid_path">Fitxategirako bide baliogabea.</string>
<string name="error_no_name">Izen bat behar da.</string>
<string name="error_nopass">Pasahitz edo gako fitxategi bat beharrezkoak dira.</string>
<string name="error_nokeyfile">Pasahitz edo gako fitxategi bat beharrezkoak dira.</string>
<string name="error_out_of_memory">Telefonoa memoriarik gabe gelditu da zure datubasea arakatzean. Handiegia izan daiteke zure telefonorako.</string>
<string name="error_pass_gen_type">Pasahitza sortzeko mota bat gutxienez aukeratu behar da</string>
<string name="error_pass_match">Pasahitzak ez datoz bat.</string>
@@ -96,7 +96,7 @@
<string name="error_wrong_length">Eremuaren luzeran entero positibo bat sartu</string>
<string name="field_name">Eremuaren izena</string>
<string name="field_value">Eremuaren balorea</string>
<string name="FileNotFound">Fitxategi ez aurkitua.</string>
<string name="file_not_found">Fitxategi ez aurkitua.</string>
<string name="file_browser">Fitxategien nabigatzailea</string>
<string name="generate_password">Pasahitza sortu</string>
<string name="group">Taldea</string>
@@ -107,7 +107,7 @@
<string name="hint_keyfile">gako fitxategia</string>
<string name="hint_length">luzera</string>
<string name="hint_pass">pasahitza</string>
<string name="hint_login_pass">Pasahitza</string>
<string name="password">Pasahitza</string>
<string name="hint_title">izena</string>
<string name="hint_url">url</string>
<string name="hint_username">erabiltzaile izena</string>
@@ -149,7 +149,7 @@
<string name="no_keys">Sarrerarik ez datubasean edo taldean.</string>
<string name="no_results">Emaitzarik gabeko bilaketa</string>
<string name="no_url_handler">URL kudatzeko euskarririk ez.</string>
<string name="open_recent">Duela gutxiko datubasea ireki (klik irekitzeko):</string>
<string name="open_recent">Duela gutxiko datubasea ireki :</string>
<string name="omitbackup_title">Ez bilatu segurtasun kopiaren sarreretan</string>
<string name="omitbackup_summary">Kendu segurtasun kopien taldea bilaketen emaitzetatik (.kdb fitxategie dagokie bakarrik)</string>
<string name="pass_filename">Keepass datubasearen izena:</string>

View File

@@ -53,7 +53,7 @@
<string name="decrypting_entry">Puretaan salasanatietuetta</string>
<string name="default_checkbox">Käytä tätä oletustietokantana</string>
<string name="digits">Numerot</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under the conditions of the GPL version 2 or later.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under the conditions of the GPL version 2 or later.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Anna tietokannan tiedostonimi:</string>
<string name="entry_accessed">Käytetty: </string>
@@ -63,7 +63,7 @@
<string name="entry_confpassword">Vahvista salasana:</string>
<string name="entry_created">Luotu: </string>
<string name="entry_expires">Vanhenee: </string>
<string name="entry_keyfile">Avaintiedosto (valinnainen)</string>
<string name="entry_keyfile">Avaintiedosto</string>
<string name="entry_modified">Muokattu: </string>
<string name="entry_not_found">Tietueen tietoja ei löytynyt.</string>
<string name="entry_password">Salasana:</string>
@@ -83,7 +83,7 @@
<string name="error_invalid_db">Viallinen salasanatietokanta.</string>
<string name="error_invalid_path">Viallinen hakemistopolku.</string>
<string name="error_no_name">Nimi puuttuu.</string>
<string name="error_nopass">Salasana tai avaintiedosto puuttuu.</string>
<string name="error_nokeyfile">Salasana tai avaintiedosto puuttuu.</string>
<string name="error_out_of_memory">Puhelimesta loppui muisti salasanatietokantaa avatessa. Tietokanta voi olla liian suuri tälle puhelinmallille.</string>
<string name="error_pass_gen_type">Vähintään yksi salasanagenerointitapa täytyy olla valittuna.</string>
<string name="error_pass_match">Salasanat eivät täsmää.</string>
@@ -94,7 +94,7 @@
<string name="error_wrong_length">Syötä positiivinen kokonaisluku pituus-kenttään</string>
<string name="field_name">Kentän nimi</string>
<string name="field_value">Kentän arvo</string>
<string name="FileNotFound">Tiedostoa ei löydetty.</string>
<string name="file_not_found">Tiedostoa ei löydetty.</string>
<string name="file_browser">Tiedostoselain</string>
<string name="generate_password">Generoi salasana</string>
<string name="group">Ryhmä</string>
@@ -105,7 +105,7 @@
<string name="hint_keyfile">avaintiedosto</string>
<string name="hint_length">pituus</string>
<string name="hint_pass">salasana</string>
<string name="hint_login_pass">Salasana</string>
<string name="password">Salasana</string>
<string name="hint_title">nimi</string>
<string name="hint_url">url-osoite</string>
<string name="hint_username">käyttäjänimi</string>
@@ -147,7 +147,7 @@
<string name="no_keys">Ei tietueita tietokannassa tai ryhmässä.</string>
<string name="no_results">Ei hakutuloksia</string>
<string name="no_url_handler">Tälle URL:lle ei ole käsittelijää.</string>
<string name="open_recent">Avaa viimeisin salasanatietokanta (avaa klikkaamalla):</string>
<string name="open_recent">Avaa viimeisin salasanatietokanta :</string>
<string name="omitbackup_title">Älä etsi varmuuskopioista eikä roskakorista</string>
<string name="omitbackup_summary">Poista \'Varmuuskopiot\' ja roskakori hakutuloksista</string>
<string name="pass_filename">KeePass salasanatietokannan tiedostonimi:</string>

View File

@@ -20,8 +20,8 @@
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<string name="about_feedback">Signaler une anomalie\u00A0:</string>
<string name="about_homepage">Site web\u00A0:</string>
<string name="about_feedback">Signaler une anomalie :</string>
<string name="about_homepage">Site web :</string>
<string name="AboutText">KeePass DX est une implémentation sur Android du gestionnaire de mots de passe KeePass.</string>
<string name="accept">Accepter</string>
<string name="add_entry">Ajouter une entrée</string>
@@ -29,7 +29,7 @@
<string name="add_group_title">Ajouter un groupe</string>
<string name="add_string">Ajouter une chaîne</string>
<string name="algorithm">Algorithme</string>
<string name="algorithm_colon">Algorithme\u00A0:</string>
<string name="algorithm_colon">Algorithme :</string>
<string name="app_timeout">Application timeout</string>
<string name="app_timeout_summary">Temps avant le verrouillage de la base de données lorsque l\'application est inactive.</string>
<string name="application">Application</string>
@@ -42,31 +42,31 @@
<string name="cancel">Annuler</string>
<string name="ClearClipboard">Presse-papier vidé</string>
<string name="clipboard_error_title">Erreur de presse-papier</string>
<string name="clipboard_error">Certains appareils Android Samsung ont un bug dans l\'implémentation du presse-papier qui empêche la copie depuis des applications. Pour plus de détails, visitez\u00A0:</string>
<string name="clipboard_error">Certains appareils Android Samsung ont un bug dans l\'implémentation du presse-papier qui empêche la copie depuis des applications. Pour plus de détails, visitez :</string>
<string name="clipboard_error_clear">Le vidage du presse-papier a échoué</string>
<string name="clipboard_timeout">Clipboard timeout</string>
<string name="clipboard_timeout_summary">Temps avant le vidage du presse-papier après copie du nom d\'utilisateur ou du mot de passe</string>
<string name="copy_username">Copier le nom d\'utilisateur dans le presse-papier</string>
<string name="copy_password">Copier le mot de passe dans le presse-papier</string>
<string name="creating_db_key">Création de la clé de base de données…</string>
<string name="current_group">Groupe actuel\u00A0:</string>
<string name="current_group_root">Groupe actuel\u00A0: Racine</string>
<string name="current_group">Groupe actuel :</string>
<string name="current_group_root">Groupe actuel : Racine</string>
<string name="database">Base de données</string>
<string name="decrypting_db">Déchiffrement du contenu de la base de données…</string>
<string name="decrypting_entry">Déchiffrement de l\'entrée</string>
<string name="default_checkbox">Utiliser comme base de données par défaut</string>
<string name="digits">Nombres</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft n\'offre ABSOLUMENT AUCUNE GARANTIE; il s\'agit d\'un logiciel libre, vous pouvez le redistribuer sous les conditions de la licence GPL v2 ou ultérieure.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin n\'offre ABSOLUMENT AUCUNE GARANTIE; il s\'agit d\'un logiciel libre, vous pouvez le redistribuer sous les conditions de la licence GPL v2 ou ultérieure.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Sélectionnez la base de données\u00A0:</string>
<string name="enter_filename">Sélectionnez la base de données :</string>
<string name="entry_accessed">Dernier accès</string>
<string name="entry_and_or">Entrez un mot de passe et/ou un fichier de clé pour ouvrir la base de données\u00A0:</string>
<string name="entry_and_or">Entrez un mot de passe et/ou un fichier de clé pour ouvrir la base de données :</string>
<string name="entry_cancel">Annuler</string>
<string name="entry_comment">Commentaires</string>
<string name="entry_confpassword">Confirmer mot de passe</string>
<string name="entry_created">Créé</string>
<string name="entry_expires">Expire</string>
<string name="entry_keyfile">Clé (facultative)</string>
<string name="entry_keyfile">Fichier clé</string>
<string name="entry_modified">Modifié</string>
<string name="entry_not_found">Entry data not found.</string>
<string name="entry_password">Mot de passe</string>
@@ -82,22 +82,22 @@
<string name="error_database_settings">Impossible de déterminer les paramètres de la base de données.</string>
<string name="error_failed_to_launch_link">Échec lors de l\'ouverture du lien.</string>
<string name="error_filename_required">Le nom de fichier est obligatoire.</string>
<string name="error_file_not_create">Impossible de créer le fichier\u00A0:</string>
<string name="error_file_not_create">Impossible de créer le fichier :</string>
<string name="error_invalid_db">Base de données invalide.</string>
<string name="error_invalid_path">Chemin invalide.</string>
<string name="error_no_name">Le nom est obligatoire.</string>
<string name="error_nopass">Un mot de passe ou clé de fichier est requis.</string>
<string name="error_nokeyfile">Un fichier clé est requis.</string>
<string name="error_out_of_memory">Votre appareil ne dispose pas de suffisamment de mémoire pour ouvrir votre base de données. Elle est probablement trop grosse pour votre appareil.</string>
<string name="error_pass_gen_type">Au moins un type de génération de mot de passe doit être sélectionné</string>
<string name="error_pass_match">Les mots de passe ne correspondent pas.</string>
<string name="error_rounds_not_number">La série doit être un nombre.</string>
<string name="error_rounds_too_large">Rounds too big. Setting to 2147483648.</string>
<string name="error_rounds_too_large">Niveau trop gros. Paramètres à 2147483648.</string>
<string name="error_string_key">A field name is required for each string.</string>
<string name="error_title_required">Le titre est obligatoire.</string>
<string name="error_wrong_length">Entrez un entier positif pour la longueur du champ</string>
<string name="field_name">Nom du champ</string>
<string name="field_value">Valeur</string>
<string name="FileNotFound">Fichier non trouvé.</string>
<string name="file_not_found">Fichier non trouvé.</string>
<string name="file_not_found_content">Fichier non trouvé. Essayer de l\'ouvrir à nouveau à partir de votre fournisseur de contenu.</string>
<string name="file_browser">Navigateur de fichiers</string>
<string name="generate_password">Générer mot de passe</string>
@@ -109,7 +109,7 @@
<string name="hint_keyfile">clé de fichier</string>
<string name="hint_length">longueur</string>
<string name="hint_pass">mot de passe</string>
<string name="hint_login_pass">Mot de passe</string>
<string name="password">Mot de passe</string>
<string name="hint_title">nom</string>
<string name="hint_url">URL</string>
<string name="hint_username">nom d\'utilisateur</string>
@@ -121,8 +121,8 @@
<string name="keyfile_does_not_exist">Le fichier de clé n\'existe pas.</string>
<string name="keyfile_is_empty">Le fichier de clé est vide.</string>
<string name="length">Longueur</string>
<string name="list_size_title">Taille de la liste des groupes</string>
<string name="list_size_summary">Taille de la police de caractères utilisée pour la liste des groupes</string>
<string name="list_size_title">Taille des éléments de liste</string>
<string name="list_size_summary">Taille de la police de caractères utilisée pour les éléments de liste</string>
<string name="loading_database">Ouverture de la base de données…</string>
<string name="lowercase">Minuscule</string>
<string name="MaskedPassword">*****</string>
@@ -151,7 +151,7 @@
<string name="no_keys">Aucun élément.</string>
<string name="no_results">Aucun résultat pour cette recherche.</string>
<string name="no_url_handler">Impossible d\'ouvrir cette URL.</string>
<string name="open_recent">Bases de données utilisées récemment\u00A0:</string>
<string name="open_recent">Bases de données utilisées récemment :</string>
<string name="omitbackup_title">Ignorer les sauvegardes</string>
<string name="omitbackup_summary">Ignorer le groupe Sauvegardes des résultats de recherche (uniquement pour .kdb)</string>
<string name="pass_filename">Fichier de base de données KeePass</string>
@@ -172,6 +172,9 @@
<string name="rounds">Niveau du chiffrement</string>
<string name="rounds_explaination">Un niveau de chiffrement supérieur assure une protection supplémentaire contre les attaques de force brute, mais peut considérablement ralentir l\'ouverture et l\'enregistrement.</string>
<string name="rounds_hint">niveaux</string>
<string name="rounds_fix_title">Résolution de la base de données</string>
<string name="rounds_fix">Clé de niveau de chiffrement avant corruption</string>
<string name="rounds_fix_explanation">Si votre base de données a été corrompue par KeePassDroid version 2.2.0.0 à 2.2.0.6, entrez le niveau de chiffrement utilisé précédemment et cela vous permettra d\'ouvrir votre base.</string>
<string name="saving_database">Enregistrement de la base de données…</string>
<string name="space">Espace</string>
<string name="search_label">Rechercher</string>
@@ -191,14 +194,51 @@
<string name="warning_password_encoding">Le format .kdb ne supporte que le jeu de caractère Latin1. Votre mot de passe doit contenir des caractères en dehors de ce jeu. Tous les caractères non-Latin1 sont convertis en un même caractère, ce qui diminue la sécurité de votre mot de passe. Le changement de votre mot de passe est recommandé.</string>
<string name="warning_read_only">Votre carte SD est actuellement en lecture seule. Vous ne pourrez pas enregistrer les changements dans la base de données.</string>
<string name="warning_unmounted">Votre carte SD n\'est actuellement pas montée sur votre appareil. Vous ne pourrez pas charger ou créer votre base de données.</string>
<string name="version_label">Version\u00A0:</string>
<string name="warning_empty_password">Voulez-vous vraiment utiliser une chaine de caractères vide comme mot de passe ?</string>
<string name="warning_no_encryption_key">Etes-vous sûr de ne vouloir utiliser aucune clé de chiffrement.</string>
<string name="version_label">Version :</string>
<string name="configure_fingerprint">Reconnaissance d\'empreinte digitale non configuré pour cet appareil</string>
<string name="scanning_fingerprint">Attente d\'une reconnaissance d\'empreinte digitale</string>
<string name="encrypted_value_stored">Mot de passe encrypté stocké</string>
<string name="fingerprint_invalid_key">Problème de clé invalide</string>
<string name="fingerprint_not_recognized">Empreinte digitale non reconnue</string>
<string name="fingerprint_error">Problème d\'empreinte digitale</string>
<string name="store_with_fingerprint">Utiliser l\'empreinte digitale pour stocker le mot de passe</string>
<string name="no_password_stored">Pas de mot de passe encore stocké pour cette base de données</string>
<string name="history">Historique</string>
<string name="appearance">Apparence</string>
<string name="general">Générale</string>
<string name="password_size_title">Taille de mot de passe</string>
<string name="password_size_summary">Définir la taille par défaut du mot de passe généré</string>
<string name="list_password_generator_options_title">Caractères de mot de passe</string>
<string name="list_password_generator_options_summary">Définir les caractères par défaut du générateur de mot de passe</string>
<string name="clipboard_notifications_title">Notifications du presse-papiers</string>
<string name="clipboard_notifications_summary">Activer les notifications du presse-papiers pour copier le nom d\'utilisateur et le mot de passe</string>
<string name="lock_database_screen_off_title">Verrouillage d\'écran</string>
<string name="lock_database_screen_off_summary">Verrouiller la base de données quand l\'écran est éteint</string>
<string name="fingerprint_quick_unlock_title">Comment configurer l\'empreinte digitale pour un déverrouillage rapide?</string>
<string name="fingerprint_setting_text">Définissez votre empreinte digitale personnelle pour votre appareil dans </string>
<string name="fingerprint_setting_way_text">Paramètres -> Sécurité -> Empreinte digitale</string>
<string name="fingerprint_type_password_text">Tapez votre mot de passe dans Keepass DX</string>
<string name="fingerprint_scan_to_store">Scannez votre empreinte digitale pour stocker votre mot de passe maître en toute sécurité</string>
<string name="fingerprint_scan_to_open">Il suffit de scanner votre empreinte digitale dans Keepass DX lorsque le champ mot de passe est vide pour ouvrir la base de données</string>
<string name="usage">Usage</string>
<string name="fingerprint">Empreinte digitale</string>
<string name="fingerprint_enable_title">Écoute d\'empreintes digitales</string>
<string name="fingerprint_enable_summary">Activer l\'ouverture de la base de données par empreinte digitale</string>
<string name="fingerprint_delete_all_title">Supprimer les clés de chiffrement</string>
<string name="fingerprint_delete_all_summary">Supprimer toutes les clés de chiffrement liées à la reconnaissance des empreintes digitales</string>
<string name="fingerprint_delete_all_warning">Êtes-vous sûr de vouloir supprimer toutes les clés liées aux empreintes digitales?</string>
<string name="unavailable_feature_text">Impossible de démarrer cette fonctionnalité.</string>
<string name="unavailable_feature_version">Votre version Android %1$d n\'est pas la version minimale %2$d requise.</string>
<string name="unavailable_feature_hardware">Le matériel n\'est pas détecté.</string>
<string name="file_name">Nom de fichier</string>
<string name="path">Chemin</string>
<string name="assign_master_key">Assigner une clé maître</string>
<string name="create_keepass_file">Créer un fichier keepass</string>
<string name="bytes">Octets</string>
<string name="full_file_path_enable_title">Chemin de fichier</string>
<string name="full_file_path_enable_summary">Afficher le chemin complet des fichiers</string>
<string-array name="clipboard_timeout_options">
<item>30 secondes</item>
@@ -218,7 +258,5 @@
<item>Light Theme</item>
<item>Night Theme</item>
</string-array>
<string name="history">Historique</string>
<string name="appearance">Apparence</string>
<string name="general">Générale</string>
</resources>

View File

@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2009-2016 Brian Pellin.
This file is part of KeePassDroid.
KeePassDroid is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
KeePassDroid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="about_feedback">Visszajelzés:</string>
<string name="about_homepage">Weboldal:</string>
@@ -35,7 +50,7 @@
<string name="decrypting_entry">Bejegyzés dekódolása</string>
<string name="default_checkbox">Adatbázis beállítása alapértelmezettként</string>
<string name="digits">Számok</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. Ehhez a programhoz SEMMILYEN GARANCIA NEM JÁR; Ez egy szabad szoftver, GNU General Public License v2 vagy későbbi verziójának feltételei mellett terjeszthető, illetve módosítható. Fordította: intel</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin. Ehhez a programhoz SEMMILYEN GARANCIA NEM JÁR; Ez egy szabad szoftver, GNU General Public License v2 vagy későbbi verziójának feltételei mellett terjeszthető, illetve módosítható. Fordította: Eversmann</string>
<string name="ellipsis"></string>
<string name="enter_filename">Adja meg az adatbázis fájlnevét:</string>
<string name="entry_accessed">Utolsó hozzáférés:</string>
@@ -45,7 +60,7 @@
<string name="entry_confpassword">Jelszó megerősítése:</string>
<string name="entry_created">Létrehozva:</string>
<string name="entry_expires">Lejárat:</string>
<string name="entry_keyfile">Kulcsfájl (opcionális)</string>
<string name="entry_keyfile">Kulcsfájl</string>
<string name="entry_modified">Módosítva:</string>
<string name="entry_password">Jelszó:</string>
<string name="entry_save">Mentés</string>
@@ -63,19 +78,20 @@
<string name="error_file_not_create">Nem sikerült létrehozni a fájlt:</string>
<string name="error_invalid_db">Érvénytelen adatbázis.</string>
<string name="error_invalid_path">Érvénytelen útvonal.</string>
<string name="error_no_name">Egy névre van szükség.</string>
<string name="error_nopass">Jelszóra vagy kulcsfájlra van szükség.</string>
<string name="error_no_name">Név szükséges.</string>
<string name="error_nokeyfile">Jelszóra vagy kulcsfájlra van szükség.</string>
<string name="error_out_of_memory">A telefon memóriája megtelt az adatbázis feldolgozása közben. Lehet túl sok ez a telefonnak.</string>
<string name="error_pass_gen_type">Legalább egy jelszógenerálási típust kell választania</string>
<string name="error_pass_match">A jelszavak nem egyeznek meg.</string>
<string name="error_rounds_not_number">A mező csak számokat tartalmazhat.</string>
<string name="error_rounds_too_large">A menetek száma túl nagy. A maximális 2147483648.</string>
<string name="error_string_key">A megnevezés mező kitöltése kötelező.</string>
<string name="error_title_required">Cím szükséges.</string>
<string name="error_string_key">Az érték mező kitöltése kötelező.</string>
<string name="error_title_required">Név szükséges.</string>
<string name="error_wrong_length">Írjon be egy pozitív egész számot a hossz mezőbe</string>
<string name="field_name">Megnevezés</string>
<string name="field_value">Érték</string>
<string name="FileNotFound">A fájl nem található.</string>
<string name="file_not_found">A fájl nem található</string>
<string name="file_not_found_content">A fájl nem található. Próbálja meg újra megnyitni.</string>
<string name="file_browser">Fájlkezelő</string>
<string name="generate_password">Jelszó generálás</string>
<string name="group">Csoport</string>
@@ -86,7 +102,7 @@
<string name="hint_keyfile">kulcsfájl</string>
<string name="hint_length">hosszúság</string>
<string name="hint_pass">jelszó</string>
<string name="hint_login_pass">Jelszó</string>
<string name="password">Jelszó</string>
<string name="hint_title">név</string>
<string name="hint_url">url</string>
<string name="hint_username">felhasználónév</string>
@@ -114,7 +130,7 @@
<string name="menu_db_settings">Adatbázis beállítások</string>
<string name="menu_delete">Törlés</string>
<string name="menu_donate">Támogatás</string>
<string name="menu_edit">Szerkeszt</string>
<string name="menu_edit">Szerkesztés</string>
<string name="menu_hide_password">Jelszó elrejtése</string>
<string name="menu_homepage">Weboldal megtekintése</string>
<string name="menu_lock">Adatbázis lezárása</string>
@@ -128,14 +144,19 @@
<string name="no_keys">Nincs bejegyzés az adatbázisban vagy csoportban.</string>
<string name="no_results">Nincs találat</string>
<string name="no_url_handler">Nem található kezelő ehhez az url-hez.</string>
<string name="open_recent">Korábbi adatbázis megnyitása (kattintson rá a megnyitáshoz):</string>
<string name="open_recent">Korábbi adatbázis megnyitása :</string>
<string name="omitbackup_title">Keresési kivételek</string>
<string name="omitbackup_summary">A Backup és Lomtár csoportok kihagyása a keresésből</string>
<string name="pass_filename">KeePass adatbázis fájlnév:</string>
<string name="password_title">Adatbázis jelszó megadása</string>
<string name="password_title">Adatbázis jelszó</string>
<string name="progress_create">Új adatbázis létrehozása…</string>
<string name="progress_title">Feldolgozás…</string>
<string name="protection">Memórián belüli védelem</string>
<string name="read_only">Csak olvasható</string>
<string name="read_only_warning">A programnak nincs engedélye az adatbázis írásához a jelenlegi helyén, ezért a megnyitása után csak olvasható lesz.</string>
<string name="read_only_kitkat_warning">A KitKat-es android verziótól kezdődően a futó alkalmazásoknak nincs jogosultságuk írni a külső SD kártyára.</string>
<string name="recentfile_title">Adatbázis mentése</string>
<string name="recentfile_summary">Jegyezze meg az adatbázisok helyét</string>
<string name="remember_keyfile_summary">Jegyezze meg a kulcsfájlok helyét</string>
<string name="remember_keyfile_title">Kulcsfájl mentése</string>
<string name="remove_from_filelist">Eltávolítás</string>
@@ -151,15 +172,26 @@
<string name="sort_name">Rendezés név alapján</string>
<string name="sort_db">Rendezés dátum alapján</string>
<string name="special">Speciális</string>
<string name="search_hint">Bejegyzés cím/leírás</string>
<string name="search_hint">Bejegyzés név/leírás</string>
<string name="search_results">Keresés eredménye</string>
<string name="twofish">Twofish</string>
<string name="underline">Aláhúzás</string>
<string name="unsupported_db_version">Nem támogatott adatbázis.</string>
<string name="uppercase">Nagybetűk</string>
<string name="use_saf_summary">Használja az Android Storage Access Framework-öt a fájlok böngészéséhez (KitKat vagy későbbi)</string>
<string name="use_saf_title">Storage Access Framework</string>
<string name="warning">Figyelmeztetés</string>
<string name="warning_password_encoding">A .kdb formátum csak a Latin1 karakterkészletet támogatja. A jelenlegi jelszó olyan karaktereket is tartalmazhat, amik ezen kívül esnek. Az összes ilyen karakter át lesz lesz konvertálva, viszont ez nagyban csökkenteni fogja a jelszó erősségét. Mielőbbi megváltoztatása ajánlott.</string>
<string name="warning_read_only">A memóriakártya jelenleg csak olvasható. Lehet, hogy nem tudja menteni a módosításokat az adatbázisban.</string>
<string name="warning_unmounted">A memóriakártya jelenleg el van távolítva. Nem fogja tudni az adatbázist betölteni vagy módosítani.</string>
<string name="version_label">Verzió:</string>
<string name="configure_fingerprint">Az ujjlenyomat használat nincs még beállítva az eszközön</string>
<string name="scanning_fingerprint">Várakozás az ujjelnyomat megadására</string>
<string name="encrypted_value_stored">Titkosított jelszó elmentve</string>
<string name="fingerprint_invalid_key">Érvénytelen kulcs</string>
<string name="fingerprint_error">Érvénytelen ujjlenyomat</string>
<string name="store_with_fingerprint">Használjon ujjlenyomatot a jelszó elmentéséhez</string>
<string name="no_password_stored">Nincs még jelszó beállítva ehhez az adatbázishoz</string>
<string-array name="clipboard_timeout_options">
<item>30 másodperc</item>
@@ -172,4 +204,4 @@
<item>Közepes</item>
<item>Nagy</item>
</string-array>
</resources>
</resources>

View File

@@ -51,7 +51,7 @@
<string name="decrypting_entry">Decodifica valore</string>
<string name="default_checkbox">Usa come database predefinito</string>
<string name="digits">Cifre</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft viene distribuito ASSOLUTAMENTE con NESSUNA GARANZIA; Si tratta di software libero, e sei invitato a distribuirlo sotto le condizioni della licenza GPL versione 2 o superiore.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin viene distribuito ASSOLUTAMENTE con NESSUNA GARANZIA; Si tratta di software libero, e sei invitato a distribuirlo sotto le condizioni della licenza GPL versione 2 o superiore.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Inserisci il nome file del database:</string>
<string name="entry_accessed">Ultimo accesso: </string>
@@ -61,7 +61,7 @@
<string name="entry_confpassword">Conferma password:</string>
<string name="entry_created">Creato: </string>
<string name="entry_expires">Scade: </string>
<string name="entry_keyfile">File chiave (opzionale)</string>
<string name="entry_keyfile">File chiave</string>
<string name="entry_modified">Modificato: </string>
<string name="entry_password">Password</string>
<string name="entry_save">Salva</string>
@@ -80,7 +80,7 @@
<string name="error_invalid_db">Database non valido.</string>
<string name="error_invalid_path">Percorso non valido.</string>
<string name="error_no_name">E\' necessario un nome.</string>
<string name="error_nopass">E\' necessaria una password o un file chiave.</string>
<string name="error_nokeyfile">E\' necessaria una password o un file chiave.</string>
<string name="error_out_of_memory">Il telefono ha esaurito la memoria durante l\'elaborazione del database. Potrebbe essere troppo grande per il tuo telefono.</string>
<string name="error_pass_gen_type">Almeno un tipo di generazione password deve essere selezionato</string>
<string name="error_pass_match">Le password non corrispondono.</string>
@@ -88,7 +88,7 @@
<string name="error_rounds_too_large">Livello troppo grande. Impostato a 2147483648.</string>
<string name="error_title_required">E\' necessario un titolo.</string>
<string name="error_wrong_length">Inserisci un valore positivo nella lunghezza campo</string>
<string name="FileNotFound">File non trovato.</string>
<string name="file_not_found">File non trovato.</string>
<string name="file_browser">Sfoglia file</string>
<string name="generate_password">Genera password</string>
<string name="group">Gruppo</string>
@@ -98,7 +98,7 @@
<string name="hint_group_name">Nome gruppo</string>
<string name="hint_keyfile">file chiave</string>
<string name="hint_length">lunghezza</string>
<string name="hint_login_pass">Password</string>
<string name="password">Password</string>
<string name="hint_pass">password</string>
<string name="hint_title">nome</string>
<string name="hint_url">url</string>
@@ -138,7 +138,7 @@
<string name="no_keys">Nessuna voce nel database o nel gruppo.</string>
<string name="no_results">Nessun risultato di ricerca</string>
<string name="no_url_handler">Nessun gestore per questo URL.</string>
<string name="open_recent">Apri un database recente (clicca per aprire):</string>
<string name="open_recent">Apri un database recente :</string>
<string name="omitbackup_title">Non cercare voci di backup e nel Cestino</string>
<string name="omitbackup_summary">Ometti i gruppi \'Backup\' e Cestino dai risultati di ricerca</string>
<string name="pass_filename">Nome file del database di KeePass:</string>

View File

@@ -62,7 +62,7 @@
<string name="entry_confpassword">אשר סיסמה:</string>
<string name="entry_created">תאריך יצירה: </string>
<string name="entry_expires">פג תוקף: </string>
<string name="entry_keyfile">קובץ מפתח (רשות)</string>
<string name="entry_keyfile">קובץ מפתח</string>
<string name="entry_modified">נערך לאחרונה: </string>
<string name="entry_not_found">נתוני ערך לא נמצאו.</string>
<string name="entry_password">סיסמה:</string>
@@ -81,7 +81,7 @@
<string name="error_invalid_db">מסד נתונים לא חוקי.</string>
<string name="error_invalid_path">נתיב לא חוקי.</string>
<string name="error_no_name">שם נדרש.</string>
<string name="error_nopass">סיסמה או קובץ מפתח נדרשים.</string>
<string name="error_nokeyfile">סיסמה או קובץ מפתח נדרשים.</string>
<string name="error_out_of_memory">זיכרון המכשיר אזל בזמן ניתוח מסד הנתונים. יתכן והוא גדול מדי למכשירך.</string>
<string name="error_pass_gen_type">לפחות סוג אחד ליצירת סיסמה צריך להיבחר</string>
<string name="error_pass_match">הסיסמאות לא תואמות.</string>
@@ -92,7 +92,7 @@
<string name="error_wrong_length">הזן מספר חיובי בשדה האורך</string>
<string name="field_name">שם השדה</string>
<string name="field_value">ערך השדה</string>
<string name="FileNotFound">קובץ לא נמצא.</string>
<string name="file_not_found">קובץ לא נמצא.</string>
<string name="file_browser">סייר קבצים</string>
<string name="generate_password">צור סיסמה</string>
<string name="group">קבוצה</string>
@@ -103,7 +103,7 @@
<string name="hint_keyfile">קובץ המפתח</string>
<string name="hint_length">אורך</string>
<string name="hint_pass">סיסמה</string>
<string name="hint_login_pass">סיסמה</string>
<string name="password">סיסמה</string>
<string name="hint_title">שם</string>
<string name="hint_username">שם משתמש</string>
<string name="install_from_market">התקן מחנות Play</string>
@@ -143,7 +143,7 @@
<string name="no_keys">אין ערכים במסד נתונים או בקבוצה.</string>
<string name="no_results">אין תוצאות חיפוש</string>
<string name="no_url_handler">אין מטפל לכתובת url זו.</string>
<string name="open_recent">פתח מסד נתונים אחרון (לחץ לפתיחה):</string>
<string name="open_recent">פתח מסד נתונים אחרון :</string>
<string name="omitbackup_title">אל תחפש ערכי גיבוי</string>
<string name="omitbackup_summary">הורד את קבוצת \"גיבוי\" מתוצאות חיפוש (תואם רק לקבצי kdb)</string>
<string name="pass_filename">שם קובץ למסד נתוני KeePass:</string>

View File

@@ -48,7 +48,7 @@
<string name="decrypting_entry">暗号解除エントリー</string>
<string name="default_checkbox">このデータベースを次回以降も利用する</string>
<string name="digits">数字</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft によって作られたフリーソフトウェアであり、無保証です。GPLバージョン2以上の条件下でこれを再頒布することができます。</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin によって作られたフリーソフトウェアであり、無保証です。GPLバージョン2以上の条件下でこれを再頒布することができます。</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">データベースファイル:</string>
<string name="entry_accessed">最終アクセス日: </string>
@@ -58,7 +58,7 @@
<string name="entry_confpassword">パスワードの確認:</string>
<string name="entry_created">作成日: </string>
<string name="entry_expires">有効期限: </string>
<string name="entry_keyfile">キーファイル(オプション)</string>
<string name="entry_keyfile">キーファイル</string>
<string name="entry_modified">更新日: </string>
<string name="entry_password">パスワード:</string>
<string name="entry_save">保存</string>
@@ -77,7 +77,7 @@
<string name="error_invalid_db">無効なデータベースです。</string>
<string name="error_invalid_path">パスが無効です。</string>
<string name="error_no_name">タイトルは必須入力です。</string>
<string name="error_nopass">パスワードかキーファイルが必要です。</string>
<string name="error_nokeyfile">パスワードかキーファイルが必要です。</string>
<string name="error_out_of_memory">データベース解析中にメモリ不足になりました。</string>
<string name="error_pass_gen_type">少なくとも1つ以上のパスワード生成タイプを選択する必要があります。</string>
<string name="error_pass_match">パスワードが一致しません</string>
@@ -85,7 +85,7 @@
<string name="error_rounds_too_large">値が大きすぎます。 2147483648にセットしました。</string>
<string name="error_title_required">タイトルは必須入力です。</string>
<string name="error_wrong_length">\"長さ\"欄には正の整数を入力してください。</string>
<string name="FileNotFound">ファイルが見つかりません。</string>
<string name="file_not_found">ファイルが見つかりません。</string>
<string name="file_browser">ファイルブラウザ</string>
<string name="generate_password">パスワードを生成する</string>
<string name="group">グループ</string>
@@ -95,7 +95,7 @@
<string name="hint_group_name">グループ名</string>
<string name="hint_keyfile">キーファイル</string>
<string name="hint_length">長さ</string>
<string name="hint_login_pass">パスワード</string>
<string name="password">パスワード</string>
<string name="hint_pass">パスワード</string>
<string name="hint_title">タイトル</string>
<string name="hint_url">url</string>
@@ -135,7 +135,7 @@
<string name="no_keys">データベース・グループがありません。</string>
<string name="no_results">検索結果に該当するものはありません。</string>
<string name="no_url_handler">このuriを処理できません。</string>
<string name="open_recent">以前使用したデータベースを開く(クリック):</string>
<string name="open_recent">以前使用したデータベースを開く:</string>
<string name="omitbackup_title">検索対象から除外</string>
<string name="omitbackup_summary">\"バックアップ\"と\"ごみ箱\"を検索対象から除外します</string>
<string name="pass_filename">KeePassデータベースファイル:</string>

View File

@@ -2,7 +2,7 @@
<resources>
<string name="AboutText">KeePass DX yra KeePass slaptažodžių tvarkyklės realizacija Android platformai</string>
<string name="ClearClipboard">Iškarpinė išvalyta.</string>
<string name="FileNotFound">Failas nerastas.</string>
<string name="file_not_found">Failas nerastas.</string>
<string name="InvalidPassword">Neteisingas slaptažodis arba rakto failas.</string>
<string name="MaskedPassword">*****</string>
<string name="about_feedback">Atsiliepimai:</string>
@@ -44,7 +44,7 @@
<string name="hint_group_name">Grupės pavadinimas</string>
<string name="hint_keyfile">rakto failas</string>
<string name="hint_length">ilgis</string>
<string name="hint_login_pass">Slaptažodis</string>
<string name="password">Slaptažodis</string>
<string name="hint_pass">slaptažodis</string>
<string name="hint_title">pavadinimas</string>
<string name="hint_url">url</string>
@@ -102,7 +102,7 @@
<string name="entry_not_found">Įrašo duomenys nerasti.</string>
<string name="keyfile_is_empty">Rakto failas yra tuščias.</string>
<string name="keyfile_does_not_exist">Rakto failas neegzistuoja.</string>
<string name="entry_keyfile">Rakto failas (pasirinktinis)</string>
<string name="entry_keyfile">Rakto failas</string>
<string name="search_hint">Įrašo pavadinimas/aprašymas</string>
<string name="menu_change_key">Pakeisti master raktą</string>
<string name="entry_accessed">Naudota:</string>
@@ -113,7 +113,7 @@
<string name="lowercase">Mažosios raidės</string>
<string name="minus">Minusas</string>
<string name="underline">Pabraukimas</string>
<string name="open_recent">Atidaryti naujausią duomenų bazę (spausti)</string>
<string name="open_recent">Atidaryti naujausią duomenų bazę </string>
<string name="default_checkbox">Naudoti šią duomenų bazę kaip numatytąją</string>
<string name="maskpass_summary">Slėpti slaptažodžius pagal nutylėjimą</string>
<string name="menu_homepage">Eiti į pagrindinį puslapį</string>

View File

@@ -35,7 +35,6 @@
<string name="decrypting_entry">Ieraksta atšifrēšana</string>
<string name="default_checkbox">Izmantot šo kā manu noklusējuma datu bāzi</string>
<string name="digits">Cipari</string>
<string name="disclaimer_formal">Paroles Aurortiesības 20092012 Brian Pellin.</string>
<string name="ellipsis"></string>
<string name="enter_filename">Ievadiet datu bāzes nosaukumu:</string>
<string name="entry_accessed">Piekļuve:</string>
@@ -45,7 +44,7 @@
<string name="entry_confpassword">Apstipriniet paroli:</string>
<string name="entry_created">Izveidots:</string>
<string name="entry_expires">Derīguma termiņš beidzas:</string>
<string name="entry_keyfile">Atslēgas fails (pēc izvēles)</string>
<string name="entry_keyfile">Atslēgas fails</string>
<string name="entry_modified">Modificēts:</string>
<string name="entry_password">Parole:</string>
<string name="entry_save">Saglabāt</string>
@@ -64,7 +63,7 @@
<string name="error_invalid_db">Nederīga datu bāze.</string>
<string name="error_invalid_path">Nederīgs ceļš.</string>
<string name="error_no_name">Vajag ievadīt faila nosaukumu</string>
<string name="error_nopass">Vajadzīga parole vai atslēgas fails.</string>
<string name="error_nokeyfile">Vajadzīga parole vai atslēgas fails.</string>
<string name="error_out_of_memory">Darbam ar datu bāzi, tālrunī nepietiek atmiņas.</string>
<string name="error_pass_gen_type">Ir jāatlasa vismaz viens paroles ģenerēšanas tips</string>
<string name="error_pass_match">Paroles nesakrīt.</string>
@@ -75,7 +74,7 @@
<string name="error_wrong_length">Norādiet garumu lielāku par nulli</string>
<string name="field_name">Lauka nosaukums</string>
<string name="field_value">Lauka vērtība</string>
<string name="FileNotFound">Fails nav atrasts.</string>
<string name="file_not_found">Fails nav atrasts.</string>
<string name="file_browser">Failu pārlūks</string>
<string name="generate_password">Ģenerēt Paroli</string>
<string name="group">Grupa</string>
@@ -86,7 +85,7 @@
<string name="hint_keyfile">atslēgas fails</string>
<string name="hint_length">garums</string>
<string name="hint_pass">parole</string>
<string name="hint_login_pass">Parole</string>
<string name="password">Parole</string>
<string name="hint_title">vārds</string>
<string name="hint_url">url</string>
<string name="hint_username">lietotājvārds</string>
@@ -128,7 +127,7 @@
<string name="no_keys">Nav ierakstu datu bāzē vai grupā.</string>
<string name="no_results">Nav meklēšanas rezultātu</string>
<string name="no_url_handler">Neizdevās atvērt saiti.</string>
<string name="open_recent">Atvērt pēdējo datu bāzi (noklikšķiniet, lai atvērtu):</string>
<string name="open_recent">Atvērt pēdējo datu bāzi :</string>
<string name="omitbackup_title">Nemeklēt kopijās un atkritnē</string>
<string name="omitbackup_summary">Izlaist kopijas un atkritni no meklēšanas rezultātiem</string>
<string name="pass_filename">KeePass datu bāzes faila nosaukums:</string>

View File

@@ -50,7 +50,7 @@
<string name="decrypting_entry">Record wordt ontcijferd</string>
<string name="default_checkbox">Gebruik dit als mijn standaard database</string>
<string name="digits">Cijfers</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft biedt GEEN ENKELE GARANTIE; Dit is vrije software, u mag deze software verspreiden onder de voorwaarden van de GPL versie 2 of recenter.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin biedt GEEN ENKELE GARANTIE; Dit is vrije software, u mag deze software verspreiden onder de voorwaarden van de GPL versie 2 of recenter.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Geef de databasebestandsnaam:</string>
<string name="entry_accessed">Laatst benaderd: </string>
@@ -60,7 +60,7 @@
<string name="entry_confpassword">Bevestig wachtwoord:</string>
<string name="entry_created">Aangemaakt op: </string>
<string name="entry_expires">Verloopt op: </string>
<string name="entry_keyfile">Sleutelbestand (optioneel)</string>
<string name="entry_keyfile">Sleutelbestand</string>
<string name="entry_modified">Gewijzigd op: </string>
<string name="entry_password">Wachtwoord:</string>
<string name="entry_save">Opslaan</string>
@@ -79,7 +79,7 @@
<string name="error_invalid_db">Ongeldige database.</string>
<string name="error_invalid_path">Ongeldige padnaam.</string>
<string name="error_no_name">Een naam ontbreekt.</string>
<string name="error_nopass">Een wachtwoord of sleutenbestand ontbreekt.</string>
<string name="error_nokeyfile">Een wachtwoord of sleutenbestand ontbreekt.</string>
<string name="error_out_of_memory">Onvoldoende vrij geheugen om de database te lezen. Misschien is de database te groot voor deze telefoon.</string>
<string name="error_pass_gen_type">U moet minstens één type van wachtwoordgeneratie kiezen.</string>
<string name="error_pass_match">Wachtwoorden zijn niet gelijk.</string>
@@ -87,7 +87,7 @@
<string name="error_rounds_too_large">Cycli-waarde te groot. Ik gebruik 2147483648.</string>
<string name="error_title_required">Een titel ontbreekt.</string>
<string name="error_wrong_length">Geef een positief geheel getal voor het lengteveld</string>
<string name="FileNotFound">Bestand niet gevonden.</string>
<string name="file_not_found">Bestand niet gevonden.</string>
<string name="file_browser">Verkenner</string>
<string name="generate_password">Genereer wachtwoord</string>
<string name="group">Groep</string>
@@ -97,7 +97,7 @@
<string name="hint_group_name">Groepnaam</string>
<string name="hint_keyfile">sleutelbestand</string>
<string name="hint_length">lengte</string>
<string name="hint_login_pass">Wachtwoord</string>
<string name="password">Wachtwoord</string>
<string name="hint_pass">wachtwoord</string>
<string name="hint_title">naam</string>
<string name="hint_url">url</string>
@@ -137,7 +137,7 @@
<string name="no_keys">Geen records in de database of groep.</string>
<string name="no_results">Geen zoekresultaten</string>
<string name="no_url_handler">Geen afhandeling voor deze url mogelijk.</string>
<string name="open_recent">Recente database openen (klik om te openen):</string>
<string name="open_recent">Recente database openen :</string>
<string name="omitbackup_title">Niet in backup en prullenbak zoeken</string>
<string name="omitbackup_summary">Laat \'Backup\' en Prullenbak-groep weg uit zoekresultaten</string>
<string name="pass_filename">KeePass databasebestandsnaam:</string>

View File

@@ -48,7 +48,7 @@
<string name="decrypting_entry">Dekrypterer oppføringa</string>
<string name="default_checkbox">Standarddatabasen</string>
<string name="digits">Tal</string>
<string name="disclaimer_formal">KeePass DX Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft kjem med INGEN SOM HELST GARANTI. Dette er eit fritt program. Du er velkomen til å redistribuera det i samsvar med vilkåra til GPL utgåve 2 eller nyare.</string>
<string name="disclaimer_formal">KeePass DX \u00A9 %1$d Jeremy Jamet / Kunzisoft, Brian Pellin kjem med INGEN SOM HELST GARANTI. Dette er eit fritt program. Du er velkomen til å redistribuera det i samsvar med vilkåra til GPL utgåve 2 eller nyare.</string>
<string name="ellipsis">\u2026</string>
<string name="enter_filename">Skriv filnamnet til databasen:</string>
<string name="entry_accessed">Brukt: </string>
@@ -58,7 +58,7 @@
<string name="entry_confpassword">Stadfest passordet:</string>
<string name="entry_created">Laga: </string>
<string name="entry_expires">Går ut: </string>
<string name="entry_keyfile">Nøkkelfil (valfri)</string>
<string name="entry_keyfile">Nøkkelfil</string>
<string name="entry_modified">Endra: </string>
<string name="entry_password">Passord:</string>
<string name="entry_save">Lagra</string>
@@ -77,7 +77,7 @@
<string name="error_invalid_db">Ugyldig database.</string>
<string name="error_invalid_path">Ugyldig stig.</string>
<string name="error_no_name">Treng eit namn.</string>
<string name="error_nopass">Treng eit passord eller ei nøkkelfil.</string>
<string name="error_nokeyfile">Treng eit passord eller ei nøkkelfil.</string>
<string name="error_out_of_memory">Telefonen gjekk tom for minne ved lesinga av databasen din. Databasen er kanskje for stor.</string>
<string name="error_pass_gen_type">Du må velja minst éin passordlagingstype</string>
<string name="error_pass_match">Passorda samsvarer ikkje.</string>
@@ -85,7 +85,7 @@
<string name="error_rounds_too_large">For mange omgangar. Bruker 2147483648.</string>
<string name="error_title_required">Treng ein tittel.</string>
<string name="error_wrong_length">Bruk eit positivt heiltal i lengdfeltet</string>
<string name="FileNotFound">Fann ikkje fila.</string>
<string name="file_not_found">Fann ikkje fila.</string>
<string name="file_browser">Filbehandlar</string>
<string name="generate_password">Lag passord</string>
<string name="group">Gruppe</string>
@@ -95,7 +95,7 @@
<string name="hint_group_name">Gruppenamn</string>
<string name="hint_keyfile">nøkkelfil</string>
<string name="hint_length">lengd</string>
<string name="hint_login_pass">Passord</string>
<string name="password">Passord</string>
<string name="hint_pass">passord</string>
<string name="hint_title">namn</string>
<string name="hint_url">adresse</string>
@@ -135,7 +135,7 @@
<string name="no_keys">Ingen oppføringar i databasen eller gruppa.</string>
<string name="no_results">Ingen søkjeresultat</string>
<string name="no_url_handler">Ingen behandlar for denne adressa.</string>
<string name="open_recent">Opna nyleg brukt database (klikk for å opna):</string>
<string name="open_recent">Opna nyleg brukt database :</string>
<string name="omitbackup_title">Søk ikkje i kopipostane eller søppelbøtta</string>
<string name="omitbackup_summary">Søkjeresultatet inneheld ikkje oppføringar frå \'Backup\' eller søppelbøtta</string>
<string name="pass_filename">Filnamnet til KeePass-databasen:</string>
@@ -151,7 +151,7 @@
<string name="rounds_explaination">Fleire krypteringsomgangar gjev tilleggsvern mot rå makt-åtak, men kan òg gjera lasting og lagring mykje tregare.</string>
<string name="rounds_hint">omgangar</string>
<string name="saving_database">Lagrar databasen …</string>
<string name="space">Mellomrom</string>
<string name="space">Mellomrom</string>
<string name="search_label">Søk</string>
<string name="show_password">Syn passordet</string>
<string name="sort_name">Sorter etter namn</string>

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