mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Compare commits
146 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6574bd10a0 | ||
|
|
23f3335988 | ||
|
|
a5d7f33c82 | ||
|
|
3782c4dac0 | ||
|
|
1fc02fd2fe | ||
|
|
cc347c1dbe | ||
|
|
79ff20eb18 | ||
|
|
e6e8a447da | ||
|
|
233f0c5bdb | ||
|
|
9ed4271a14 | ||
|
|
470c0b6b43 | ||
|
|
afa8ae42b9 | ||
|
|
63f8826fd8 | ||
|
|
ef836e8b84 | ||
|
|
abc1c43a51 | ||
|
|
6b54dd9e0d | ||
|
|
1f54e7752d | ||
|
|
6ac941f276 | ||
|
|
6c308483f7 | ||
|
|
9d25fb74ec | ||
|
|
d217b52744 | ||
|
|
319da4b174 | ||
|
|
9bee467942 | ||
|
|
44ac70fc97 | ||
|
|
d897611d62 | ||
|
|
c2ae251e73 | ||
|
|
35ad285864 | ||
|
|
97bdae21eb | ||
|
|
d6dc6e43c7 | ||
|
|
01e6e530d5 | ||
|
|
9ec0178beb | ||
|
|
d66f2f6d24 | ||
|
|
79cd4004cc | ||
|
|
991243e2df | ||
|
|
b91cf11d86 | ||
|
|
d182ec09fa | ||
|
|
8641822358 | ||
|
|
9665cbb428 | ||
|
|
a280dfaf3b | ||
|
|
3e56521ea8 | ||
|
|
b205230ea9 | ||
|
|
51645ab126 | ||
|
|
5d04897e75 | ||
|
|
1ac0ea5cc6 | ||
|
|
a07e8b51e5 | ||
|
|
a81f0238f4 | ||
|
|
2b81eb8ec7 | ||
|
|
e5eb642781 | ||
|
|
a4cbe25733 | ||
|
|
2042c85b22 | ||
|
|
3149f8745c | ||
|
|
15b9f1616f | ||
|
|
94c02b7288 | ||
|
|
7e70b59a59 | ||
|
|
2c7f5e41ed | ||
|
|
108b8df280 | ||
|
|
553098f9be | ||
|
|
131eb78407 | ||
|
|
f956a279a5 | ||
|
|
7150686b92 | ||
|
|
4b1fb2c173 | ||
|
|
94464bf608 | ||
|
|
2faa88784a | ||
|
|
e6607b53d8 | ||
|
|
3f6a6c864a | ||
|
|
30a578257d | ||
|
|
8411134adf | ||
|
|
f86a5d1a19 | ||
|
|
be72492537 | ||
|
|
76f9e8ec6e | ||
|
|
8fb1c44e58 | ||
|
|
f607b35cf3 | ||
|
|
0e56bec35a | ||
|
|
c890d10114 | ||
|
|
dee2fe5ce7 | ||
|
|
4a4d767bce | ||
|
|
d57e0cf601 | ||
|
|
7fa141dd1b | ||
|
|
c261a0cbca | ||
|
|
66661cbd49 | ||
|
|
100c126c3d | ||
|
|
d466e3077d | ||
|
|
24587dc34e | ||
|
|
32cc57dd03 | ||
|
|
a55488846b | ||
|
|
dcf61fd4e2 | ||
|
|
5bf998468a | ||
|
|
01c9625c59 | ||
|
|
772c378922 | ||
|
|
ee50a91379 | ||
|
|
9cfda3bad8 | ||
|
|
aa19b08bd9 | ||
|
|
87f69bb7e2 | ||
|
|
41c0aeedbe | ||
|
|
3cbe53d76f | ||
|
|
aed60d6c1e | ||
|
|
be7d35490d | ||
|
|
d0ea997c63 | ||
|
|
1fecffeba2 | ||
|
|
76319a56a2 | ||
|
|
1d71de7031 | ||
|
|
e9a1cfea11 | ||
|
|
9115856d19 | ||
|
|
8c45266c18 | ||
|
|
6b4130df89 | ||
|
|
e3e10e7dfa | ||
|
|
cbf900004d | ||
|
|
51b36dc460 | ||
|
|
9f8016afe2 | ||
|
|
d5a36db50a | ||
|
|
ecd458d8d0 | ||
|
|
9088297c41 | ||
|
|
5282deb088 | ||
|
|
75d661f12b | ||
|
|
83bc769d9e | ||
|
|
8324acadc8 | ||
|
|
6e42db41be | ||
|
|
3917bfc9e6 | ||
|
|
d11febb1ce | ||
|
|
360eb6f9cc | ||
|
|
e4ac5d01d0 | ||
|
|
6a51fc0668 | ||
|
|
ba03f07fbe | ||
|
|
7bf7d63f64 | ||
|
|
d3efaabc24 | ||
|
|
b4283ed98b | ||
|
|
de407e4cf9 | ||
|
|
60ed3a9836 | ||
|
|
7948358d85 | ||
|
|
96b82bb9b2 | ||
|
|
699ccf13f0 | ||
|
|
ae88aa4e42 | ||
|
|
bcce13b12f | ||
|
|
d7d68ccdeb | ||
|
|
5cf6362db4 | ||
|
|
4efcc48160 | ||
|
|
383274ce0f | ||
|
|
c9dec3a2f7 | ||
|
|
2d4bf2903b | ||
|
|
2b88cfbda0 | ||
|
|
eb6ab7a156 | ||
|
|
688cbe50f2 | ||
|
|
e0577d1628 | ||
|
|
0f8dd17fde | ||
|
|
4bc8a08606 | ||
|
|
cf34433186 |
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -8,9 +8,11 @@ assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
@@ -18,9 +20,11 @@ Steps to reproduce the behavior:
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**KeePass Database**
|
||||
|
||||
- Created with: [e.g Windows KeePass 2.42]
|
||||
- Version: [e.g. 2]
|
||||
- Location: [e.g. Remote file retrieved with GDrive app]
|
||||
@@ -28,15 +32,18 @@ A clear and concise description of what you expected to happen.
|
||||
- Size: [e.g. 150Mo]
|
||||
- Contains attachment: [e.g. Yes]
|
||||
|
||||
**KeePassDX (please complete the following information):**
|
||||
**KeePassDX:**
|
||||
|
||||
- Version: [e.g. 2.5.0.0beta23]
|
||||
- Build: [e.g. Free]
|
||||
- Language: [e.g. French]
|
||||
|
||||
**Android (please complete the following information):**
|
||||
**Android:**
|
||||
|
||||
- Device: [e.g. GalaxyS8]
|
||||
- Version: [e.g. 8.1]
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context about the problem here.
|
||||
- Browser for Autofill: [e.g. Chrome version X]
|
||||
|
||||
24
CHANGELOG
24
CHANGELOG
@@ -1,3 +1,27 @@
|
||||
KeePassDX(3.4.1)
|
||||
* Fix search mode with Magikeyboard #1292
|
||||
* Fix select another entry with Magikeyboard #1293
|
||||
* Fix unexpected lock with Magikeyboard #1294
|
||||
* Small UI changes
|
||||
|
||||
KeePassDX(3.4.0)
|
||||
* Passphrase implementation #218
|
||||
* Show visual password strength indicator with entropy #631 #869 #454 #1270
|
||||
* Dynamically save password generator configuration #618 #696
|
||||
* Add advanced password filters #1052 #448 #983 #271 #539
|
||||
* Better search implementation #175 #1254 #1267
|
||||
* Manage package name from Magikeyboard #1010 #1261
|
||||
* Ask confirmation to lock if changes without save #970
|
||||
* Fix small bugs #1282
|
||||
|
||||
KeePassDX(3.3.3)
|
||||
* Fix shared otpauth link if database not open #1274
|
||||
* Ellipsize attachment name #1253
|
||||
* Add a warning to inform about KeyStore usage #1269
|
||||
* Fingerprint unlock no more by default #1273
|
||||
* Tabs to show main and advanced content separately
|
||||
* Fix URL color
|
||||
|
||||
KeePassDX(3.3.2)
|
||||
* Merge KeePassDX & KeePassDX Pro #1257
|
||||
* Create new Contributor Pro app
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Android KeePassDX
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/icon.png"> KeePassDX is a **multi-format KeePass manager for Android devices**. The app allows creating keys and passwords in a secure way by integrating with the Android design standards.
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/icon.png"> **Lightweight password manager for Android**, KeePassDX allows editing encrypted data in a single file in KeePass format and fill in the forms in a secure way.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/Kunzisoft/KeePassDX/master/art/screen.jpg" width="220">
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ android {
|
||||
applicationId "com.kunzisoft.keepass"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 31
|
||||
versionCode = 104
|
||||
versionName = "3.3.2"
|
||||
versionCode = 110
|
||||
versionName = "3.4.1"
|
||||
multiDexEnabled true
|
||||
|
||||
testApplicationId = "com.kunzisoft.keepass.tests"
|
||||
@@ -127,6 +127,8 @@ dependencies {
|
||||
// Apache Commons
|
||||
implementation 'commons-io:commons-io:2.8.0'
|
||||
implementation 'commons-codec:commons-codec:1.15'
|
||||
// Password generator
|
||||
implementation 'me.gosimple:nbvcxz:1.5.0'
|
||||
// Encrypt lib
|
||||
implementation project(path: ':crypto')
|
||||
// Icon pack
|
||||
|
||||
19
app/src/free/res/drawable/ic_app_white_24dp.xml
Normal file
19
app/src/free/res/drawable/ic_app_white_24dp.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="84"
|
||||
android:viewportHeight="84">
|
||||
<group
|
||||
android:translateX="-12"
|
||||
android:translateY="-12">
|
||||
<path
|
||||
android:fillColor="#ffa726"
|
||||
android:strokeWidth="1.99999297"
|
||||
android:pathData="M36,36 L36,40.2422 L67.7578,72 L72,72 L72,67.7578 L40.2422,36 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeWidth="1.99999297"
|
||||
android:pathData="M63.9961,34.0059 C61.5643,34.096,59.2564,35.102,57.5352,36.8223 C53.7682,40.589,53.7682,46.6982,57.5352,50.4649 C61.3017,54.232,67.4073,54.232,71.1739,50.4649 C74.9409,46.6982,74.9409,40.589,71.1739,36.8223 C69.2766,34.9258,66.6768,33.9054,63.9962,34.0059 Z M68.1992,40.6954 C69.8278,40.6958,71.148,42.016,71.1484,43.6446 C71.148,45.2732,69.8278,46.5934,68.1992,46.5938 C66.5706,46.5934,65.2504,45.2732,65.25,43.6446 C65.2504,42.016,66.5706,40.6958,68.1992,40.6954 Z M48.3438,55.4141 L36,67.7578 L36,72 L40.2422,72 L44.7578,67.4844 L44.7578,67.5 L49,67.5 L49,63.2578 L48.9844,63.2578 L49,63.2422 L49,63.2578 L53.2578,63.2578 L53.2578,60.3281 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
19
app/src/libre/res/drawable/ic_app_white_24dp.xml
Normal file
19
app/src/libre/res/drawable/ic_app_white_24dp.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="84"
|
||||
android:viewportHeight="84">
|
||||
<group
|
||||
android:translateX="-12"
|
||||
android:translateY="-12">
|
||||
<path
|
||||
android:fillColor="#ffa726"
|
||||
android:strokeWidth="1.99999297"
|
||||
android:pathData="M36,36 L36,40.2422 L67.7578,72 L72,72 L72,67.7578 L40.2422,36 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeWidth="1.99999297"
|
||||
android:pathData="M64.501,35.0576 C63.7095,35.0576,62.918,35.3613,62.3115,35.9678 L55.0127,43.2666 C53.7998,44.4795,53.7998,46.4306,55.0127,47.6436 L62.3115,54.9424 C63.5244,56.1553,65.4775,56.1553,66.6904,54.9424 L73.9873,47.6436 C75.2002,46.4307,75.2002,44.4796,73.9873,43.2666 L66.6904,35.9678 C66.0839,35.3613,65.2924,35.0576,64.5009,35.0576 Z M67.6729,42.6006 C69.3298,42.6006,70.6729,43.9437,70.6729,45.6006 C70.6729,47.2575,69.3298,48.6006,67.6729,48.6006 C66.016,48.6006,64.6729,47.2575,64.6729,45.6006 C64.6729,43.9437,66.016,42.6006,67.6729,42.6006 Z M48.3438,55.4141 L36,67.7578 L36,72 L40.2422,72 L44.7578,67.4844 L44.7578,67.5 L49,67.5 L49,63.2578 L48.9844,63.2578 L49,63.2422 L49,63.2578 L53.2578,63.2578 L53.2578,60.3281 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -131,6 +131,9 @@
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.activities.IconPickerActivity"
|
||||
android:configChanges="keyboardHidden" />
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.activities.KeyGeneratorActivity"
|
||||
android:configChanges="keyboardHidden" />
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.activities.ImageViewerActivity"
|
||||
android:configChanges="keyboardHidden" />
|
||||
@@ -169,9 +172,6 @@
|
||||
<data android:scheme="otpauth" android:host="hotp" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.activities.MagikeyboardLauncherActivity"
|
||||
android:theme="@style/Theme.Transparent" />
|
||||
<activity
|
||||
android:name="com.kunzisoft.keepass.settings.MagikeyboardSettingsActivity"
|
||||
android:label="@string/keyboard_setting_label"
|
||||
|
||||
@@ -44,6 +44,7 @@ import androidx.core.graphics.ColorUtils
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.fragments.EntryFragment
|
||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||
@@ -83,6 +84,7 @@ class EntryActivity : DatabaseLockActivity() {
|
||||
private var titleIconView: ImageView? = null
|
||||
private var historyView: View? = null
|
||||
private var tagsListView: RecyclerView? = null
|
||||
private var entryContentTab: TabLayout? = null
|
||||
private var tagsAdapter: TagsAdapter? = null
|
||||
private var entryProgress: LinearProgressIndicator? = null
|
||||
private var lockView: View? = null
|
||||
@@ -133,6 +135,7 @@ class EntryActivity : DatabaseLockActivity() {
|
||||
titleIconView = findViewById(R.id.entry_icon)
|
||||
historyView = findViewById(R.id.history_container)
|
||||
tagsListView = findViewById(R.id.entry_tags_list_view)
|
||||
entryContentTab = findViewById(R.id.entry_content_tab)
|
||||
entryProgress = findViewById(R.id.entry_progress)
|
||||
lockView = findViewById(R.id.lock_button)
|
||||
loadingView = findViewById(R.id.loading)
|
||||
@@ -162,6 +165,19 @@ class EntryActivity : DatabaseLockActivity() {
|
||||
adapter = tagsAdapter
|
||||
}
|
||||
|
||||
// Init content tab
|
||||
entryContentTab?.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab?) {
|
||||
mEntryViewModel.selectSection(EntryViewModel.EntrySection.
|
||||
getEntrySectionByPosition(tab?.position ?: 0)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab?) {}
|
||||
})
|
||||
|
||||
// Get Entry from UUID
|
||||
try {
|
||||
intent.getParcelableExtra<NodeId<UUID>?>(KEY_ENTRY)?.let { mainEntryId ->
|
||||
@@ -193,6 +209,10 @@ class EntryActivity : DatabaseLockActivity() {
|
||||
lockAndExit()
|
||||
}
|
||||
|
||||
mEntryViewModel.sectionSelected.observe(this) { entrySection ->
|
||||
entryContentTab?.getTabAt(entrySection.position)?.select()
|
||||
}
|
||||
|
||||
mEntryViewModel.entryInfoHistory.observe(this) { entryInfoHistory ->
|
||||
if (entryInfoHistory != null) {
|
||||
this.mMainEntryId = entryInfoHistory.mainEntryId
|
||||
|
||||
@@ -58,9 +58,13 @@ import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||
import com.kunzisoft.keepass.database.element.*
|
||||
import com.kunzisoft.keepass.database.element.node.Node
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
import com.kunzisoft.keepass.database.element.template.*
|
||||
import com.kunzisoft.keepass.database.element.template.Template
|
||||
import com.kunzisoft.keepass.education.EntryEditActivityEducation
|
||||
import com.kunzisoft.keepass.model.*
|
||||
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
|
||||
import com.kunzisoft.keepass.model.AttachmentState
|
||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||
import com.kunzisoft.keepass.model.RegisterInfo
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.otp.OtpElement
|
||||
import com.kunzisoft.keepass.services.AttachmentFileNotificationService
|
||||
import com.kunzisoft.keepass.services.ClipboardEntryNotificationService
|
||||
@@ -78,11 +82,9 @@ import com.kunzisoft.keepass.viewmodels.ColorPickerViewModel
|
||||
import com.kunzisoft.keepass.viewmodels.EntryEditViewModel
|
||||
import org.joda.time.DateTime
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class EntryEditActivity : DatabaseLockActivity(),
|
||||
EntryCustomFieldDialogFragment.EntryCustomFieldListener,
|
||||
GeneratePasswordDialogFragment.GeneratePasswordListener,
|
||||
SetOTPDialogFragment.CreateOtpListener,
|
||||
DatePickerDialog.OnDateSetListener,
|
||||
TimePickerDialog.OnTimeSetListener,
|
||||
@@ -119,6 +121,20 @@ class EntryEditActivity : DatabaseLockActivity(),
|
||||
mEntryEditViewModel.selectIcon(icon)
|
||||
}
|
||||
|
||||
private var mPasswordField: Field? = null
|
||||
private var mKeyGeneratorResultLauncher = KeyGeneratorActivity.registerForGeneratedKeyResult(this) { keyGenerated ->
|
||||
keyGenerated?.let {
|
||||
mPasswordField?.let {
|
||||
it.protectedValue.stringValue = keyGenerated
|
||||
mEntryEditViewModel.selectPassword(it)
|
||||
}
|
||||
}
|
||||
mPasswordField = null
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
performedNextEducation()
|
||||
}
|
||||
}
|
||||
|
||||
// To ask data lost only one time
|
||||
private var backPressedAlreadyApproved = false
|
||||
|
||||
@@ -268,9 +284,8 @@ class EntryEditActivity : DatabaseLockActivity(),
|
||||
}
|
||||
|
||||
mEntryEditViewModel.requestPasswordSelection.observe(this) { passwordField ->
|
||||
GeneratePasswordDialogFragment
|
||||
.getInstance(passwordField)
|
||||
.show(supportFragmentManager, "PasswordGeneratorFragment")
|
||||
mPasswordField = passwordField
|
||||
KeyGeneratorActivity.launch(this, mKeyGeneratorResultLauncher)
|
||||
}
|
||||
|
||||
mEntryEditViewModel.requestCustomFieldEdition.observe(this) { field ->
|
||||
@@ -420,9 +435,10 @@ class EntryEditActivity : DatabaseLockActivity(),
|
||||
|
||||
private fun entryValidatedForKeyboardSelection(database: Database, entry: Entry) {
|
||||
// Populate Magikeyboard with entry
|
||||
populateKeyboardAndMoveAppToBackground(this,
|
||||
entry.getEntryInfo(database),
|
||||
intent)
|
||||
MagikeyboardService.populateKeyboardAndMoveAppToBackground(
|
||||
this,
|
||||
entry.getEntryInfo(database)
|
||||
)
|
||||
onValidateSpecialMode()
|
||||
// Don't keep activity history for entry edition
|
||||
finishForEntryResult(entry)
|
||||
@@ -656,17 +672,6 @@ class EntryEditActivity : DatabaseLockActivity(),
|
||||
mEntryEditViewModel.selectTime(hours, minutes)
|
||||
}
|
||||
|
||||
override fun acceptPassword(passwordField: Field) {
|
||||
mEntryEditViewModel.selectPassword(passwordField)
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
performedNextEducation()
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancelPassword(passwordField: Field) {
|
||||
// Do nothing here
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
onApprovedBackPressed {
|
||||
super@EntryEditActivity.onBackPressed()
|
||||
|
||||
@@ -19,17 +19,16 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
|
||||
import com.kunzisoft.keepass.model.EntryInfo
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.otp.OtpEntryFields
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
@@ -50,151 +49,165 @@ class EntrySelectionLauncherActivity : DatabaseModeActivity() {
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
var sharedWebDomain: String? = null
|
||||
var otpString: String? = null
|
||||
|
||||
when (intent?.action) {
|
||||
Intent.ACTION_SEND -> {
|
||||
if ("text/plain" == intent.type) {
|
||||
// Retrieve web domain or OTP
|
||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let { extra ->
|
||||
if (OtpEntryFields.isOTPUri(extra))
|
||||
otpString = extra
|
||||
else
|
||||
sharedWebDomain = Uri.parse(extra).host
|
||||
val keySelectionBundle = intent.getBundleExtra(KEY_SELECTION_BUNDLE)
|
||||
if (keySelectionBundle != null) {
|
||||
// To manage package name
|
||||
var searchInfo = SearchInfo()
|
||||
keySelectionBundle.getParcelable<SearchInfo>(KEY_SEARCH_INFO)?.let { mSearchInfo ->
|
||||
searchInfo = mSearchInfo
|
||||
}
|
||||
launch(database, searchInfo)
|
||||
} else {
|
||||
// To manage share
|
||||
var sharedWebDomain: String? = null
|
||||
var otpString: String? = null
|
||||
|
||||
when (intent?.action) {
|
||||
Intent.ACTION_SEND -> {
|
||||
if ("text/plain" == intent.type) {
|
||||
// Retrieve web domain or OTP
|
||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let { extra ->
|
||||
if (OtpEntryFields.isOTPUri(extra))
|
||||
otpString = extra
|
||||
else
|
||||
sharedWebDomain = Uri.parse(extra).host
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Intent.ACTION_VIEW -> {
|
||||
// Retrieve OTP
|
||||
intent.dataString?.let { extra ->
|
||||
if (OtpEntryFields.isOTPUri(extra))
|
||||
otpString = extra
|
||||
Intent.ACTION_VIEW -> {
|
||||
// Retrieve OTP
|
||||
intent.dataString?.let { extra ->
|
||||
if (OtpEntryFields.isOTPUri(extra))
|
||||
otpString = extra
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
// Build domain search param
|
||||
val searchInfo = SearchInfo().apply {
|
||||
this.webDomain = sharedWebDomain
|
||||
this.otpString = otpString
|
||||
}
|
||||
// Build domain search param
|
||||
val searchInfo = SearchInfo().apply {
|
||||
this.webDomain = sharedWebDomain
|
||||
this.otpString = otpString
|
||||
}
|
||||
|
||||
SearchInfo.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
||||
searchInfo.webDomain = concreteWebDomain
|
||||
launch(database, searchInfo)
|
||||
SearchInfo.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
||||
searchInfo.webDomain = concreteWebDomain
|
||||
launch(database, searchInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launch(database: Database?,
|
||||
searchInfo: SearchInfo) {
|
||||
|
||||
if (!searchInfo.containsOnlyNullValues()) {
|
||||
// Setting to integrate Magikeyboard
|
||||
val searchShareForMagikeyboard = PreferencesUtil.isKeyboardSearchShareEnable(this)
|
||||
// Setting to integrate Magikeyboard
|
||||
val searchShareForMagikeyboard = PreferencesUtil.isKeyboardSearchShareEnable(this)
|
||||
|
||||
// If database is open
|
||||
val readOnly = database?.isReadOnly != false
|
||||
SearchHelper.checkAutoSearchInfo(this,
|
||||
database,
|
||||
searchInfo,
|
||||
{ openedDatabase, items ->
|
||||
// Items found
|
||||
if (searchInfo.otpString != null) {
|
||||
if (!readOnly) {
|
||||
GroupActivity.launchForSaveResult(
|
||||
this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
false)
|
||||
} else {
|
||||
Toast.makeText(applicationContext,
|
||||
R.string.autofill_read_only_save,
|
||||
Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
} else if (searchShareForMagikeyboard) {
|
||||
if (items.size == 1) {
|
||||
// Automatically populate keyboard
|
||||
val entryPopulate = items[0]
|
||||
populateKeyboardAndMoveAppToBackground(
|
||||
this,
|
||||
entryPopulate,
|
||||
intent)
|
||||
} else {
|
||||
// Select the one we want
|
||||
GroupActivity.launchForKeyboardSelectionResult(this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
true)
|
||||
}
|
||||
} else {
|
||||
GroupActivity.launchForSearchResult(this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
true)
|
||||
}
|
||||
},
|
||||
{ openedDatabase ->
|
||||
// Show the database UI to select the entry
|
||||
if (searchInfo.otpString != null) {
|
||||
if (!readOnly) {
|
||||
GroupActivity.launchForSaveResult(this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
false)
|
||||
} else {
|
||||
Toast.makeText(applicationContext,
|
||||
R.string.autofill_read_only_save,
|
||||
Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
} else if (readOnly || searchShareForMagikeyboard) {
|
||||
GroupActivity.launchForKeyboardSelectionResult(this,
|
||||
// If database is open
|
||||
val readOnly = database?.isReadOnly != false
|
||||
SearchHelper.checkAutoSearchInfo(this,
|
||||
database,
|
||||
searchInfo,
|
||||
{ openedDatabase, items ->
|
||||
// Items found
|
||||
if (searchInfo.otpString != null) {
|
||||
if (!readOnly) {
|
||||
GroupActivity.launchForSaveResult(
|
||||
this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
false)
|
||||
} else {
|
||||
Toast.makeText(applicationContext,
|
||||
R.string.autofill_read_only_save,
|
||||
Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
} else if (searchShareForMagikeyboard) {
|
||||
MagikeyboardService.performSelection(
|
||||
items,
|
||||
{ entryInfo ->
|
||||
// Automatically populate keyboard
|
||||
MagikeyboardService.populateKeyboardAndMoveAppToBackground(
|
||||
this,
|
||||
entryInfo
|
||||
)
|
||||
},
|
||||
{ autoSearch ->
|
||||
GroupActivity.launchForKeyboardSelectionResult(this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
autoSearch)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
GroupActivity.launchForSearchResult(this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
true)
|
||||
}
|
||||
},
|
||||
{ openedDatabase ->
|
||||
// Show the database UI to select the entry
|
||||
if (searchInfo.otpString != null) {
|
||||
if (!readOnly) {
|
||||
GroupActivity.launchForSaveResult(this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
false)
|
||||
}
|
||||
},
|
||||
{
|
||||
// If database not open
|
||||
if (searchInfo.otpString != null) {
|
||||
if (!readOnly) {
|
||||
FileDatabaseSelectActivity.launchForSaveResult(this,
|
||||
searchInfo)
|
||||
} else {
|
||||
Toast.makeText(applicationContext,
|
||||
R.string.autofill_read_only_save,
|
||||
Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
} else if (searchShareForMagikeyboard) {
|
||||
FileDatabaseSelectActivity.launchForKeyboardSelectionResult(this,
|
||||
searchInfo)
|
||||
} else {
|
||||
FileDatabaseSelectActivity.launchForSearchResult(this,
|
||||
searchInfo)
|
||||
Toast.makeText(applicationContext,
|
||||
R.string.autofill_read_only_save,
|
||||
Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
} else if (readOnly || searchShareForMagikeyboard) {
|
||||
GroupActivity.launchForKeyboardSelectionResult(this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
false)
|
||||
} else {
|
||||
GroupActivity.launchForSearchResult(this,
|
||||
openedDatabase,
|
||||
searchInfo,
|
||||
false)
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
// If database not open
|
||||
if (searchInfo.otpString != null) {
|
||||
FileDatabaseSelectActivity.launchForSaveResult(this,
|
||||
searchInfo)
|
||||
} else if (searchShareForMagikeyboard) {
|
||||
FileDatabaseSelectActivity.launchForKeyboardSelectionResult(this,
|
||||
searchInfo)
|
||||
} else {
|
||||
FileDatabaseSelectActivity.launchForSearchResult(this,
|
||||
searchInfo)
|
||||
}
|
||||
}
|
||||
)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun populateKeyboardAndMoveAppToBackground(activity: Activity,
|
||||
entry: EntryInfo,
|
||||
intent: Intent,
|
||||
toast: Boolean = true) {
|
||||
// Populate Magikeyboard with entry
|
||||
MagikeyboardService.addEntryAndLaunchNotificationIfAllowed(activity, entry, toast)
|
||||
// Consume the selection mode
|
||||
EntrySelectionHelper.removeModesFromIntent(intent)
|
||||
activity.moveTaskToBack(true)
|
||||
companion object {
|
||||
|
||||
private const val KEY_SELECTION_BUNDLE = "KEY_SELECTION_BUNDLE"
|
||||
private const val KEY_SEARCH_INFO = "KEY_SEARCH_INFO"
|
||||
|
||||
fun launch(context: Context,
|
||||
searchInfo: SearchInfo? = null) {
|
||||
val intent = Intent(context, EntrySelectionLauncherActivity::class.java).apply {
|
||||
putExtra(KEY_SELECTION_BUNDLE, Bundle().apply {
|
||||
putParcelable(KEY_SEARCH_INFO, searchInfo)
|
||||
})
|
||||
}
|
||||
// New task needed because don't launch from an Activity context
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||
Intent.FLAG_ACTIVITY_CLEAR_TASK or
|
||||
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
@@ -67,6 +67,7 @@ import com.kunzisoft.keepass.database.element.node.Type
|
||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||
import com.kunzisoft.keepass.database.search.SearchParameters
|
||||
import com.kunzisoft.keepass.education.GroupActivityEducation
|
||||
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
|
||||
import com.kunzisoft.keepass.model.GroupInfo
|
||||
import com.kunzisoft.keepass.model.MainCredential
|
||||
import com.kunzisoft.keepass.model.RegisterInfo
|
||||
@@ -79,6 +80,7 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.settings.SettingsActivity
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import com.kunzisoft.keepass.view.*
|
||||
import com.kunzisoft.keepass.viewmodels.GroupEditViewModel
|
||||
@@ -182,6 +184,11 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
|
||||
addSearch()
|
||||
//loadGroup()
|
||||
|
||||
// Back to previous keyboard
|
||||
if (PreferencesUtil.isKeyboardPreviousSearchEnable(this@GroupActivity)) {
|
||||
sendBroadcast(Intent(BACK_PREVIOUS_KEYBOARD_ACTION))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -200,7 +207,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
finishNodeAction()
|
||||
if (mSearchState == null) {
|
||||
mSearchState = SearchState(searchFiltersView?.searchParameters
|
||||
?: SearchParameters(), 0)
|
||||
?: PreferencesUtil.getDefaultSearchParameters(this), 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,7 +726,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
val stringQuery = intent.getStringExtra(SearchManager.QUERY)?.trim { it <= ' ' } ?: ""
|
||||
intent.action = Intent.ACTION_DEFAULT
|
||||
intent.removeExtra(SearchManager.QUERY)
|
||||
mSearchState = SearchState(SearchParameters().apply {
|
||||
mSearchState = SearchState(PreferencesUtil.getDefaultSearchParameters(this).apply {
|
||||
searchQuery = stringQuery
|
||||
}, mSearchState?.firstVisibleItem ?: 0)
|
||||
}
|
||||
@@ -884,10 +891,9 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
private fun entrySelectedForKeyboardSelection(database: Database, entry: Entry) {
|
||||
reloadCurrentGroup()
|
||||
// Populate Magikeyboard with entry
|
||||
populateKeyboardAndMoveAppToBackground(
|
||||
MagikeyboardService.populateKeyboardAndMoveAppToBackground(
|
||||
this,
|
||||
entry.getEntryInfo(database),
|
||||
intent
|
||||
entry.getEntryInfo(database)
|
||||
)
|
||||
onValidateSpecialMode()
|
||||
}
|
||||
@@ -1126,11 +1132,13 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
|
||||
finishNodeAction()
|
||||
searchView?.setOnQueryTextListener(null)
|
||||
searchFiltersView?.saveSearchParameters()
|
||||
}
|
||||
|
||||
private fun addSearchQueryInSearchView(searchQuery: String) {
|
||||
searchView?.setOnQueryTextListener(null)
|
||||
searchView?.setQuery(searchQuery, false)
|
||||
searchView?.clearFocus()
|
||||
searchView?.setOnQueryTextListener(mOnSearchQueryTextListener)
|
||||
}
|
||||
|
||||
@@ -1605,50 +1613,31 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
autofillActivityResultLauncher: ActivityResultLauncher<Intent>?) {
|
||||
EntrySelectionHelper.doSpecialAction(activity.intent,
|
||||
{
|
||||
GroupActivity.launch(
|
||||
// Default action
|
||||
launch(
|
||||
activity,
|
||||
database,
|
||||
true
|
||||
)
|
||||
},
|
||||
{ searchInfo ->
|
||||
SearchHelper.checkAutoSearchInfo(activity,
|
||||
// Search action
|
||||
if (database.loaded) {
|
||||
launchForSearchResult(activity,
|
||||
database,
|
||||
searchInfo,
|
||||
{ _, _ ->
|
||||
// Response is build
|
||||
GroupActivity.launchForSearchResult(activity,
|
||||
database,
|
||||
searchInfo,
|
||||
true)
|
||||
onLaunchActivitySpecialMode()
|
||||
},
|
||||
{
|
||||
// Here no search info found
|
||||
if (database.isReadOnly) {
|
||||
GroupActivity.launchForSearchResult(activity,
|
||||
database,
|
||||
searchInfo,
|
||||
false)
|
||||
} else {
|
||||
GroupActivity.launchForSaveResult(activity,
|
||||
database,
|
||||
searchInfo,
|
||||
false)
|
||||
}
|
||||
onLaunchActivitySpecialMode()
|
||||
},
|
||||
{
|
||||
// Simply close if database not opened, normally not happened
|
||||
onCancelSpecialMode()
|
||||
}
|
||||
)
|
||||
true)
|
||||
onLaunchActivitySpecialMode()
|
||||
} else {
|
||||
// Simply close if database not opened
|
||||
onCancelSpecialMode()
|
||||
}
|
||||
},
|
||||
{ searchInfo ->
|
||||
// Save info used with OTP
|
||||
// Save info
|
||||
if (database.loaded) {
|
||||
if (!database.isReadOnly) {
|
||||
GroupActivity.launchForSaveResult(
|
||||
launchForSaveResult(
|
||||
activity,
|
||||
database,
|
||||
searchInfo,
|
||||
@@ -1667,28 +1656,33 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
}
|
||||
},
|
||||
{ searchInfo ->
|
||||
// Keyboard selection
|
||||
SearchHelper.checkAutoSearchInfo(activity,
|
||||
database,
|
||||
searchInfo,
|
||||
{ _, items ->
|
||||
// Response is build
|
||||
if (items.size == 1) {
|
||||
populateKeyboardAndMoveAppToBackground(activity,
|
||||
items[0],
|
||||
activity.intent)
|
||||
onValidateSpecialMode()
|
||||
} else {
|
||||
// Select the one we want
|
||||
GroupActivity.launchForKeyboardSelectionResult(activity,
|
||||
database,
|
||||
searchInfo,
|
||||
true)
|
||||
onLaunchActivitySpecialMode()
|
||||
}
|
||||
MagikeyboardService.performSelection(
|
||||
items,
|
||||
{ entryInfo ->
|
||||
// Keyboard populated
|
||||
MagikeyboardService.populateKeyboardAndMoveAppToBackground(
|
||||
activity,
|
||||
entryInfo
|
||||
)
|
||||
onValidateSpecialMode()
|
||||
},
|
||||
{ autoSearch ->
|
||||
launchForKeyboardSelectionResult(activity,
|
||||
database,
|
||||
searchInfo,
|
||||
autoSearch)
|
||||
onLaunchActivitySpecialMode()
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
// Here no search info found, disable auto search
|
||||
GroupActivity.launchForKeyboardSelectionResult(activity,
|
||||
launchForKeyboardSelectionResult(activity,
|
||||
database,
|
||||
searchInfo,
|
||||
false)
|
||||
@@ -1701,6 +1695,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
)
|
||||
},
|
||||
{ searchInfo, autofillComponent ->
|
||||
// Autofill selection
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
SearchHelper.checkAutoSearchInfo(activity,
|
||||
database,
|
||||
@@ -1712,7 +1707,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
},
|
||||
{
|
||||
// Here no search info found, disable auto search
|
||||
GroupActivity.launchForAutofillResult(activity,
|
||||
launchForAutofillResult(activity,
|
||||
database,
|
||||
autofillActivityResultLauncher,
|
||||
autofillComponent,
|
||||
@@ -1730,20 +1725,21 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
}
|
||||
},
|
||||
{ registerInfo ->
|
||||
// Autofill registration
|
||||
if (!database.isReadOnly) {
|
||||
SearchHelper.checkAutoSearchInfo(activity,
|
||||
database,
|
||||
registerInfo?.searchInfo,
|
||||
{ _, _ ->
|
||||
// No auto search, it's a registration
|
||||
GroupActivity.launchForRegistration(activity,
|
||||
launchForRegistration(activity,
|
||||
database,
|
||||
registerInfo)
|
||||
onLaunchActivitySpecialMode()
|
||||
},
|
||||
{
|
||||
// Here no search info found, disable auto search
|
||||
GroupActivity.launchForRegistration(activity,
|
||||
launchForRegistration(activity,
|
||||
database,
|
||||
registerInfo)
|
||||
onLaunchActivitySpecialMode()
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.kunzisoft.keepass.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.commit
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.fragments.KeyGeneratorFragment
|
||||
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.view.updateLockPaddingLeft
|
||||
import com.kunzisoft.keepass.viewmodels.KeyGeneratorViewModel
|
||||
|
||||
class KeyGeneratorActivity : DatabaseLockActivity() {
|
||||
|
||||
private lateinit var toolbar: Toolbar
|
||||
private lateinit var coordinatorLayout: CoordinatorLayout
|
||||
private lateinit var validationButton: View
|
||||
private var lockView: View? = null
|
||||
|
||||
private val keyGeneratorViewModel: KeyGeneratorViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_key_generator)
|
||||
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
toolbar.title = " "
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
coordinatorLayout = findViewById(R.id.key_generator_coordinator)
|
||||
|
||||
lockView = findViewById(R.id.lock_button)
|
||||
lockView?.setOnClickListener {
|
||||
lockAndExit()
|
||||
}
|
||||
|
||||
validationButton = findViewById(R.id.key_generator_validation)
|
||||
validationButton.setOnClickListener {
|
||||
keyGeneratorViewModel.validateKeyGenerated()
|
||||
}
|
||||
|
||||
supportFragmentManager.commit {
|
||||
replace(R.id.key_generator_fragment, KeyGeneratorFragment.getInstance(
|
||||
// Default selection tab
|
||||
KeyGeneratorFragment.KeyGeneratorTab.PASSWORD
|
||||
), KEY_GENERATED_FRAGMENT_TAG
|
||||
)
|
||||
}
|
||||
|
||||
keyGeneratorViewModel.keyGenerated.observe(this) { keyGenerated ->
|
||||
setResult(Activity.RESULT_OK, Intent().apply {
|
||||
putExtra(KEY_GENERATED, keyGenerated)
|
||||
})
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun viewToInvalidateTimeout(): View? {
|
||||
return findViewById<ViewGroup>(R.id.key_generator_container)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
// Show the lock button
|
||||
lockView?.visibility = if (PreferencesUtil.showLockDatabaseButton(this)) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
|
||||
// Padding if lock button visible
|
||||
toolbar.updateLockPaddingLeft()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
super.onCreateOptionsMenu(menu)
|
||||
menuInflater.inflate(R.menu.key_generator, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
}
|
||||
R.id.menu_generate -> {
|
||||
keyGeneratorViewModel.requireKeyGeneration()
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
setResult(Activity.RESULT_CANCELED, Intent())
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_GENERATED = "KEY_GENERATED"
|
||||
private const val KEY_GENERATED_FRAGMENT_TAG = "KEY_GENERATED_FRAGMENT_TAG"
|
||||
|
||||
fun registerForGeneratedKeyResult(activity: FragmentActivity,
|
||||
keyGeneratedListener: (String?) -> Unit): ActivityResultLauncher<Intent> {
|
||||
return activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
keyGeneratedListener.invoke(
|
||||
result.data?.getStringExtra(KEY_GENERATED)
|
||||
)
|
||||
} else {
|
||||
keyGeneratedListener.invoke(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun launch(context: FragmentActivity,
|
||||
resultLauncher: ActivityResultLauncher<Intent>) {
|
||||
// Create an instance to return the picker icon
|
||||
resultLauncher.launch(
|
||||
Intent(context, KeyGeneratorActivity::class.java)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.activities
|
||||
|
||||
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||
|
||||
/**
|
||||
* Activity to select entry in database and populate it in Magikeyboard
|
||||
*/
|
||||
class MagikeyboardLauncherActivity : DatabaseModeActivity() {
|
||||
|
||||
override fun applyCustomStyle(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun finishActivityIfReloadRequested(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
SearchHelper.checkAutoSearchInfo(this,
|
||||
database,
|
||||
null,
|
||||
{ _, _ ->
|
||||
// Not called
|
||||
// if items found directly returns before calling this activity
|
||||
},
|
||||
{ openedDatabase ->
|
||||
// Select if not found
|
||||
GroupActivity.launchForKeyboardSelectionResult(this, openedDatabase)
|
||||
},
|
||||
{
|
||||
// Pass extra to get entry
|
||||
FileDatabaseSelectActivity.launchForKeyboardSelectionResult(this)
|
||||
}
|
||||
)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@@ -34,11 +34,13 @@ import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.fragment.app.commit
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
@@ -52,6 +54,7 @@ import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
||||
import com.kunzisoft.keepass.autofill.AutofillComponent
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||
import com.kunzisoft.keepass.biometric.AdvancedUnlockFragment
|
||||
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
|
||||
import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException
|
||||
@@ -63,6 +66,7 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.MAIN_CREDENTIAL_KEY
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.READ_ONLY_KEY
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.settings.SettingsAdvancedUnlockActivity
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION
|
||||
import com.kunzisoft.keepass.utils.MenuUtil
|
||||
@@ -79,6 +83,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
|
||||
// Views
|
||||
private var toolbar: Toolbar? = null
|
||||
private var filenameView: TextView? = null
|
||||
private var advancedUnlockButton: View? = null
|
||||
private var mainCredentialView: MainCredentialView? = null
|
||||
private var confirmButtonView: Button? = null
|
||||
private var infoContainerView: ViewGroup? = null
|
||||
@@ -116,6 +121,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
filenameView = findViewById(R.id.filename)
|
||||
advancedUnlockButton = findViewById(R.id.activity_password_advanced_unlock_button)
|
||||
mainCredentialView = findViewById(R.id.activity_password_credentials)
|
||||
confirmButtonView = findViewById(R.id.activity_password_open_button)
|
||||
infoContainerView = findViewById(R.id.activity_password_info_container)
|
||||
@@ -143,6 +149,11 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
|
||||
getUriFromIntent(intent)
|
||||
|
||||
// Init Biometric elements
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
advancedUnlockButton?.setOnClickListener {
|
||||
startActivity(Intent(this, SettingsAdvancedUnlockActivity::class.java))
|
||||
}
|
||||
}
|
||||
advancedUnlockFragment = supportFragmentManager
|
||||
.findFragmentByTag(UNLOCK_FRAGMENT_TAG) as? AdvancedUnlockFragment?
|
||||
if (advancedUnlockFragment == null) {
|
||||
@@ -227,6 +238,15 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
if (database != null) {
|
||||
// Trying to load another database
|
||||
if (mDatabaseFileUri != null
|
||||
&& database.fileUri != null
|
||||
&& mDatabaseFileUri != database.fileUri) {
|
||||
Toast.makeText(this,
|
||||
R.string.warning_database_already_opened,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
launchGroupActivityIfLoaded(database)
|
||||
}
|
||||
}
|
||||
@@ -588,15 +608,29 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
|
||||
{
|
||||
performedNextEducation(menu)
|
||||
})
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& !readOnlyEducationPerformed) {
|
||||
val biometricCanAuthenticate = AdvancedUnlockManager.canAuthenticate(this)
|
||||
if ((biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS)
|
||||
&& advancedUnlockButton != null) {
|
||||
mPasswordActivityEducation.checkAndPerformedBiometricEducation(
|
||||
advancedUnlockButton!!,
|
||||
{
|
||||
startActivity(
|
||||
Intent(
|
||||
this,
|
||||
SettingsAdvancedUnlockActivity::class.java
|
||||
)
|
||||
)
|
||||
},
|
||||
{
|
||||
|
||||
advancedUnlockFragment?.performEducation(mPasswordActivityEducation,
|
||||
readOnlyEducationPerformed,
|
||||
{
|
||||
performedNextEducation(menu)
|
||||
},
|
||||
{
|
||||
performedNextEducation(menu)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (ignored: Exception) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.activities.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Field
|
||||
import com.kunzisoft.keepass.password.PasswordGenerator
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
||||
import com.kunzisoft.keepass.view.applyFontVisibility
|
||||
|
||||
class GeneratePasswordDialogFragment : DatabaseDialogFragment() {
|
||||
|
||||
private var mListener: GeneratePasswordListener? = null
|
||||
|
||||
private var root: View? = null
|
||||
private var lengthTextView: EditText? = null
|
||||
private var passwordInputLayoutView: TextInputLayout? = null
|
||||
private var passwordView: EditText? = null
|
||||
|
||||
private var mPasswordField: Field? = null
|
||||
|
||||
private var uppercaseBox: CompoundButton? = null
|
||||
private var lowercaseBox: CompoundButton? = null
|
||||
private var digitsBox: CompoundButton? = null
|
||||
private var minusBox: CompoundButton? = null
|
||||
private var underlineBox: CompoundButton? = null
|
||||
private var spaceBox: CompoundButton? = null
|
||||
private var specialsBox: CompoundButton? = null
|
||||
private var bracketsBox: CompoundButton? = null
|
||||
private var extendedBox: CompoundButton? = null
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
try {
|
||||
mListener = context as GeneratePasswordListener
|
||||
} catch (e: ClassCastException) {
|
||||
throw ClassCastException(context.toString()
|
||||
+ " must implement " + GeneratePasswordListener::class.java.name)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
mListener = null
|
||||
super.onDetach()
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
activity?.let { activity ->
|
||||
val builder = AlertDialog.Builder(activity)
|
||||
val inflater = activity.layoutInflater
|
||||
root = inflater.inflate(R.layout.fragment_generate_password, null)
|
||||
|
||||
passwordInputLayoutView = root?.findViewById(R.id.password_input_layout)
|
||||
passwordView = root?.findViewById(R.id.password)
|
||||
passwordView?.applyFontVisibility()
|
||||
val passwordCopyView: ImageView? = root?.findViewById(R.id.password_copy_button)
|
||||
passwordCopyView?.visibility = if(PreferencesUtil.allowCopyProtectedFields(activity))
|
||||
View.VISIBLE else View.GONE
|
||||
val clipboardHelper = ClipboardHelper(activity)
|
||||
passwordCopyView?.setOnClickListener {
|
||||
clipboardHelper.timeoutCopyToClipboard(passwordView!!.text.toString(),
|
||||
getString(R.string.copy_field,
|
||||
getString(R.string.entry_password)))
|
||||
}
|
||||
|
||||
lengthTextView = root?.findViewById(R.id.length)
|
||||
|
||||
uppercaseBox = root?.findViewById(R.id.cb_uppercase)
|
||||
lowercaseBox = root?.findViewById(R.id.cb_lowercase)
|
||||
digitsBox = root?.findViewById(R.id.cb_digits)
|
||||
minusBox = root?.findViewById(R.id.cb_minus)
|
||||
underlineBox = root?.findViewById(R.id.cb_underline)
|
||||
spaceBox = root?.findViewById(R.id.cb_space)
|
||||
specialsBox = root?.findViewById(R.id.cb_specials)
|
||||
bracketsBox = root?.findViewById(R.id.cb_brackets)
|
||||
extendedBox = root?.findViewById(R.id.cb_extended)
|
||||
|
||||
mPasswordField = arguments?.getParcelable(KEY_PASSWORD_FIELD)
|
||||
|
||||
assignDefaultCharacters()
|
||||
|
||||
val seekBar = root?.findViewById<SeekBar>(R.id.seekbar_length)
|
||||
seekBar?.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
lengthTextView?.setText(progress.toString())
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
})
|
||||
|
||||
context?.let { context ->
|
||||
seekBar?.progress = PreferencesUtil.getDefaultPasswordLength(context)
|
||||
}
|
||||
|
||||
root?.findViewById<Button>(R.id.generate_password_button)
|
||||
?.setOnClickListener { fillPassword() }
|
||||
|
||||
builder.setView(root)
|
||||
.setPositiveButton(R.string.accept) { _, _ ->
|
||||
mPasswordField?.let { passwordField ->
|
||||
passwordView?.text?.toString()?.let { passwordValue ->
|
||||
passwordField.protectedValue.stringValue = passwordValue
|
||||
}
|
||||
mListener?.acceptPassword(passwordField)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
mPasswordField?.let { passwordField ->
|
||||
mListener?.cancelPassword(passwordField)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
||||
// Pre-populate a password to possibly save the user a few clicks
|
||||
fillPassword()
|
||||
|
||||
return builder.create()
|
||||
}
|
||||
return super.onCreateDialog(savedInstanceState)
|
||||
}
|
||||
|
||||
private fun assignDefaultCharacters() {
|
||||
uppercaseBox?.isChecked = false
|
||||
lowercaseBox?.isChecked = false
|
||||
digitsBox?.isChecked = false
|
||||
minusBox?.isChecked = false
|
||||
underlineBox?.isChecked = false
|
||||
spaceBox?.isChecked = false
|
||||
specialsBox?.isChecked = false
|
||||
bracketsBox?.isChecked = false
|
||||
extendedBox?.isChecked = false
|
||||
|
||||
context?.let { context ->
|
||||
PreferencesUtil.getDefaultPasswordCharacters(context)?.let { charSet ->
|
||||
for (passwordChar in charSet) {
|
||||
when (passwordChar) {
|
||||
getString(R.string.value_password_uppercase) -> uppercaseBox?.isChecked = true
|
||||
getString(R.string.value_password_lowercase) -> lowercaseBox?.isChecked = true
|
||||
getString(R.string.value_password_digits) -> digitsBox?.isChecked = true
|
||||
getString(R.string.value_password_minus) -> minusBox?.isChecked = true
|
||||
getString(R.string.value_password_underline) -> underlineBox?.isChecked = true
|
||||
getString(R.string.value_password_space) -> spaceBox?.isChecked = true
|
||||
getString(R.string.value_password_special) -> specialsBox?.isChecked = true
|
||||
getString(R.string.value_password_brackets) -> bracketsBox?.isChecked = true
|
||||
getString(R.string.value_password_extended) -> extendedBox?.isChecked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillPassword() {
|
||||
root?.findViewById<EditText>(R.id.password)?.setText(generatePassword())
|
||||
}
|
||||
|
||||
fun generatePassword(): String {
|
||||
var password = ""
|
||||
try {
|
||||
val length = Integer.valueOf(root?.findViewById<EditText>(R.id.length)?.text.toString())
|
||||
password = PasswordGenerator(resources).generatePassword(length,
|
||||
uppercaseBox?.isChecked == true,
|
||||
lowercaseBox?.isChecked == true,
|
||||
digitsBox?.isChecked == true,
|
||||
minusBox?.isChecked == true,
|
||||
underlineBox?.isChecked == true,
|
||||
spaceBox?.isChecked == true,
|
||||
specialsBox?.isChecked == true,
|
||||
bracketsBox?.isChecked == true,
|
||||
extendedBox?.isChecked == true)
|
||||
passwordInputLayoutView?.error = null
|
||||
} catch (e: NumberFormatException) {
|
||||
passwordInputLayoutView?.error = getString(R.string.error_wrong_length)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
passwordInputLayoutView?.error = e.message
|
||||
}
|
||||
|
||||
return password
|
||||
}
|
||||
|
||||
interface GeneratePasswordListener {
|
||||
fun acceptPassword(passwordField: Field)
|
||||
fun cancelPassword(passwordField: Field)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_PASSWORD_FIELD = "KEY_PASSWORD_FIELD"
|
||||
|
||||
fun getInstance(field: Field): GeneratePasswordDialogFragment {
|
||||
return GeneratePasswordDialogFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(KEY_PASSWORD_FIELD, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,8 +36,11 @@ import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
|
||||
import com.kunzisoft.keepass.model.MainCredential
|
||||
import com.kunzisoft.keepass.password.PasswordEntropy
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import com.kunzisoft.keepass.view.KeyFileSelectionView
|
||||
import com.kunzisoft.keepass.view.PassKeyView
|
||||
import com.kunzisoft.keepass.view.applyFontVisibility
|
||||
|
||||
class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||
|
||||
@@ -48,8 +51,7 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||
|
||||
private var passwordCheckBox: CompoundButton? = null
|
||||
|
||||
private var passwordTextInputLayout: TextInputLayout? = null
|
||||
private var passwordView: TextView? = null
|
||||
private var passKeyView: PassKeyView? = null
|
||||
private var passwordRepeatTextInputLayout: TextInputLayout? = null
|
||||
private var passwordRepeatView: TextView? = null
|
||||
|
||||
@@ -59,6 +61,7 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||
private var mListener: AssignMainCredentialDialogListener? = null
|
||||
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
private var mPasswordEntropyCalculator: PasswordEntropy? = null
|
||||
|
||||
private var mEmptyPasswordConfirmationDialog: AlertDialog? = null
|
||||
private var mNoKeyConfirmationDialog: AlertDialog? = null
|
||||
@@ -100,6 +103,13 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||
super.onDetach()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Create the password entropy object
|
||||
mPasswordEntropyCalculator = PasswordEntropy()
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
activity?.let { activity ->
|
||||
|
||||
@@ -123,10 +133,10 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||
}
|
||||
|
||||
passwordCheckBox = rootView?.findViewById(R.id.password_checkbox)
|
||||
passwordTextInputLayout = rootView?.findViewById(R.id.password_input_layout)
|
||||
passwordView = rootView?.findViewById(R.id.pass_password)
|
||||
passKeyView = rootView?.findViewById(R.id.password_view)
|
||||
passwordRepeatTextInputLayout = rootView?.findViewById(R.id.password_repeat_input_layout)
|
||||
passwordRepeatView = rootView?.findViewById(R.id.pass_conf_password)
|
||||
passwordRepeatView = rootView?.findViewById(R.id.password_confirmation)
|
||||
passwordRepeatView?.applyFontVisibility()
|
||||
|
||||
keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox)
|
||||
keyFileSelectionView = rootView?.findViewById(R.id.keyfile_selection)
|
||||
@@ -162,7 +172,7 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||
if (allowNoMasterKey)
|
||||
showNoKeyConfirmationDialog()
|
||||
else {
|
||||
passwordTextInputLayout?.error = getString(R.string.error_disallow_no_credentials)
|
||||
passwordRepeatTextInputLayout?.error = getString(R.string.error_disallow_no_credentials)
|
||||
}
|
||||
}
|
||||
if (!error) {
|
||||
@@ -194,22 +204,22 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||
super.onResume()
|
||||
|
||||
// To check checkboxes if a text is present
|
||||
passwordView?.addTextChangedListener(passwordTextWatcher)
|
||||
passKeyView?.addTextChangedListener(passwordTextWatcher)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
passwordView?.removeTextChangedListener(passwordTextWatcher)
|
||||
passKeyView?.removeTextChangedListener(passwordTextWatcher)
|
||||
}
|
||||
|
||||
private fun verifyPassword(): Boolean {
|
||||
var error = false
|
||||
if (passwordCheckBox != null
|
||||
&& passwordCheckBox!!.isChecked
|
||||
&& passwordView != null
|
||||
&& passKeyView != null
|
||||
&& passwordRepeatView != null) {
|
||||
mMasterPassword = passwordView!!.text.toString()
|
||||
mMasterPassword = passKeyView!!.passwordString
|
||||
val confPassword = passwordRepeatView!!.text.toString()
|
||||
|
||||
// Verify that passwords match
|
||||
|
||||
@@ -16,7 +16,6 @@ import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.DateInstant
|
||||
import com.kunzisoft.keepass.database.element.template.TemplateField
|
||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||
import com.kunzisoft.keepass.model.EntryInfo
|
||||
@@ -25,6 +24,7 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
||||
import com.kunzisoft.keepass.utils.UuidUtil
|
||||
import com.kunzisoft.keepass.view.TemplateView
|
||||
import com.kunzisoft.keepass.view.hideByFading
|
||||
import com.kunzisoft.keepass.view.showByFading
|
||||
import com.kunzisoft.keepass.viewmodels.EntryViewModel
|
||||
import java.util.*
|
||||
@@ -32,6 +32,9 @@ import java.util.*
|
||||
class EntryFragment: DatabaseFragment() {
|
||||
|
||||
private lateinit var rootView: View
|
||||
private lateinit var mainSection: View
|
||||
private lateinit var advancedSection: View
|
||||
|
||||
private lateinit var templateView: TemplateView
|
||||
|
||||
private lateinit var creationDateView: TextView
|
||||
@@ -72,6 +75,10 @@ class EntryFragment: DatabaseFragment() {
|
||||
if (savedInstanceState == null) {
|
||||
view.isVisible = false
|
||||
}
|
||||
|
||||
mainSection = view.findViewById(R.id.entry_section_main)
|
||||
advancedSection = view.findViewById(R.id.entry_section_advanced)
|
||||
|
||||
templateView = view.findViewById(R.id.entry_template)
|
||||
loadTemplateSettings()
|
||||
|
||||
@@ -111,6 +118,19 @@ class EntryFragment: DatabaseFragment() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mEntryViewModel.sectionSelected.observe(viewLifecycleOwner) { entrySection ->
|
||||
when (entrySection ?: EntryViewModel.EntrySection.MAIN) {
|
||||
EntryViewModel.EntrySection.MAIN -> {
|
||||
mainSection.showByFading()
|
||||
advancedSection.hideByFading()
|
||||
}
|
||||
EntryViewModel.EntrySection.ADVANCED -> {
|
||||
mainSection.hideByFading()
|
||||
advancedSection.showByFading()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
|
||||
@@ -26,14 +26,14 @@ class IconPickerFragment : DatabaseFragment() {
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_icon_picker, container, false)
|
||||
return inflater.inflate(R.layout.fragment_tabs_pagination, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
viewPager = view.findViewById(R.id.icon_picker_pager)
|
||||
tabLayout = view.findViewById(R.id.icon_picker_tabs)
|
||||
viewPager = view.findViewById(R.id.tabs_view_pager)
|
||||
tabLayout = view.findViewById(R.id.tabs_layout)
|
||||
resetAppTimeoutWhenViewFocusedOrChanged(view)
|
||||
|
||||
arguments?.apply {
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2022 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.activities.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.adapters.KeyGeneratorPagerAdapter
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.viewmodels.KeyGeneratorViewModel
|
||||
|
||||
class KeyGeneratorFragment : DatabaseFragment() {
|
||||
|
||||
private var keyGeneratorPagerAdapter: KeyGeneratorPagerAdapter? = null
|
||||
private lateinit var viewPager: ViewPager2
|
||||
private lateinit var tabLayout: TabLayout
|
||||
|
||||
private val mKeyGeneratorViewModel: KeyGeneratorViewModel by activityViewModels()
|
||||
|
||||
private var mSelectedTab = KeyGeneratorTab.PASSWORD
|
||||
private var mOnPageChangeCallback: ViewPager2.OnPageChangeCallback = object:
|
||||
ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
mSelectedTab = KeyGeneratorTab.getKeyGeneratorTabByPosition(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_tabs_pagination, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
keyGeneratorPagerAdapter = KeyGeneratorPagerAdapter(this, )
|
||||
viewPager = view.findViewById(R.id.tabs_view_pager)
|
||||
tabLayout = view.findViewById(R.id.tabs_layout)
|
||||
viewPager.adapter = keyGeneratorPagerAdapter
|
||||
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
|
||||
tab.text = getString(KeyGeneratorTab.getKeyGeneratorTabByPosition(position).stringId)
|
||||
}.attach()
|
||||
viewPager.registerOnPageChangeCallback(mOnPageChangeCallback)
|
||||
|
||||
resetAppTimeoutWhenViewFocusedOrChanged(view)
|
||||
|
||||
arguments?.apply {
|
||||
if (containsKey(PASSWORD_TAB_ARG)) {
|
||||
viewPager.currentItem = getInt(PASSWORD_TAB_ARG)
|
||||
}
|
||||
remove(PASSWORD_TAB_ARG)
|
||||
}
|
||||
|
||||
mKeyGeneratorViewModel.requireKeyGeneration.observe(viewLifecycleOwner) {
|
||||
when (mSelectedTab) {
|
||||
KeyGeneratorTab.PASSWORD -> {
|
||||
mKeyGeneratorViewModel.requirePasswordGeneration()
|
||||
}
|
||||
KeyGeneratorTab.PASSPHRASE -> {
|
||||
mKeyGeneratorViewModel.requirePassphraseGeneration()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mKeyGeneratorViewModel.keyGeneratedValidated.observe(viewLifecycleOwner) {
|
||||
when (mSelectedTab) {
|
||||
KeyGeneratorTab.PASSWORD -> {
|
||||
mKeyGeneratorViewModel.validatePasswordGenerated()
|
||||
}
|
||||
KeyGeneratorTab.PASSPHRASE -> {
|
||||
mKeyGeneratorViewModel.validatePassphraseGenerated()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
viewPager.unregisterOnPageChangeCallback(mOnPageChangeCallback)
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
// Nothing here
|
||||
}
|
||||
|
||||
enum class KeyGeneratorTab(@StringRes val stringId: Int) {
|
||||
PASSWORD(R.string.password), PASSPHRASE(R.string.passphrase);
|
||||
|
||||
companion object {
|
||||
fun getKeyGeneratorTabByPosition(position: Int): KeyGeneratorTab {
|
||||
return when (position) {
|
||||
0 -> PASSWORD
|
||||
else -> PASSPHRASE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val PASSWORD_TAB_ARG = "PASSWORD_TAB_ARG"
|
||||
|
||||
fun getInstance(keyGeneratorTab: KeyGeneratorTab): KeyGeneratorFragment {
|
||||
val fragment = KeyGeneratorFragment()
|
||||
fragment.arguments = Bundle().apply {
|
||||
putInt(PASSWORD_TAB_ARG, keyGeneratorTab.ordinal)
|
||||
}
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2022 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.activities.fragments
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.password.PassphraseGenerator
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
||||
import com.kunzisoft.keepass.view.PassKeyView
|
||||
import com.kunzisoft.keepass.viewmodels.KeyGeneratorViewModel
|
||||
|
||||
class PassphraseGeneratorFragment : DatabaseFragment() {
|
||||
|
||||
private lateinit var passKeyView: PassKeyView
|
||||
|
||||
private lateinit var sliderWordCount: Slider
|
||||
private lateinit var wordCountText: EditText
|
||||
private lateinit var charactersCountText: TextView
|
||||
private lateinit var wordSeparator: EditText
|
||||
private lateinit var wordCaseSpinner: Spinner
|
||||
|
||||
private var minSliderWordCount: Int = 0
|
||||
private var maxSliderWordCount: Int = 0
|
||||
private var wordCaseAdapter: ArrayAdapter<String>? = null
|
||||
|
||||
private val mKeyGeneratorViewModel: KeyGeneratorViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View {
|
||||
return inflater.inflate(R.layout.fragment_generate_passphrase, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
passKeyView = view.findViewById(R.id.passphrase_view)
|
||||
val passphraseCopyView: ImageView? = view.findViewById(R.id.passphrase_copy_button)
|
||||
sliderWordCount = view.findViewById(R.id.slider_word_count)
|
||||
wordCountText = view.findViewById(R.id.word_count)
|
||||
charactersCountText = view.findViewById(R.id.character_count)
|
||||
wordSeparator = view.findViewById(R.id.word_separator)
|
||||
wordCaseSpinner = view.findViewById(R.id.word_case)
|
||||
|
||||
minSliderWordCount = resources.getInteger(R.integer.passphrase_generator_word_count_min)
|
||||
maxSliderWordCount = resources.getInteger(R.integer.passphrase_generator_word_count_max)
|
||||
|
||||
contextThemed?.let { context ->
|
||||
passphraseCopyView?.visibility = if(PreferencesUtil.allowCopyProtectedFields(context))
|
||||
View.VISIBLE else View.GONE
|
||||
val clipboardHelper = ClipboardHelper(context)
|
||||
passphraseCopyView?.setOnClickListener {
|
||||
clipboardHelper.timeoutCopyToClipboard(passKeyView.passwordString,
|
||||
getString(R.string.copy_field,
|
||||
getString(R.string.entry_password)))
|
||||
}
|
||||
|
||||
wordCaseAdapter = ArrayAdapter(context,
|
||||
android.R.layout.simple_spinner_item, resources.getStringArray(R.array.word_case_array)).apply {
|
||||
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
}
|
||||
wordCaseSpinner.adapter = wordCaseAdapter
|
||||
}
|
||||
|
||||
loadSettings()
|
||||
|
||||
var listenSlider = true
|
||||
var listenEditText = true
|
||||
sliderWordCount.addOnChangeListener { _, value, _ ->
|
||||
try {
|
||||
listenEditText = false
|
||||
if (listenSlider) {
|
||||
wordCountText.setText(value.toInt().toString())
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to set the word count value", e)
|
||||
} finally {
|
||||
listenEditText = true
|
||||
}
|
||||
}
|
||||
sliderWordCount.addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
|
||||
// TODO upgrade material-components lib
|
||||
// https://stackoverflow.com/questions/70873160/material-slider-onslidertouchlisteners-methods-can-only-be-called-from-within-t
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onStartTrackingTouch(slider: Slider) {}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onStopTrackingTouch(slider: Slider) {
|
||||
generatePassphrase()
|
||||
}
|
||||
})
|
||||
wordCountText.doOnTextChanged { _, _, _, _ ->
|
||||
if (listenEditText) {
|
||||
try {
|
||||
listenSlider = false
|
||||
setSliderValue(getWordCount())
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to get the word count value", e)
|
||||
} finally {
|
||||
listenSlider = true
|
||||
generatePassphrase()
|
||||
}
|
||||
}
|
||||
}
|
||||
wordSeparator.doOnTextChanged { _, _, _, _ ->
|
||||
generatePassphrase()
|
||||
}
|
||||
wordCaseSpinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
generatePassphrase()
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||
}
|
||||
|
||||
generatePassphrase()
|
||||
|
||||
mKeyGeneratorViewModel.passphraseGeneratedValidated.observe(viewLifecycleOwner) {
|
||||
mKeyGeneratorViewModel.setKeyGenerated(passKeyView.passwordString)
|
||||
}
|
||||
|
||||
mKeyGeneratorViewModel.requirePassphraseGeneration.observe(viewLifecycleOwner) {
|
||||
generatePassphrase()
|
||||
}
|
||||
|
||||
resetAppTimeoutWhenViewFocusedOrChanged(view)
|
||||
}
|
||||
|
||||
private fun getWordCount(): Int {
|
||||
return try {
|
||||
Integer.valueOf(wordCountText.text.toString())
|
||||
} catch (numberException: NumberFormatException) {
|
||||
minSliderWordCount
|
||||
}
|
||||
}
|
||||
|
||||
private fun setWordCount(wordCount: Int) {
|
||||
setSliderValue(wordCount)
|
||||
wordCountText.setText(wordCount.toString())
|
||||
}
|
||||
|
||||
private fun setSliderValue(value: Int) {
|
||||
when {
|
||||
value < minSliderWordCount -> {
|
||||
sliderWordCount.value = minSliderWordCount.toFloat()
|
||||
}
|
||||
value > maxSliderWordCount -> {
|
||||
sliderWordCount.value = maxSliderWordCount.toFloat()
|
||||
}
|
||||
else -> {
|
||||
sliderWordCount.value = value.toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getWordSeparator(): String {
|
||||
return wordSeparator.text.toString().ifEmpty { " " }
|
||||
}
|
||||
|
||||
private fun getWordCase(): PassphraseGenerator.WordCase {
|
||||
var wordCase = PassphraseGenerator.WordCase.LOWER_CASE
|
||||
try {
|
||||
wordCase = PassphraseGenerator.WordCase.getByOrdinal(wordCaseSpinner.selectedItemPosition)
|
||||
} catch (caseException: Exception) {
|
||||
Log.e(TAG, "Unable to retrieve the word case", caseException)
|
||||
}
|
||||
return wordCase
|
||||
}
|
||||
|
||||
private fun setWordCase(wordCase: PassphraseGenerator.WordCase) {
|
||||
wordCaseSpinner.setSelection(wordCase.ordinal)
|
||||
}
|
||||
|
||||
private fun getSeparator(): String {
|
||||
return wordSeparator.text?.toString() ?: ""
|
||||
}
|
||||
|
||||
private fun setSeparator(separator: String) {
|
||||
wordSeparator.setText(separator)
|
||||
}
|
||||
|
||||
private fun generatePassphrase() {
|
||||
var passphrase = ""
|
||||
try {
|
||||
passphrase = PassphraseGenerator().generatePassphrase(
|
||||
getWordCount(),
|
||||
getWordSeparator(),
|
||||
getWordCase())
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to generate a passphrase", e)
|
||||
}
|
||||
passKeyView.passwordString = passphrase
|
||||
charactersCountText.text = getString(R.string.character_count, passphrase.length)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
saveSettings()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun saveSettings() {
|
||||
context?.let { context ->
|
||||
PreferencesUtil.setDefaultPassphraseWordCount(context, getWordCount())
|
||||
PreferencesUtil.setDefaultPassphraseWordCase(context, getWordCase())
|
||||
PreferencesUtil.setDefaultPassphraseSeparator(context, getSeparator())
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadSettings() {
|
||||
context?.let { context ->
|
||||
setWordCount(PreferencesUtil.getDefaultPassphraseWordCount(context))
|
||||
setWordCase(PreferencesUtil.getDefaultPassphraseWordCase(context))
|
||||
setSeparator(PreferencesUtil.getDefaultPassphraseSeparator(context))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
// Nothing here
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PassphraseGnrtrFrgmt"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.activities.fragments
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.password.PasswordGenerator
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
||||
import com.kunzisoft.keepass.view.PassKeyView
|
||||
import com.kunzisoft.keepass.viewmodels.KeyGeneratorViewModel
|
||||
|
||||
class PasswordGeneratorFragment : DatabaseFragment() {
|
||||
|
||||
private lateinit var passKeyView: PassKeyView
|
||||
|
||||
private lateinit var sliderLength: Slider
|
||||
private lateinit var lengthEditView: EditText
|
||||
|
||||
private lateinit var uppercaseCompound: CompoundButton
|
||||
private lateinit var lowercaseCompound: CompoundButton
|
||||
private lateinit var digitsCompound: CompoundButton
|
||||
private lateinit var minusCompound: CompoundButton
|
||||
private lateinit var underlineCompound: CompoundButton
|
||||
private lateinit var spaceCompound: CompoundButton
|
||||
private lateinit var specialsCompound: CompoundButton
|
||||
private lateinit var bracketsCompound: CompoundButton
|
||||
private lateinit var extendedCompound: CompoundButton
|
||||
private lateinit var considerCharsEditText: EditText
|
||||
private lateinit var ignoreCharsEditText: EditText
|
||||
private lateinit var atLeastOneCompound: CompoundButton
|
||||
private lateinit var excludeAmbiguousCompound: CompoundButton
|
||||
|
||||
private var minLengthSlider: Int = 0
|
||||
private var maxLengthSlider: Int = 0
|
||||
|
||||
private val mKeyGeneratorViewModel: KeyGeneratorViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View {
|
||||
return inflater.inflate(R.layout.fragment_generate_password, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
passKeyView = view.findViewById(R.id.password_view)
|
||||
val passwordCopyView: ImageView? = view.findViewById(R.id.password_copy_button)
|
||||
|
||||
sliderLength = view.findViewById(R.id.slider_length)
|
||||
lengthEditView = view.findViewById(R.id.length)
|
||||
|
||||
uppercaseCompound = view.findViewById(R.id.upperCase_filter)
|
||||
lowercaseCompound = view.findViewById(R.id.lowerCase_filter)
|
||||
digitsCompound = view.findViewById(R.id.digits_filter)
|
||||
minusCompound = view.findViewById(R.id.minus_filter)
|
||||
underlineCompound = view.findViewById(R.id.underline_filter)
|
||||
spaceCompound = view.findViewById(R.id.space_filter)
|
||||
specialsCompound = view.findViewById(R.id.special_filter)
|
||||
bracketsCompound = view.findViewById(R.id.brackets_filter)
|
||||
extendedCompound = view.findViewById(R.id.extendedASCII_filter)
|
||||
considerCharsEditText = view.findViewById(R.id.consider_chars_filter)
|
||||
ignoreCharsEditText = view.findViewById(R.id.ignore_chars_filter)
|
||||
atLeastOneCompound = view.findViewById(R.id.atLeastOne_filter)
|
||||
excludeAmbiguousCompound = view.findViewById(R.id.excludeAmbiguous_filter)
|
||||
|
||||
contextThemed?.let { context ->
|
||||
passwordCopyView?.visibility = if(PreferencesUtil.allowCopyProtectedFields(context))
|
||||
View.VISIBLE else View.GONE
|
||||
val clipboardHelper = ClipboardHelper(context)
|
||||
passwordCopyView?.setOnClickListener {
|
||||
clipboardHelper.timeoutCopyToClipboard(passKeyView.passwordString,
|
||||
getString(R.string.copy_field,
|
||||
getString(R.string.entry_password)))
|
||||
}
|
||||
}
|
||||
|
||||
minLengthSlider = resources.getInteger(R.integer.password_generator_length_min)
|
||||
maxLengthSlider = resources.getInteger(R.integer.password_generator_length_max)
|
||||
|
||||
loadSettings()
|
||||
|
||||
uppercaseCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
lowercaseCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
digitsCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
minusCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
underlineCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
spaceCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
specialsCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
bracketsCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
extendedCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
considerCharsEditText.doOnTextChanged { _, _, _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
ignoreCharsEditText.doOnTextChanged { _, _, _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
atLeastOneCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
excludeAmbiguousCompound.setOnCheckedChangeListener { _, _ ->
|
||||
generatePassword()
|
||||
}
|
||||
|
||||
var listenSlider = true
|
||||
var listenEditText = true
|
||||
sliderLength.addOnChangeListener { _, value, _ ->
|
||||
try {
|
||||
listenEditText = false
|
||||
if (listenSlider) {
|
||||
lengthEditView.setText(value.toInt().toString())
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to set the length value", e)
|
||||
} finally {
|
||||
listenEditText = true
|
||||
}
|
||||
}
|
||||
sliderLength.addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
|
||||
// TODO upgrade material-components lib
|
||||
// https://stackoverflow.com/questions/70873160/material-slider-onslidertouchlisteners-methods-can-only-be-called-from-within-t
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onStartTrackingTouch(slider: Slider) {}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onStopTrackingTouch(slider: Slider) {
|
||||
generatePassword()
|
||||
}
|
||||
})
|
||||
lengthEditView.doOnTextChanged { _, _, _, _ ->
|
||||
if (listenEditText) {
|
||||
try {
|
||||
listenSlider = false
|
||||
setSliderValue(getPasswordLength())
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to get the length value", e)
|
||||
} finally {
|
||||
listenSlider = true
|
||||
generatePassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-populate a password to possibly save the user a few clicks
|
||||
generatePassword()
|
||||
|
||||
mKeyGeneratorViewModel.passwordGeneratedValidated.observe(viewLifecycleOwner) {
|
||||
mKeyGeneratorViewModel.setKeyGenerated(passKeyView.passwordString)
|
||||
}
|
||||
|
||||
mKeyGeneratorViewModel.requirePasswordGeneration.observe(viewLifecycleOwner) {
|
||||
generatePassword()
|
||||
}
|
||||
|
||||
resetAppTimeoutWhenViewFocusedOrChanged(view)
|
||||
}
|
||||
|
||||
private fun getPasswordLength(): Int {
|
||||
return try {
|
||||
Integer.valueOf(lengthEditView.text.toString())
|
||||
} catch (numberException: NumberFormatException) {
|
||||
minLengthSlider
|
||||
}
|
||||
}
|
||||
|
||||
private fun setPasswordLength(passwordLength: Int) {
|
||||
setSliderValue(passwordLength)
|
||||
lengthEditView.setText(passwordLength.toString())
|
||||
}
|
||||
|
||||
private fun getOptions(): Set<String> {
|
||||
val optionsSet = mutableSetOf<String>()
|
||||
if (uppercaseCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_uppercase))
|
||||
if (lowercaseCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_lowercase))
|
||||
if (digitsCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_digits))
|
||||
if (minusCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_minus))
|
||||
if (underlineCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_underline))
|
||||
if (spaceCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_space))
|
||||
if (specialsCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_special))
|
||||
if (bracketsCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_brackets))
|
||||
if (extendedCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_extended))
|
||||
if (atLeastOneCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_atLeastOne))
|
||||
if (excludeAmbiguousCompound.isChecked)
|
||||
optionsSet.add(getString(R.string.value_password_excludeAmbiguous))
|
||||
return optionsSet
|
||||
}
|
||||
|
||||
private fun setOptions(options: Set<String>) {
|
||||
uppercaseCompound.isChecked = false
|
||||
lowercaseCompound.isChecked = false
|
||||
digitsCompound.isChecked = false
|
||||
minusCompound.isChecked = false
|
||||
underlineCompound.isChecked = false
|
||||
spaceCompound.isChecked = false
|
||||
specialsCompound.isChecked = false
|
||||
bracketsCompound.isChecked = false
|
||||
extendedCompound.isChecked = false
|
||||
atLeastOneCompound.isChecked = false
|
||||
excludeAmbiguousCompound.isChecked = false
|
||||
for (option in options) {
|
||||
when (option) {
|
||||
getString(R.string.value_password_uppercase) -> uppercaseCompound.isChecked = true
|
||||
getString(R.string.value_password_lowercase) -> lowercaseCompound.isChecked = true
|
||||
getString(R.string.value_password_digits) -> digitsCompound.isChecked = true
|
||||
getString(R.string.value_password_minus) -> minusCompound.isChecked = true
|
||||
getString(R.string.value_password_underline) -> underlineCompound.isChecked = true
|
||||
getString(R.string.value_password_space) -> spaceCompound.isChecked = true
|
||||
getString(R.string.value_password_special) -> specialsCompound.isChecked = true
|
||||
getString(R.string.value_password_brackets) -> bracketsCompound.isChecked = true
|
||||
getString(R.string.value_password_extended) -> extendedCompound.isChecked = true
|
||||
getString(R.string.value_password_atLeastOne) -> atLeastOneCompound.isChecked = true
|
||||
getString(R.string.value_password_excludeAmbiguous) -> excludeAmbiguousCompound.isChecked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConsiderChars(): String {
|
||||
return considerCharsEditText.text.toString()
|
||||
}
|
||||
|
||||
private fun setConsiderChars(chars: String) {
|
||||
considerCharsEditText.setText(chars)
|
||||
}
|
||||
|
||||
private fun getIgnoreChars(): String {
|
||||
return ignoreCharsEditText.text.toString()
|
||||
}
|
||||
|
||||
private fun setIgnoreChars(chars: String) {
|
||||
ignoreCharsEditText.setText(chars)
|
||||
}
|
||||
|
||||
private fun generatePassword() {
|
||||
var password = ""
|
||||
try {
|
||||
password = PasswordGenerator(resources).generatePassword(getPasswordLength(),
|
||||
uppercaseCompound.isChecked,
|
||||
lowercaseCompound.isChecked,
|
||||
digitsCompound.isChecked,
|
||||
minusCompound.isChecked,
|
||||
underlineCompound.isChecked,
|
||||
spaceCompound.isChecked,
|
||||
specialsCompound.isChecked,
|
||||
bracketsCompound.isChecked,
|
||||
extendedCompound.isChecked,
|
||||
getConsiderChars(),
|
||||
getIgnoreChars(),
|
||||
atLeastOneCompound.isChecked,
|
||||
excludeAmbiguousCompound.isChecked)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to generate a password", e)
|
||||
}
|
||||
passKeyView.passwordString = password
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
saveSettings()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
// Nothing here
|
||||
}
|
||||
|
||||
private fun saveSettings() {
|
||||
context?.let { context ->
|
||||
PreferencesUtil.setDefaultPasswordOptions(context, getOptions())
|
||||
PreferencesUtil.setDefaultPasswordLength(context, getPasswordLength())
|
||||
PreferencesUtil.setDefaultPasswordConsiderChars(context, getConsiderChars())
|
||||
PreferencesUtil.setDefaultPasswordIgnoreChars(context, getIgnoreChars())
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadSettings() {
|
||||
context?.let { context ->
|
||||
setOptions(PreferencesUtil.getDefaultPasswordOptions(context))
|
||||
setPasswordLength(PreferencesUtil.getDefaultPasswordLength(context))
|
||||
setConsiderChars(PreferencesUtil.getDefaultPasswordConsiderChars(context))
|
||||
setIgnoreChars(PreferencesUtil.getDefaultPasswordIgnoreChars(context))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSliderValue(value: Int) {
|
||||
when {
|
||||
value < minLengthSlider -> {
|
||||
sliderLength.value = minLengthSlider.toFloat()
|
||||
}
|
||||
value > maxLengthSlider -> {
|
||||
sliderLength.value = maxLengthSlider.toFloat()
|
||||
}
|
||||
else -> {
|
||||
sliderLength.value = value.toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PasswordGeneratorFrgmt"
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ object EntrySelectionHelper {
|
||||
return intent.getParcelableExtra(KEY_SEARCH_INFO)
|
||||
}
|
||||
|
||||
fun addRegisterInfoInIntent(intent: Intent, registerInfo: RegisterInfo?) {
|
||||
private fun addRegisterInfoInIntent(intent: Intent, registerInfo: RegisterInfo?) {
|
||||
registerInfo?.let {
|
||||
intent.putExtra(KEY_REGISTER_INFO, it)
|
||||
}
|
||||
@@ -113,7 +113,7 @@ object EntrySelectionHelper {
|
||||
?: SpecialMode.DEFAULT
|
||||
}
|
||||
|
||||
fun addTypeModeInIntent(intent: Intent, typeMode: TypeMode) {
|
||||
private fun addTypeModeInIntent(intent: Intent, typeMode: TypeMode) {
|
||||
intent.putExtra(KEY_TYPE_MODE, typeMode as Serializable)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.dialogs.DeleteNodesDialogFragment
|
||||
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
|
||||
@@ -431,7 +432,19 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
|
||||
}
|
||||
|
||||
protected fun lockAndExit() {
|
||||
sendBroadcast(Intent(LOCK_ACTION))
|
||||
// Ask confirmation if modification not saved
|
||||
if (mDatabase?.isReadOnly == false
|
||||
&& mDatabase?.dataModifiedSinceLastLoading == true
|
||||
&& !PreferencesUtil.isAutoSaveDatabaseEnabled(this)) {
|
||||
AlertDialog.Builder(this)
|
||||
.setMessage(R.string.discard_changes)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.lock) { _, _ ->
|
||||
sendBroadcast(Intent(LOCK_ACTION))
|
||||
}.create().show()
|
||||
} else {
|
||||
sendBroadcast(Intent(LOCK_ACTION))
|
||||
}
|
||||
}
|
||||
|
||||
fun resetAppTimeout() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.kunzisoft.keepass.activities.legacy
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
@@ -95,11 +96,9 @@ abstract class DatabaseModeActivity : DatabaseActivity() {
|
||||
|
||||
private fun backToTheMainAppAndFinish() {
|
||||
// To move the app in background and return to the main app
|
||||
// Not visible as opened with FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
|
||||
moveTaskToBack(true)
|
||||
// To remove this instance in the OS app selector
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
finish()
|
||||
}, 500)
|
||||
// Not finish() to prevent service kill
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.kunzisoft.keepass.adapters
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.kunzisoft.keepass.activities.fragments.PassphraseGeneratorFragment
|
||||
import com.kunzisoft.keepass.activities.fragments.PasswordGeneratorFragment
|
||||
import com.kunzisoft.keepass.activities.fragments.KeyGeneratorFragment
|
||||
|
||||
class KeyGeneratorPagerAdapter(fragment: Fragment)
|
||||
: FragmentStateAdapter(fragment) {
|
||||
|
||||
private val passwordGeneratorFragment = PasswordGeneratorFragment()
|
||||
private val passphraseGeneratorFragment = PassphraseGeneratorFragment()
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return KeyGeneratorFragment.KeyGeneratorTab.values().size
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (KeyGeneratorFragment.KeyGeneratorTab.getKeyGeneratorTabByPosition(position)) {
|
||||
KeyGeneratorFragment.KeyGeneratorTab.PASSWORD -> passwordGeneratorFragment
|
||||
KeyGeneratorFragment.KeyGeneratorTab.PASSPHRASE -> passphraseGeneratorFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,7 +516,7 @@ class NodesAdapter (private val context: Context,
|
||||
null -> {}
|
||||
}
|
||||
holder?.otpToken?.apply {
|
||||
text = otpElement?.token
|
||||
text = otpElement?.tokenString
|
||||
setTextSize(mTextSizeUnit, mOtpTokenTextDefaultDimension, mPrefSizeMultiplier)
|
||||
}
|
||||
holder?.otpContainer?.setOnClickListener {
|
||||
|
||||
@@ -26,7 +26,7 @@ import kotlinx.coroutines.*
|
||||
*/
|
||||
class IOActionTask<T>(
|
||||
private val action: () -> T ,
|
||||
private val afterActionDatabaseListener: ((T?) -> Unit)? = null) {
|
||||
private val afterActionListener: ((T?) -> Unit)? = null) {
|
||||
|
||||
private val mainScope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
@@ -42,7 +42,7 @@ class IOActionTask<T>(
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
afterActionDatabaseListener?.invoke(asyncResult.await())
|
||||
afterActionListener?.invoke(asyncResult.await())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ import com.kunzisoft.keepass.model.CipherEncryptDatabase
|
||||
import com.kunzisoft.keepass.model.CredentialStorage
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
|
||||
import com.kunzisoft.keepass.view.hideByFading
|
||||
import com.kunzisoft.keepass.view.showByFading
|
||||
import com.kunzisoft.keepass.viewmodels.AdvancedUnlockViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -579,10 +581,13 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU
|
||||
|
||||
private fun showViews(show: Boolean) {
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
mAdvancedUnlockInfoView?.visibility = if (show)
|
||||
View.VISIBLE
|
||||
if (show) {
|
||||
if (mAdvancedUnlockInfoView?.visibility != View.VISIBLE)
|
||||
mAdvancedUnlockInfoView?.showByFading()
|
||||
}
|
||||
else {
|
||||
View.GONE
|
||||
if (mAdvancedUnlockInfoView?.visibility == View.VISIBLE)
|
||||
mAdvancedUnlockInfoView?.hideByFading()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,26 +613,6 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU
|
||||
}
|
||||
}
|
||||
|
||||
fun performEducation(passwordActivityEducation: PasswordActivityEducation,
|
||||
readOnlyEducationPerformed: Boolean,
|
||||
onEducationViewClick: ((TapTargetView?) -> Unit)? = null,
|
||||
onOuterViewClick: ((TapTargetView?) -> Unit)? = null) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& !readOnlyEducationPerformed) {
|
||||
val biometricCanAuthenticate = AdvancedUnlockManager.canAuthenticate(requireContext())
|
||||
PreferencesUtil.isAdvancedUnlockEnable(requireContext())
|
||||
&& (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS)
|
||||
&& mAdvancedUnlockInfoView != null && mAdvancedUnlockInfoView?.visibility == View.VISIBLE
|
||||
&& mAdvancedUnlockInfoView?.unlockIconImageView != null
|
||||
&& passwordActivityEducation.checkAndPerformedBiometricEducation(mAdvancedUnlockInfoView!!.unlockIconImageView!!,
|
||||
onEducationViewClick,
|
||||
onOuterViewClick)
|
||||
}
|
||||
} catch (ignored: Exception) {}
|
||||
}
|
||||
|
||||
enum class Mode {
|
||||
BIOMETRIC_UNAVAILABLE,
|
||||
BIOMETRIC_SECURITY_UPDATE_REQUIRED,
|
||||
|
||||
@@ -124,7 +124,7 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSecretKey(): SecretKey? {
|
||||
@Synchronized private fun getSecretKey(): SecretKey? {
|
||||
if (!isKeyManagerInitialized) {
|
||||
return null
|
||||
}
|
||||
@@ -141,8 +141,8 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity)
|
||||
KeyGenParameterSpec.Builder(
|
||||
ADVANCED_UNLOCK_KEYSTORE_KEY,
|
||||
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
||||
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
|
||||
.setBlockModes(ADVANCED_UNLOCK_BLOCKS_MODES)
|
||||
.setEncryptionPaddings(ADVANCED_UNLOCK_ENCRYPTION_PADDING)
|
||||
.apply {
|
||||
// Require the user to authenticate with a fingerprint to authorize every use
|
||||
// of the key, don't use it for device credential because it's the user authentication
|
||||
@@ -173,11 +173,11 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity)
|
||||
return null
|
||||
}
|
||||
|
||||
fun initEncryptData(actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit,) {
|
||||
@Synchronized fun initEncryptData(actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit,) {
|
||||
initEncryptData(actionIfCypherInit, true)
|
||||
}
|
||||
|
||||
private fun initEncryptData(actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit,
|
||||
@Synchronized private fun initEncryptData(actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit,
|
||||
firstLaunch: Boolean) {
|
||||
if (!isKeyManagerInitialized) {
|
||||
return
|
||||
@@ -213,7 +213,7 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity)
|
||||
}
|
||||
}
|
||||
|
||||
fun encryptData(value: ByteArray) {
|
||||
@Synchronized fun encryptData(value: ByteArray) {
|
||||
if (!isKeyManagerInitialized) {
|
||||
return
|
||||
}
|
||||
@@ -229,12 +229,12 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity)
|
||||
}
|
||||
}
|
||||
|
||||
fun initDecryptData(ivSpecValue: ByteArray,
|
||||
@Synchronized fun initDecryptData(ivSpecValue: ByteArray,
|
||||
actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit) {
|
||||
initDecryptData(ivSpecValue, actionIfCypherInit, true)
|
||||
}
|
||||
|
||||
private fun initDecryptData(ivSpecValue: ByteArray,
|
||||
@Synchronized private fun initDecryptData(ivSpecValue: ByteArray,
|
||||
actionIfCypherInit: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit,
|
||||
firstLaunch: Boolean = true) {
|
||||
if (!isKeyManagerInitialized) {
|
||||
@@ -278,7 +278,7 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity)
|
||||
}
|
||||
}
|
||||
|
||||
fun decryptData(encryptedValue: ByteArray) {
|
||||
@Synchronized fun decryptData(encryptedValue: ByteArray) {
|
||||
if (!isKeyManagerInitialized) {
|
||||
return
|
||||
}
|
||||
@@ -296,7 +296,7 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteKeystoreKey() {
|
||||
@Synchronized fun deleteKeystoreKey() {
|
||||
try {
|
||||
keyStore?.load(null)
|
||||
keyStore?.deleteEntry(ADVANCED_UNLOCK_KEYSTORE_KEY)
|
||||
@@ -306,7 +306,7 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity)
|
||||
}
|
||||
}
|
||||
|
||||
fun openAdvancedUnlockPrompt(cryptoPrompt: AdvancedUnlockCryptoPrompt,
|
||||
@Synchronized fun openAdvancedUnlockPrompt(cryptoPrompt: AdvancedUnlockCryptoPrompt,
|
||||
deviceCredentialResultLauncher: ActivityResultLauncher<Intent>
|
||||
) {
|
||||
// Init advanced unlock prompt
|
||||
@@ -346,7 +346,7 @@ class AdvancedUnlockManager(private var retrieveContext: () -> FragmentActivity)
|
||||
}
|
||||
}
|
||||
|
||||
fun closeBiometricPrompt() {
|
||||
@Synchronized fun closeBiometricPrompt() {
|
||||
biometricPrompt?.cancelAuthentication()
|
||||
}
|
||||
|
||||
|
||||
@@ -913,53 +913,24 @@ class Database {
|
||||
try {
|
||||
val saveUri = databaseCopyUri ?: this.fileUri
|
||||
if (saveUri != null) {
|
||||
if (saveUri.scheme == "file") {
|
||||
saveUri.path?.let { filename ->
|
||||
val tempFile = File("$filename.tmp")
|
||||
|
||||
var fileOutputStream: FileOutputStream? = null
|
||||
try {
|
||||
fileOutputStream = FileOutputStream(tempFile)
|
||||
val pmo = mDatabaseKDB?.let { DatabaseOutputKDB(it, fileOutputStream) }
|
||||
?: mDatabaseKDBX?.let { DatabaseOutputKDBX(it, fileOutputStream) }
|
||||
pmo?.output()
|
||||
} catch (e: Exception) {
|
||||
throw IOException(e)
|
||||
} finally {
|
||||
fileOutputStream?.close()
|
||||
}
|
||||
|
||||
// Force data to disk before continuing
|
||||
try {
|
||||
fileOutputStream?.fd?.sync()
|
||||
} catch (e: SyncFailedException) {
|
||||
// Ignore if fsync fails. We tried.
|
||||
}
|
||||
|
||||
if (!tempFile.renameTo(File(filename))) {
|
||||
throw IOException()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var outputStream: OutputStream? = null
|
||||
try {
|
||||
outputStream = contentResolver.openOutputStream(saveUri, "rwt")
|
||||
outputStream?.let { definedOutputStream ->
|
||||
val databaseOutput =
|
||||
mDatabaseKDB?.let { DatabaseOutputKDB(it, definedOutputStream) }
|
||||
?: mDatabaseKDBX?.let {
|
||||
DatabaseOutputKDBX(
|
||||
it,
|
||||
definedOutputStream
|
||||
)
|
||||
}
|
||||
databaseOutput?.output()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw IOException(e)
|
||||
} finally {
|
||||
outputStream?.close()
|
||||
var outputStream: OutputStream? = null
|
||||
try {
|
||||
outputStream = UriUtil.getUriOutputStream(contentResolver, saveUri)
|
||||
outputStream?.let { definedOutputStream ->
|
||||
val databaseOutput =
|
||||
mDatabaseKDB?.let { DatabaseOutputKDB(it, definedOutputStream) }
|
||||
?: mDatabaseKDBX?.let {
|
||||
DatabaseOutputKDBX(
|
||||
it,
|
||||
definedOutputStream
|
||||
)
|
||||
}
|
||||
databaseOutput?.output()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw IOException(e)
|
||||
} finally {
|
||||
outputStream?.close()
|
||||
}
|
||||
if (databaseCopyUri == null) {
|
||||
this.dataModifiedSinceLastLoading = false
|
||||
|
||||
@@ -60,6 +60,11 @@ object TemplateField {
|
||||
const val LABEL_SECURE_NOTE = "Secure Note"
|
||||
const val LABEL_MEMBERSHIP = "Membership"
|
||||
|
||||
fun isStandardPasswordName(context: Context, name: String): Boolean {
|
||||
return name.equals(LABEL_PASSWORD, true)
|
||||
|| name == getLocalizedName(context, LABEL_PASSWORD)
|
||||
}
|
||||
|
||||
fun isStandardFieldName(name: String): Boolean {
|
||||
return arrayOf(
|
||||
LABEL_TITLE,
|
||||
|
||||
@@ -148,9 +148,9 @@ class SearchHelper {
|
||||
onDatabaseClosed.invoke()
|
||||
} else if (TimeoutHelper.checkTime(context)) {
|
||||
var searchWithoutUI = false
|
||||
if (PreferencesUtil.isAutofillAutoSearchEnable(context)
|
||||
&& searchInfo != null && !searchInfo.manualSelection
|
||||
&& !searchInfo.containsOnlyNullValues()) {
|
||||
if (searchInfo != null
|
||||
&& !searchInfo.manualSelection
|
||||
&& !searchInfo.containsOnlyNullValues()) {
|
||||
// If search provide results
|
||||
database.createVirtualGroupFromSearchInfo(
|
||||
searchInfo.toString(),
|
||||
@@ -181,7 +181,7 @@ class SearchHelper {
|
||||
return false
|
||||
|
||||
// Exclude entry expired
|
||||
if (searchParameters.excludeExpired) {
|
||||
if (!searchParameters.searchInExpired) {
|
||||
if (entry.isCurrentlyExpires)
|
||||
return false
|
||||
}
|
||||
@@ -237,14 +237,20 @@ class SearchHelper {
|
||||
return false
|
||||
return if (searchParameters.isRegex) {
|
||||
val regex = if (searchParameters.caseSensitive) {
|
||||
searchParameters.searchQuery.toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
searchParameters.searchQuery
|
||||
.toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
} else {
|
||||
searchParameters.searchQuery
|
||||
.toRegex(setOf(RegexOption.DOT_MATCHES_ALL, RegexOption.IGNORE_CASE))
|
||||
}
|
||||
regex.matches(stringToCheck)
|
||||
} else {
|
||||
stringToCheck.contains(searchParameters.searchQuery, !searchParameters.caseSensitive)
|
||||
var searchFound = true
|
||||
searchParameters.searchQuery.split(" ").forEach { word ->
|
||||
searchFound = searchFound
|
||||
&& stringToCheck.contains(word, !searchParameters.caseSensitive)
|
||||
}
|
||||
searchFound
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class SearchParameters() : Parcelable{
|
||||
var searchInUsernames = true
|
||||
var searchInPasswords = false
|
||||
var searchInUrls = true
|
||||
var excludeExpired = false
|
||||
var searchInExpired = false
|
||||
var searchInNotes = true
|
||||
var searchInOTP = false
|
||||
var searchInOther = true
|
||||
@@ -49,11 +49,12 @@ class SearchParameters() : Parcelable{
|
||||
constructor(parcel: Parcel) : this() {
|
||||
searchQuery = parcel.readString() ?: searchQuery
|
||||
caseSensitive = parcel.readByte() != 0.toByte()
|
||||
isRegex = parcel.readByte() != 0.toByte()
|
||||
searchInTitles = parcel.readByte() != 0.toByte()
|
||||
searchInUsernames = parcel.readByte() != 0.toByte()
|
||||
searchInPasswords = parcel.readByte() != 0.toByte()
|
||||
searchInUrls = parcel.readByte() != 0.toByte()
|
||||
excludeExpired = parcel.readByte() != 0.toByte()
|
||||
searchInExpired = parcel.readByte() != 0.toByte()
|
||||
searchInNotes = parcel.readByte() != 0.toByte()
|
||||
searchInOTP = parcel.readByte() != 0.toByte()
|
||||
searchInOther = parcel.readByte() != 0.toByte()
|
||||
@@ -68,11 +69,12 @@ class SearchParameters() : Parcelable{
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(searchQuery)
|
||||
parcel.writeByte(if (caseSensitive) 1 else 0)
|
||||
parcel.writeByte(if (isRegex) 1 else 0)
|
||||
parcel.writeByte(if (searchInTitles) 1 else 0)
|
||||
parcel.writeByte(if (searchInUsernames) 1 else 0)
|
||||
parcel.writeByte(if (searchInPasswords) 1 else 0)
|
||||
parcel.writeByte(if (searchInUrls) 1 else 0)
|
||||
parcel.writeByte(if (excludeExpired) 1 else 0)
|
||||
parcel.writeByte(if (searchInExpired) 1 else 0)
|
||||
parcel.writeByte(if (searchInNotes) 1 else 0)
|
||||
parcel.writeByte(if (searchInOTP) 1 else 0)
|
||||
parcel.writeByte(if (searchInOther) 1 else 0)
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.kunzisoft.keepass.magikeyboard;
|
||||
|
||||
import static com.kunzisoft.keepass.magikeyboard.MagikeyboardService.KEY_BACK_KEYBOARD;
|
||||
import static com.kunzisoft.keepass.magikeyboard.MagikeyboardService.KEY_CHANGE_KEYBOARD;
|
||||
import static com.kunzisoft.keepass.magikeyboard.MagikeyboardService.KEY_ENTRY;
|
||||
import static com.kunzisoft.keepass.magikeyboard.MagikeyboardService.KEY_ENTRY_ALT;
|
||||
import static com.kunzisoft.keepass.magikeyboard.MagikeyboardService.KEY_OTP;
|
||||
import static com.kunzisoft.keepass.magikeyboard.MagikeyboardService.KEY_OTP_ALT;
|
||||
|
||||
@@ -1049,6 +1051,9 @@ public class KeyboardView extends View implements View.OnClickListener {
|
||||
if (popupKey.codes[0] == KEY_BACK_KEYBOARD) {
|
||||
mKeyboardActionListener.onKey(KEY_CHANGE_KEYBOARD, popupKey.codes);
|
||||
return true;
|
||||
} else if (popupKey.codes[0] == KEY_ENTRY) {
|
||||
mKeyboardActionListener.onKey(KEY_ENTRY_ALT, popupKey.codes);
|
||||
return true;
|
||||
} else if (popupKey.codes[0] == KEY_OTP) {
|
||||
mKeyboardActionListener.onKey(KEY_OTP_ALT, popupKey.codes);
|
||||
return true;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
package com.kunzisoft.keepass.magikeyboard
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.inputmethodservice.InputMethodService
|
||||
@@ -30,19 +31,28 @@ import android.view.*
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||
import androidx.core.graphics.BlendModeCompat
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.MagikeyboardLauncherActivity
|
||||
import com.kunzisoft.keepass.activities.EntrySelectionLauncherActivity
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.adapters.FieldsAdapter
|
||||
import com.kunzisoft.keepass.database.action.DatabaseTaskProvider
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Field
|
||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||
import com.kunzisoft.keepass.model.EntryInfo
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD
|
||||
import com.kunzisoft.keepass.services.KeyboardEntryNotificationService
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.utils.*
|
||||
import java.util.*
|
||||
|
||||
class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionListener {
|
||||
|
||||
@@ -50,13 +60,19 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
private var keyboardView: KeyboardView? = null
|
||||
private var entryContainer: View? = null
|
||||
private var entryText: TextView? = null
|
||||
private var databaseText: TextView? = null
|
||||
private var databaseColorView: ImageView? = null
|
||||
private var packageText: TextView? = null
|
||||
private var keyboard: Keyboard? = null
|
||||
private var keyboardEntry: Keyboard? = null
|
||||
private var popupCustomKeys: PopupWindow? = null
|
||||
private var fieldsAdapter: FieldsAdapter? = null
|
||||
private var playSoundDuringCLick: Boolean = false
|
||||
|
||||
private var mFormPackageName: String? = null
|
||||
|
||||
private var lockReceiver: LockReceiver? = null
|
||||
|
||||
override fun onCreate() {
|
||||
@@ -66,6 +82,7 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
mDatabaseTaskProvider?.registerProgressTask()
|
||||
mDatabaseTaskProvider?.onDatabaseRetrieved = { database ->
|
||||
this.mDatabase = database
|
||||
assignKeyboardView()
|
||||
}
|
||||
// Remove the entry and lock the keyboard when the lock signal is receive
|
||||
lockReceiver = LockReceiver {
|
||||
@@ -82,7 +99,11 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
override fun onCreateInputView(): View {
|
||||
|
||||
val rootKeyboardView = layoutInflater.inflate(R.layout.keyboard_container, null)
|
||||
entryContainer = rootKeyboardView.findViewById(R.id.magikeyboard_entry_container)
|
||||
entryText = rootKeyboardView.findViewById(R.id.magikeyboard_entry_text)
|
||||
databaseText = rootKeyboardView.findViewById(R.id.magikeyboard_database_text)
|
||||
databaseColorView = rootKeyboardView.findViewById(R.id.magikeyboard_database_color)
|
||||
packageText = rootKeyboardView.findViewById(R.id.magikeyboard_package_text)
|
||||
keyboardView = rootKeyboardView.findViewById(R.id.magikeyboard_view)
|
||||
|
||||
if (keyboardView != null) {
|
||||
@@ -94,7 +115,7 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
.inflate(R.layout.keyboard_popup_fields, FrameLayout(context))
|
||||
|
||||
popupCustomKeys = PopupWindow(context).apply {
|
||||
width = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
width = WindowManager.LayoutParams.MATCH_PARENT
|
||||
height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
inputMethodMode = PopupWindow.INPUT_METHOD_NEEDED
|
||||
contentView = popupFieldsView
|
||||
@@ -104,7 +125,7 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
fieldsAdapter = FieldsAdapter(this)
|
||||
fieldsAdapter?.onItemClickListener = object : FieldsAdapter.OnItemClickListener {
|
||||
override fun onItemClick(item: Field) {
|
||||
currentInputConnection.commitText(entryInfoKey?.getGeneratedFieldValue(item.name) , 1)
|
||||
currentInputConnection.commitText(getEntryInfo()?.getGeneratedFieldValue(item.name) , 1)
|
||||
actionTabAutomatically()
|
||||
}
|
||||
}
|
||||
@@ -114,17 +135,6 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
val closeView = popupFieldsView.findViewById<View>(R.id.keyboard_popup_close)
|
||||
closeView.setOnClickListener { popupCustomKeys?.dismiss() }
|
||||
|
||||
// Remove entry info if the database is not loaded
|
||||
// or if entry info timestamp is before database loaded timestamp
|
||||
val databaseTime = mDatabase?.loadTimestamp
|
||||
val entryTime = entryInfoTimestamp
|
||||
if (mDatabase == null
|
||||
|| mDatabase?.loaded != true
|
||||
|| databaseTime == null
|
||||
|| entryTime == null
|
||||
|| entryTime < databaseTime) {
|
||||
removeEntryInfo()
|
||||
}
|
||||
assignKeyboardView()
|
||||
keyboardView?.onKeyboardActionListener = this
|
||||
|
||||
@@ -134,17 +144,27 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
return super.onCreateInputView()
|
||||
}
|
||||
|
||||
private fun getEntryInfo(): EntryInfo? {
|
||||
var entryInfoRetrieved: EntryInfo? = null
|
||||
entryUUID?.let { entryId ->
|
||||
entryInfoRetrieved = mDatabase
|
||||
?.getEntryById(NodeIdUUID(entryId))
|
||||
?.getEntryInfo(mDatabase)
|
||||
}
|
||||
return entryInfoRetrieved
|
||||
}
|
||||
|
||||
private fun assignKeyboardView() {
|
||||
dismissCustomKeys()
|
||||
if (keyboardView != null) {
|
||||
if (entryInfoKey != null) {
|
||||
val entryInfo = getEntryInfo()
|
||||
populateEntryInfoInView(entryInfo)
|
||||
if (entryInfo != null) {
|
||||
if (keyboardEntry != null) {
|
||||
populateEntryInfoInView()
|
||||
keyboardView?.keyboard = keyboardEntry
|
||||
}
|
||||
} else {
|
||||
if (keyboard != null) {
|
||||
hideEntryInfo()
|
||||
keyboardView?.keyboard = keyboard
|
||||
}
|
||||
}
|
||||
@@ -153,23 +173,45 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
keyboardView?.isHapticFeedbackEnabled = PreferencesUtil.isKeyboardVibrationEnable(this)
|
||||
playSoundDuringCLick = PreferencesUtil.isKeyboardSoundEnable(this)
|
||||
}
|
||||
setDatabaseViews()
|
||||
}
|
||||
|
||||
private fun populateEntryInfoInView() {
|
||||
entryText?.visibility = View.VISIBLE
|
||||
if (entryInfoKey?.title?.isNotEmpty() == true) {
|
||||
entryText?.text = entryInfoKey?.title
|
||||
private fun setDatabaseViews() {
|
||||
if (mDatabase == null || mDatabase?.loaded != true) {
|
||||
entryContainer?.visibility = View.GONE
|
||||
} else {
|
||||
hideEntryInfo()
|
||||
entryContainer?.visibility = View.VISIBLE
|
||||
}
|
||||
databaseText?.text = mDatabase?.name ?: ""
|
||||
val databaseColor = mDatabase?.customColor
|
||||
if (databaseColor != null) {
|
||||
databaseColorView?.drawable?.colorFilter = BlendModeColorFilterCompat
|
||||
.createBlendModeColorFilterCompat(databaseColor, BlendModeCompat.SRC_IN)
|
||||
databaseColorView?.visibility = View.VISIBLE
|
||||
} else {
|
||||
databaseColorView?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideEntryInfo() {
|
||||
entryText?.visibility = View.GONE
|
||||
private fun populateEntryInfoInView(entryInfo: EntryInfo?) {
|
||||
if (entryInfo == null) {
|
||||
entryText?.text = ""
|
||||
entryText?.visibility = View.GONE
|
||||
} else {
|
||||
entryText?.text = entryInfo.getVisualTitle()
|
||||
entryText?.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartInputView(info: EditorInfo, restarting: Boolean) {
|
||||
super.onStartInputView(info, restarting)
|
||||
mFormPackageName = info.packageName
|
||||
if (!mFormPackageName.isNullOrEmpty()) {
|
||||
packageText?.text = mFormPackageName
|
||||
packageText?.visibility = View.VISIBLE
|
||||
} else {
|
||||
packageText?.visibility = View.GONE
|
||||
}
|
||||
assignKeyboardView()
|
||||
}
|
||||
|
||||
@@ -228,16 +270,17 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
(getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?)
|
||||
?.showInputMethodPicker()
|
||||
}
|
||||
KEY_UNLOCK -> {
|
||||
}
|
||||
KEY_ENTRY -> {
|
||||
// Stop current service and reinit entry
|
||||
stopService(Intent(this, KeyboardEntryNotificationService::class.java))
|
||||
removeEntryInfo()
|
||||
val intent = Intent(this, MagikeyboardLauncherActivity::class.java)
|
||||
// New task needed because don't launch from an Activity context
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
var searchInfo: SearchInfo? = null
|
||||
if (mFormPackageName != null) {
|
||||
searchInfo = SearchInfo().apply {
|
||||
applicationId = mFormPackageName
|
||||
}
|
||||
}
|
||||
actionKeyEntry(searchInfo)
|
||||
}
|
||||
KEY_ENTRY_ALT -> {
|
||||
actionKeyEntry()
|
||||
}
|
||||
KEY_LOCK -> {
|
||||
removeEntryInfo()
|
||||
@@ -245,12 +288,13 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
dismissCustomKeys()
|
||||
}
|
||||
KEY_USERNAME -> {
|
||||
entryInfoKey?.username?.let { username ->
|
||||
getEntryInfo()?.username?.let { username ->
|
||||
currentInputConnection.commitText(username, 1)
|
||||
}
|
||||
actionTabAutomatically()
|
||||
}
|
||||
KEY_PASSWORD -> {
|
||||
val entryInfoKey = getEntryInfo()
|
||||
entryInfoKey?.password?.let { password ->
|
||||
currentInputConnection.commitText(password, 1)
|
||||
}
|
||||
@@ -258,14 +302,14 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
actionGoAutomatically(!otpFieldExists)
|
||||
}
|
||||
KEY_OTP -> {
|
||||
entryInfoKey?.let { entryInfo ->
|
||||
getEntryInfo()?.let { entryInfo ->
|
||||
currentInputConnection.commitText(
|
||||
entryInfo.getGeneratedFieldValue(OTP_TOKEN_FIELD), 1)
|
||||
}
|
||||
actionGoAutomatically()
|
||||
}
|
||||
KEY_OTP_ALT -> {
|
||||
entryInfoKey?.let { entryInfo ->
|
||||
getEntryInfo()?.let { entryInfo ->
|
||||
val otpToken = entryInfo.getGeneratedFieldValue(OTP_TOKEN_FIELD)
|
||||
if (otpToken.isNotEmpty()) {
|
||||
// Cut to fill each digit separatelyKeyEvent.KEYCODE_TAB
|
||||
@@ -282,19 +326,20 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
actionGoAutomatically()
|
||||
}
|
||||
KEY_URL -> {
|
||||
entryInfoKey?.url?.let { url ->
|
||||
getEntryInfo()?.url?.let { url ->
|
||||
currentInputConnection.commitText(url, 1)
|
||||
}
|
||||
actionGoAutomatically()
|
||||
}
|
||||
KEY_FIELDS -> {
|
||||
entryInfoKey?.customFields?.let { customFields ->
|
||||
getEntryInfo()?.customFields?.let { customFields ->
|
||||
fieldsAdapter?.apply {
|
||||
setFields(customFields.filter { it.name != OTP_TOKEN_FIELD})
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
popupCustomKeys?.showAtLocation(keyboardView, Gravity.END or Gravity.TOP, 0, 0)
|
||||
popupCustomKeys?.showAtLocation(keyboardView,
|
||||
Gravity.END or Gravity.TOP, 0, 180)
|
||||
}
|
||||
Keyboard.KEYCODE_DELETE -> {
|
||||
inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
|
||||
@@ -303,6 +348,43 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
}
|
||||
}
|
||||
|
||||
private fun actionKeyEntry(searchInfo: SearchInfo? = null) {
|
||||
SearchHelper.checkAutoSearchInfo(this,
|
||||
mDatabase,
|
||||
searchInfo,
|
||||
{ _, items ->
|
||||
performSelection(
|
||||
items,
|
||||
{
|
||||
// Automatically populate keyboard
|
||||
addEntryAndLaunchNotificationIfAllowed(
|
||||
this,
|
||||
items[0],
|
||||
true
|
||||
)
|
||||
assignKeyboardView()
|
||||
},
|
||||
{
|
||||
launchEntrySelection(searchInfo)
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
// Select if not found
|
||||
launchEntrySelection(searchInfo)
|
||||
},
|
||||
{
|
||||
// Select if database not opened
|
||||
removeEntryInfo()
|
||||
launchEntrySelection(searchInfo)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun launchEntrySelection(searchInfo: SearchInfo?) {
|
||||
EntrySelectionLauncherActivity.launch(this, searchInfo)
|
||||
}
|
||||
|
||||
private fun actionTabAutomatically() {
|
||||
if (PreferencesUtil.isAutoGoActionEnable(this))
|
||||
currentInputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB))
|
||||
@@ -359,23 +441,20 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
|
||||
const val KEY_BACK_KEYBOARD = 600
|
||||
const val KEY_CHANGE_KEYBOARD = 601
|
||||
private const val KEY_UNLOCK = 610
|
||||
private const val KEY_LOCK = 611
|
||||
private const val KEY_ENTRY = 620
|
||||
private const val KEY_USERNAME = 500
|
||||
private const val KEY_PASSWORD = 510
|
||||
const val KEY_LOCK = 611
|
||||
const val KEY_ENTRY = 620
|
||||
const val KEY_ENTRY_ALT = 621
|
||||
const val KEY_USERNAME = 500
|
||||
const val KEY_PASSWORD = 510
|
||||
const val KEY_OTP = 515
|
||||
const val KEY_OTP_ALT = 516
|
||||
private const val KEY_URL = 520
|
||||
private const val KEY_FIELDS = 530
|
||||
const val KEY_URL = 520
|
||||
const val KEY_FIELDS = 530
|
||||
|
||||
// TODO Retrieve entry info from id and service when database is open
|
||||
private var entryInfoKey: EntryInfo? = null
|
||||
private var entryInfoTimestamp: Long? = null
|
||||
private var entryUUID: UUID? = null
|
||||
|
||||
private fun removeEntryInfo() {
|
||||
entryInfoKey = null
|
||||
entryInfoTimestamp = null
|
||||
entryUUID = null
|
||||
}
|
||||
|
||||
fun removeEntry(context: Context) {
|
||||
@@ -384,10 +463,47 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
||||
|
||||
fun addEntryAndLaunchNotificationIfAllowed(context: Context, entry: EntryInfo, toast: Boolean = false) {
|
||||
// Add a new entry
|
||||
entryInfoKey = entry
|
||||
entryInfoTimestamp = System.currentTimeMillis()
|
||||
entryUUID = entry.id
|
||||
// Launch notification if allowed
|
||||
KeyboardEntryNotificationService.launchNotificationIfAllowed(context, entry, toast)
|
||||
}
|
||||
|
||||
fun activatedInSettings(context: Context): Boolean {
|
||||
return ContextCompat.getSystemService(context, InputMethodManager::class.java)
|
||||
?.enabledInputMethodList
|
||||
?.any {
|
||||
it.packageName == context.packageName
|
||||
} ?: false
|
||||
}
|
||||
|
||||
fun performSelection(items: List<EntryInfo>,
|
||||
actionPopulateKeyboard: (entryInfo: EntryInfo) -> Unit,
|
||||
actionEntrySelection: (autoSearch: Boolean) -> Unit) {
|
||||
if (items.size == 1) {
|
||||
val itemFound = items[0]
|
||||
if (entryUUID != itemFound.id) {
|
||||
actionPopulateKeyboard.invoke(itemFound)
|
||||
} else {
|
||||
// Force selection if magikeyboard already populated
|
||||
actionEntrySelection.invoke(false)
|
||||
}
|
||||
} else if (items.size > 1) {
|
||||
// Select the one we want in the selection
|
||||
actionEntrySelection.invoke(true)
|
||||
} else {
|
||||
// Select an arbitrary one
|
||||
actionEntrySelection.invoke(false)
|
||||
}
|
||||
}
|
||||
|
||||
fun populateKeyboardAndMoveAppToBackground(activity: Activity,
|
||||
entry: EntryInfo,
|
||||
toast: Boolean = true) {
|
||||
// Populate Magikeyboard with entry
|
||||
addEntryAndLaunchNotificationIfAllowed(activity, entry, toast)
|
||||
// Consume the selection mode
|
||||
EntrySelectionHelper.removeModesFromIntent(activity.intent)
|
||||
activity.moveTaskToBack(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,6 +225,14 @@ class EntryInfo : NodeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fun getVisualTitle(): String {
|
||||
return title.ifEmpty {
|
||||
url.ifEmpty {
|
||||
username.ifEmpty { id.toString() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is EntryInfo) return false
|
||||
|
||||
@@ -178,6 +178,14 @@ data class OtpElement(var otpModel: OtpModel = OtpModel()) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Token with space each 3 digits
|
||||
*/
|
||||
val tokenString: String
|
||||
get() {
|
||||
return token.replace("...".toRegex(), "$0 ")
|
||||
}
|
||||
|
||||
val secondsRemaining: Int
|
||||
get() = otpModel.period - (System.currentTimeMillis() / 1000 % otpModel.period).toInt()
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.password
|
||||
|
||||
import me.gosimple.nbvcxz.resources.Generator
|
||||
|
||||
class PassphraseGenerator {
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun generatePassphrase(wordCount: Int,
|
||||
wordSeparator: String,
|
||||
wordCase: WordCase): String {
|
||||
// From eff_large dictionary
|
||||
return when (wordCase) {
|
||||
WordCase.LOWER_CASE -> {
|
||||
Generator.generatePassphrase(wordSeparator, wordCount)
|
||||
}
|
||||
WordCase.UPPER_CASE -> {
|
||||
applyWordCase(wordCount, wordSeparator) { word ->
|
||||
word.uppercase()
|
||||
}
|
||||
}
|
||||
WordCase.TITLE_CASE -> {
|
||||
applyWordCase(wordCount, wordSeparator) { word ->
|
||||
word.replaceFirstChar { char -> char.uppercaseChar() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyWordCase(wordCount: Int,
|
||||
wordSeparator: String,
|
||||
wordAction: (word: String) -> String): String {
|
||||
val splitWords = Generator.generatePassphrase(TEMP_SPLIT, wordCount).split(TEMP_SPLIT)
|
||||
val stringBuilder = StringBuilder()
|
||||
splitWords.forEach {
|
||||
stringBuilder
|
||||
.append(wordAction(it))
|
||||
.append(wordSeparator)
|
||||
}
|
||||
return stringBuilder.toString().removeSuffix(wordSeparator)
|
||||
}
|
||||
|
||||
enum class WordCase {
|
||||
LOWER_CASE,
|
||||
UPPER_CASE,
|
||||
TITLE_CASE;
|
||||
|
||||
companion object {
|
||||
fun getByOrdinal(position: Int): WordCase {
|
||||
return when (position) {
|
||||
0 -> LOWER_CASE
|
||||
1 -> UPPER_CASE
|
||||
else -> TITLE_CASE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TEMP_SPLIT = "-"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2022 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.password
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.app.database.IOActionTask
|
||||
import kotlinx.coroutines.*
|
||||
import me.gosimple.nbvcxz.Nbvcxz
|
||||
import me.gosimple.nbvcxz.resources.Configuration
|
||||
import me.gosimple.nbvcxz.resources.ConfigurationBuilder
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
class PasswordEntropy(actionOnInitFinished: (() -> Unit)? = null) {
|
||||
|
||||
private var mPasswordEntropyCalculator: Nbvcxz? = null
|
||||
private var entropyJob: Job? = null
|
||||
|
||||
init {
|
||||
IOActionTask({
|
||||
// Create the password generator object
|
||||
val configuration: Configuration = ConfigurationBuilder()
|
||||
.setLocale(Locale.getDefault())
|
||||
.setMinimumEntropy(80.0)
|
||||
.createConfiguration()
|
||||
mPasswordEntropyCalculator = Nbvcxz(configuration)
|
||||
}, {
|
||||
actionOnInitFinished?.invoke()
|
||||
}).execute()
|
||||
}
|
||||
|
||||
enum class Strength(val color: Int) {
|
||||
RISKY(Color.rgb(224, 56, 56)),
|
||||
VERY_GUESSABLE(Color.rgb(196, 63, 49)),
|
||||
SOMEWHAT_GUESSABLE(Color.rgb(219, 152, 55)),
|
||||
SAFELY_UNGUESSABLE(Color.rgb(118, 168, 24)),
|
||||
VERY_UNGUESSABLE(Color.rgb(37, 152, 41))
|
||||
}
|
||||
|
||||
data class EntropyStrength(val strength: Strength,
|
||||
val entropy: Double,
|
||||
val estimationPercent: Int) {
|
||||
override fun toString(): String {
|
||||
return "EntropyStrength(strength=$strength, entropy=$entropy, estimationPercent=$estimationPercent)"
|
||||
}
|
||||
}
|
||||
|
||||
fun getEntropyStrength(passwordString: String,
|
||||
entropyStrengthResult: (EntropyStrength) -> Unit) {
|
||||
|
||||
entropyStrengthResult.invoke(EntropyStrength(Strength.RISKY, CALCULATE_ENTROPY, 0))
|
||||
entropyJob?.cancel()
|
||||
entropyJob = CoroutineScope(Dispatchers.Main).launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val asyncResult: Deferred<EntropyStrength?> = async {
|
||||
try {
|
||||
if (passwordString.length <= MAX_PASSWORD_LENGTH) {
|
||||
val estimate = mPasswordEntropyCalculator?.estimate(passwordString)
|
||||
val basicScore = estimate?.basicScore ?: 0
|
||||
val entropy = estimate?.entropy ?: 0.0
|
||||
val percentScore = min(entropy * 100 / 200, 100.0).toInt()
|
||||
val strength =
|
||||
if (basicScore == 0 || percentScore < 10) {
|
||||
Strength.RISKY
|
||||
} else if (basicScore == 1 || percentScore < 20) {
|
||||
Strength.VERY_GUESSABLE
|
||||
} else if (basicScore == 2 || percentScore < 33) {
|
||||
Strength.SOMEWHAT_GUESSABLE
|
||||
} else if (basicScore == 3 || percentScore < 50) {
|
||||
Strength.SAFELY_UNGUESSABLE
|
||||
} else if (basicScore == 4) {
|
||||
Strength.VERY_UNGUESSABLE
|
||||
} else {
|
||||
Strength.RISKY
|
||||
}
|
||||
EntropyStrength(strength, entropy, percentScore)
|
||||
} else {
|
||||
EntropyStrength(Strength.VERY_UNGUESSABLE, HIGH_ENTROPY, 100)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
asyncResult.await()?.let { entropyStrength ->
|
||||
entropyStrengthResult.invoke(entropyStrength)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_PASSWORD_LENGTH = 128
|
||||
private const val CALCULATE_ENTROPY = -1.0
|
||||
private const val HIGH_ENTROPY = 1000.0
|
||||
|
||||
fun getStringEntropy(resources: Resources, entropy: Double): String {
|
||||
return when (entropy) {
|
||||
CALCULATE_ENTROPY -> {
|
||||
resources.getString(R.string.entropy_calculate)
|
||||
}
|
||||
HIGH_ENTROPY -> {
|
||||
resources.getString(R.string.entropy_high)
|
||||
}
|
||||
else -> {
|
||||
resources.getString(
|
||||
R.string.entropy,
|
||||
"%.${2}f".format(entropy)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,33 +20,17 @@
|
||||
package com.kunzisoft.keepass.password
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import com.kunzisoft.keepass.R
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
|
||||
class PasswordGenerator(private val resources: Resources) {
|
||||
|
||||
// From KeePassXC code https://github.com/keepassxreboot/keepassxc/pull/538
|
||||
private fun extendedChars(): String {
|
||||
val charSet = StringBuilder()
|
||||
// [U+0080, U+009F] are C1 control characters,
|
||||
// U+00A0 is non-breaking space
|
||||
run {
|
||||
var ch = '\u00A1'
|
||||
while (ch <= '\u00AC') {
|
||||
charSet.append(ch)
|
||||
++ch
|
||||
}
|
||||
}
|
||||
// U+00AD is soft hyphen (format character)
|
||||
var ch = '\u00AE'
|
||||
while (ch < '\u00FF') {
|
||||
charSet.append(ch)
|
||||
++ch
|
||||
}
|
||||
charSet.append('\u00FF')
|
||||
return charSet.toString()
|
||||
}
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun generatePassword(length: Int,
|
||||
upperCase: Boolean,
|
||||
@@ -57,7 +41,11 @@ class PasswordGenerator(private val resources: Resources) {
|
||||
space: Boolean,
|
||||
specials: Boolean,
|
||||
brackets: Boolean,
|
||||
extended: Boolean): String {
|
||||
extended: Boolean,
|
||||
considerChars: String,
|
||||
ignoreChars: String,
|
||||
atLeastOneFromEach: Boolean,
|
||||
excludeAmbiguousChar: Boolean): String {
|
||||
// Desired password length is 0 or less
|
||||
if (length <= 0) {
|
||||
throw IllegalArgumentException(resources.getString(R.string.error_wrong_length))
|
||||
@@ -72,74 +60,164 @@ class PasswordGenerator(private val resources: Resources) {
|
||||
&& !space
|
||||
&& !specials
|
||||
&& !brackets
|
||||
&& !extended) {
|
||||
&& !extended
|
||||
&& considerChars.isEmpty()) {
|
||||
throw IllegalArgumentException(resources.getString(R.string.error_pass_gen_type))
|
||||
}
|
||||
|
||||
val characterSet = getCharacterSet(
|
||||
upperCase,
|
||||
lowerCase,
|
||||
digits,
|
||||
minus,
|
||||
underline,
|
||||
space,
|
||||
specials,
|
||||
brackets,
|
||||
extended)
|
||||
|
||||
val size = characterSet.length
|
||||
|
||||
val buffer = StringBuilder()
|
||||
|
||||
val random = SecureRandom() // use more secure variant of Random!
|
||||
if (size > 0) {
|
||||
for (i in 0 until length) {
|
||||
buffer.append(characterSet[random.nextInt(size)])
|
||||
// Filter builder
|
||||
val passwordFilters = PasswordFilters().apply {
|
||||
this.length = length
|
||||
this.ignoreChars = ignoreChars
|
||||
if (excludeAmbiguousChar)
|
||||
this.ignoreChars += AMBIGUOUS_CHARS
|
||||
if (upperCase) {
|
||||
addFilter(
|
||||
UPPERCASE_CHARS,
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
if (lowerCase) {
|
||||
addFilter(
|
||||
LOWERCASE_CHARS,
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
if (digits) {
|
||||
addFilter(
|
||||
DIGIT_CHARS,
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
if (minus) {
|
||||
addFilter(
|
||||
MINUS_CHAR,
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
if (underline) {
|
||||
addFilter(
|
||||
UNDERLINE_CHAR,
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
if (space) {
|
||||
addFilter(
|
||||
SPACE_CHAR,
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
if (specials) {
|
||||
addFilter(
|
||||
SPECIAL_CHARS,
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
if (brackets) {
|
||||
addFilter(
|
||||
BRACKET_CHARS,
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
if (extended) {
|
||||
addFilter(
|
||||
extendedChars(),
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
if (considerChars.isNotEmpty()) {
|
||||
addFilter(
|
||||
considerChars,
|
||||
if (atLeastOneFromEach) 1 else 0
|
||||
)
|
||||
}
|
||||
}
|
||||
return buffer.toString()
|
||||
|
||||
return generateRandomString(SecureRandom(), passwordFilters)
|
||||
}
|
||||
|
||||
private fun getCharacterSet(upperCase: Boolean,
|
||||
lowerCase: Boolean,
|
||||
digits: Boolean,
|
||||
minus: Boolean,
|
||||
underline: Boolean,
|
||||
space: Boolean,
|
||||
specials: Boolean,
|
||||
brackets: Boolean,
|
||||
extended: Boolean): String {
|
||||
val charSet = StringBuilder()
|
||||
private fun generateRandomString(random: Random, passwordFilters: PasswordFilters): String {
|
||||
val randomString = StringBuilder()
|
||||
|
||||
if (upperCase) {
|
||||
charSet.append(UPPERCASE_CHARS)
|
||||
// Allocate appropriate memory for the password.
|
||||
var requiredCharactersLeft = passwordFilters.getRequiredCharactersLeft()
|
||||
|
||||
// Build the password.
|
||||
for (i in 0 until passwordFilters.length) {
|
||||
var selectableChars: String = if (requiredCharactersLeft < passwordFilters.length - i) {
|
||||
// choose from any group at random
|
||||
passwordFilters.getSelectableChars()
|
||||
} else {
|
||||
// choose only from a group that we need to satisfy a minimum for.
|
||||
passwordFilters.getSelectableCharsForNeed()
|
||||
}
|
||||
passwordFilters.ignoreChars.forEach {
|
||||
selectableChars = selectableChars.replace(it.toString(), "")
|
||||
}
|
||||
|
||||
// Now that the string is built, get the next random character.
|
||||
val selectableCharsMaxIndex = selectableChars.length
|
||||
val randomSelectableCharsIndex = if (selectableCharsMaxIndex > 0) random.nextInt(selectableCharsMaxIndex) else 0
|
||||
val nextChar = selectableChars[randomSelectableCharsIndex]
|
||||
|
||||
// Put at random position
|
||||
val randomStringMaxIndex = randomString.length
|
||||
val randomStringIndex = if (randomStringMaxIndex > 0) random.nextInt(randomStringMaxIndex) else 0
|
||||
randomString.insert(randomStringIndex, nextChar)
|
||||
|
||||
// Now figure out where it came from, and decrement the appropriate minimum value
|
||||
passwordFilters.getFilterThatContainsChar(nextChar)?.let {
|
||||
if (it.minCharsNeeded > 0) {
|
||||
it.minCharsNeeded--
|
||||
requiredCharactersLeft--
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lowerCase) {
|
||||
charSet.append(LOWERCASE_CHARS)
|
||||
}
|
||||
if (digits) {
|
||||
charSet.append(DIGIT_CHARS)
|
||||
}
|
||||
if (minus) {
|
||||
charSet.append(MINUS_CHAR)
|
||||
}
|
||||
if (underline) {
|
||||
charSet.append(UNDERLINE_CHAR)
|
||||
}
|
||||
if (space) {
|
||||
charSet.append(SPACE_CHAR)
|
||||
}
|
||||
if (specials) {
|
||||
charSet.append(SPECIAL_CHARS)
|
||||
}
|
||||
if (brackets) {
|
||||
charSet.append(BRACKET_CHARS)
|
||||
}
|
||||
if (extended) {
|
||||
charSet.append(extendedChars())
|
||||
return randomString.toString()
|
||||
}
|
||||
|
||||
private data class Filter(var chars: String,
|
||||
var minCharsNeeded: Int)
|
||||
|
||||
private class PasswordFilters {
|
||||
var length: Int = 0
|
||||
var ignoreChars = ""
|
||||
val filters = mutableListOf<Filter>()
|
||||
|
||||
fun addFilter(chars: String, minCharsNeeded: Int) {
|
||||
filters.add(Filter(chars, minCharsNeeded))
|
||||
}
|
||||
|
||||
return charSet.toString()
|
||||
fun getRequiredCharactersLeft(): Int {
|
||||
var charsRequired = 0
|
||||
filters.forEach {
|
||||
charsRequired += it.minCharsNeeded
|
||||
}
|
||||
return charsRequired
|
||||
}
|
||||
|
||||
fun getSelectableChars(): String {
|
||||
val stringBuilder = StringBuilder()
|
||||
filters.forEach {
|
||||
stringBuilder.append(it.chars)
|
||||
}
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
|
||||
fun getFilterThatContainsChar(char: Char): Filter? {
|
||||
return filters.find { it.chars.contains(char) }
|
||||
}
|
||||
|
||||
fun getSelectableCharsForNeed(): String {
|
||||
val selectableChars = StringBuilder()
|
||||
// choose only from a group that we need to satisfy a minimum for.
|
||||
filters.forEach {
|
||||
if (it.minCharsNeeded > 0) {
|
||||
selectableChars.append(it.chars)
|
||||
}
|
||||
}
|
||||
return selectableChars.toString()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -151,5 +229,75 @@ class PasswordGenerator(private val resources: Resources) {
|
||||
private const val SPACE_CHAR = " "
|
||||
private const val SPECIAL_CHARS = "!\"#$%&'*+,./:;=?@\\^`"
|
||||
private const val BRACKET_CHARS = "[]{}()<>"
|
||||
private const val AMBIGUOUS_CHARS = "iI|lLoO01"
|
||||
|
||||
// From KeePassXC code https://github.com/keepassxreboot/keepassxc/pull/538
|
||||
private fun extendedChars(): String {
|
||||
val charSet = StringBuilder()
|
||||
// [U+0080, U+009F] are C1 control characters,
|
||||
// U+00A0 is non-breaking space
|
||||
run {
|
||||
var ch = '\u00A1'
|
||||
while (ch <= '\u00AC') {
|
||||
charSet.append(ch)
|
||||
++ch
|
||||
}
|
||||
}
|
||||
// U+00AD is soft hyphen (format character)
|
||||
var ch = '\u00AE'
|
||||
while (ch < '\u00FF') {
|
||||
charSet.append(ch)
|
||||
++ch
|
||||
}
|
||||
charSet.append('\u00FF')
|
||||
return charSet.toString()
|
||||
}
|
||||
|
||||
fun getColorizedPassword(password: String): Spannable {
|
||||
val spannableString = SpannableStringBuilder()
|
||||
if (password.isNotEmpty()) {
|
||||
password.forEach {
|
||||
when {
|
||||
UPPERCASE_CHARS.contains(it)||
|
||||
LOWERCASE_CHARS.contains(it) -> {
|
||||
spannableString.append(it)
|
||||
}
|
||||
DIGIT_CHARS.contains(it) -> {
|
||||
// RED
|
||||
spannableString.append(colorizeChar(it, Color.rgb(246, 79, 62)))
|
||||
}
|
||||
SPECIAL_CHARS.contains(it) -> {
|
||||
// Blue
|
||||
spannableString.append(colorizeChar(it, Color.rgb(39, 166, 228)))
|
||||
}
|
||||
MINUS_CHAR.contains(it)||
|
||||
UNDERLINE_CHAR.contains(it)||
|
||||
BRACKET_CHARS.contains(it) -> {
|
||||
// Purple
|
||||
spannableString.append(colorizeChar(it, Color.rgb(185, 38, 209)))
|
||||
}
|
||||
extendedChars().contains(it) -> {
|
||||
// Green
|
||||
spannableString.append(colorizeChar(it, Color.rgb(44, 181, 50)))
|
||||
}
|
||||
else -> {
|
||||
spannableString.append(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return spannableString
|
||||
}
|
||||
|
||||
private fun colorizeChar(char: Char, color: Int): Spannable {
|
||||
val spannableColorChar = SpannableString(char.toString())
|
||||
spannableColorChar.setSpan(
|
||||
ForegroundColorSpan(color),
|
||||
0,
|
||||
1,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
return spannableColorChar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.settings.preferencedialogfragment.DurationDialogFragmentCompat
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ import com.kunzisoft.keepass.utils.UriUtil
|
||||
|
||||
class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
|
||||
private var deleteKeysAlertDialog: AlertDialog? = null
|
||||
private var warningAlertDialog: AlertDialog? = null
|
||||
|
||||
override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {
|
||||
|
||||
@@ -262,7 +262,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
val deviceCredentialChecked = deviceCredentialUnlockEnablePreference?.isChecked ?: false
|
||||
if (!biometricChecked) {
|
||||
biometricUnlockEnablePreference.isChecked = true
|
||||
deleteKeysMessage(activity) {
|
||||
warningMessage(activity, keystoreWarning = false, deleteKeys = true) {
|
||||
biometricUnlockEnablePreference.isChecked = false
|
||||
autoOpenPromptPreference?.isEnabled = deviceCredentialChecked
|
||||
tempAdvancedUnlockPreference?.isEnabled = deviceCredentialChecked
|
||||
@@ -270,13 +270,17 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
} else {
|
||||
if (deviceCredentialChecked) {
|
||||
biometricUnlockEnablePreference.isChecked = false
|
||||
deleteKeysMessage(activity) {
|
||||
warningMessage(activity, keystoreWarning = true, deleteKeys = true) {
|
||||
biometricUnlockEnablePreference.isChecked = true
|
||||
deviceCredentialUnlockEnablePreference?.isChecked = false
|
||||
}
|
||||
} else {
|
||||
autoOpenPromptPreference?.isEnabled = true
|
||||
tempAdvancedUnlockPreference?.isEnabled = true
|
||||
biometricUnlockEnablePreference.isChecked = false
|
||||
warningMessage(activity, keystoreWarning = true, deleteKeys = false) {
|
||||
biometricUnlockEnablePreference.isChecked = true
|
||||
autoOpenPromptPreference?.isEnabled = true
|
||||
tempAdvancedUnlockPreference?.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
@@ -305,7 +309,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
val biometricChecked = biometricUnlockEnablePreference?.isChecked ?: false
|
||||
if (!deviceCredentialChecked) {
|
||||
deviceCredentialUnlockEnablePreference.isChecked = true
|
||||
deleteKeysMessage(activity) {
|
||||
warningMessage(activity, keystoreWarning = false, deleteKeys = true) {
|
||||
deviceCredentialUnlockEnablePreference.isChecked = false
|
||||
autoOpenPromptPreference?.isEnabled = biometricChecked
|
||||
tempAdvancedUnlockPreference?.isEnabled = biometricChecked
|
||||
@@ -313,13 +317,17 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
} else {
|
||||
if (biometricChecked) {
|
||||
deviceCredentialUnlockEnablePreference.isChecked = false
|
||||
deleteKeysMessage(activity) {
|
||||
warningMessage(activity, keystoreWarning = true, deleteKeys = true) {
|
||||
deviceCredentialUnlockEnablePreference.isChecked = true
|
||||
biometricUnlockEnablePreference?.isChecked = false
|
||||
}
|
||||
} else {
|
||||
autoOpenPromptPreference?.isEnabled = true
|
||||
tempAdvancedUnlockPreference?.isEnabled = true
|
||||
deviceCredentialUnlockEnablePreference.isChecked = false
|
||||
warningMessage(activity, keystoreWarning = true, deleteKeys = false) {
|
||||
deviceCredentialUnlockEnablePreference.isChecked = true
|
||||
autoOpenPromptPreference?.isEnabled = true
|
||||
tempAdvancedUnlockPreference?.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
@@ -334,7 +342,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
|
||||
tempAdvancedUnlockPreference?.setOnPreferenceClickListener {
|
||||
tempAdvancedUnlockPreference.isChecked = !tempAdvancedUnlockPreference.isChecked
|
||||
deleteKeysMessage(activity) {
|
||||
warningMessage(activity, keystoreWarning = false, deleteKeys = true) {
|
||||
tempAdvancedUnlockPreference.isChecked = !tempAdvancedUnlockPreference.isChecked
|
||||
}
|
||||
true
|
||||
@@ -343,7 +351,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
val deleteKeysFingerprints: Preference? = findPreference(getString(R.string.biometric_delete_all_key_key))
|
||||
if (biometricUnlockSupported || deviceCredentialUnlockSupported) {
|
||||
deleteKeysFingerprints?.setOnPreferenceClickListener {
|
||||
deleteKeysMessage(activity)
|
||||
warningMessage(activity, keystoreWarning = false, deleteKeys = true)
|
||||
false
|
||||
}
|
||||
} else {
|
||||
@@ -357,22 +365,36 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteKeysMessage(activity: FragmentActivity, validate: (()->Unit)? = null) {
|
||||
deleteKeysAlertDialog = AlertDialog.Builder(activity)
|
||||
.setMessage(resources.getString(R.string.advanced_unlock_delete_all_key_warning))
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setPositiveButton(resources.getString(android.R.string.ok)
|
||||
) { _, _ ->
|
||||
validate?.invoke()
|
||||
deleteKeysAlertDialog?.setOnDismissListener(null)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
AdvancedUnlockManager.deleteAllEntryKeysInKeystoreForBiometric(activity)
|
||||
}
|
||||
private fun warningMessage(activity: FragmentActivity,
|
||||
keystoreWarning: Boolean,
|
||||
deleteKeys: Boolean,
|
||||
validate: (()->Unit)? = null) {
|
||||
var message = ""
|
||||
if (keystoreWarning) {
|
||||
message += resources.getString(R.string.advanced_unlock_prompt_store_credential_message)
|
||||
message += "\n\n" + resources.getString(R.string.advanced_unlock_keystore_warning)
|
||||
}
|
||||
if (keystoreWarning && deleteKeys) {
|
||||
message += "\n\n"
|
||||
}
|
||||
if (deleteKeys) {
|
||||
message += resources.getString(R.string.advanced_unlock_delete_all_key_warning)
|
||||
}
|
||||
warningAlertDialog = AlertDialog.Builder(activity)
|
||||
.setMessage(message)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setPositiveButton(resources.getString(android.R.string.ok)
|
||||
) { _, _ ->
|
||||
validate?.invoke()
|
||||
warningAlertDialog?.setOnDismissListener(null)
|
||||
if (deleteKeys && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
AdvancedUnlockManager.deleteAllEntryKeysInKeystoreForBiometric(activity)
|
||||
}
|
||||
.setNegativeButton(resources.getString(android.R.string.cancel)
|
||||
) { _, _ ->}
|
||||
.create()
|
||||
deleteKeysAlertDialog?.show()
|
||||
}
|
||||
.setNegativeButton(resources.getString(android.R.string.cancel)
|
||||
) { _, _ ->}
|
||||
.create()
|
||||
warningAlertDialog?.show()
|
||||
}
|
||||
|
||||
private fun onCreateAppearancePreferences(rootKey: String?) {
|
||||
@@ -509,7 +531,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
deleteKeysAlertDialog?.dismiss()
|
||||
warningAlertDialog?.dismiss()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,10 @@ import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.stylish.Stylish
|
||||
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
|
||||
import com.kunzisoft.keepass.database.element.SortNodeEnum
|
||||
import com.kunzisoft.keepass.database.search.SearchParameters
|
||||
import com.kunzisoft.keepass.education.Education
|
||||
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
|
||||
import com.kunzisoft.keepass.password.PassphraseGenerator
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import java.util.*
|
||||
@@ -111,6 +114,18 @@ object PreferencesUtil {
|
||||
context.resources.getBoolean(R.bool.show_entry_colors_default))
|
||||
}
|
||||
|
||||
fun hideProtectedValue(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.hide_password_key),
|
||||
context.resources.getBoolean(R.bool.hide_password_default))
|
||||
}
|
||||
|
||||
fun colorizePassword(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.colorize_password_key),
|
||||
context.resources.getBoolean(R.bool.colorize_password_default))
|
||||
}
|
||||
|
||||
fun showUsernamesListEntries(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.list_entries_show_username_key),
|
||||
@@ -192,15 +207,194 @@ object PreferencesUtil {
|
||||
|
||||
fun getDefaultPasswordLength(context: Context): Int {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getInt(context.getString(R.string.password_length_key),
|
||||
Integer.parseInt(context.getString(R.string.default_password_length)))
|
||||
return prefs.getInt(context.getString(R.string.password_generator_length_key),
|
||||
context.resources.getInteger(R.integer.password_generator_length_default))
|
||||
}
|
||||
|
||||
fun getDefaultPasswordCharacters(context: Context): Set<String>? {
|
||||
fun setDefaultPasswordLength(context: Context, passwordLength: Int) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
|
||||
putInt(
|
||||
context.getString(R.string.password_generator_length_key),
|
||||
passwordLength
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultPasswordOptions(context: Context): Set<String> {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getStringSet(context.getString(R.string.list_password_generator_options_key),
|
||||
HashSet(listOf(*context.resources
|
||||
.getStringArray(R.array.list_password_generator_options_default_values))))
|
||||
return prefs.getStringSet(context.getString(R.string.password_generator_options_key),
|
||||
HashSet(listOf(*context.resources
|
||||
.getStringArray(R.array.list_password_generator_options_default_values)))) ?: setOf()
|
||||
}
|
||||
|
||||
fun setDefaultPasswordOptions(context: Context, passwordOptionsSet: Set<String>) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
|
||||
putStringSet(
|
||||
context.getString(R.string.password_generator_options_key),
|
||||
passwordOptionsSet
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultPasswordConsiderChars(context: Context): String {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getString(context.getString(R.string.password_generator_consider_chars_key),
|
||||
context.getString(R.string.password_generator_consider_chars_default)) ?: ""
|
||||
}
|
||||
|
||||
fun setDefaultPasswordConsiderChars(context: Context, considerChars: String) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
|
||||
putString(
|
||||
context.getString(R.string.password_generator_consider_chars_key),
|
||||
considerChars
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultPasswordIgnoreChars(context: Context): String {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getString(context.getString(R.string.password_generator_ignore_chars_key),
|
||||
context.getString(R.string.password_generator_ignore_chars_default)) ?: ""
|
||||
}
|
||||
|
||||
fun setDefaultPasswordIgnoreChars(context: Context, ignoreChars: String) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
|
||||
putString(
|
||||
context.getString(R.string.password_generator_ignore_chars_key),
|
||||
ignoreChars
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultPassphraseWordCount(context: Context): Int {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getInt(context.getString(R.string.passphrase_generator_word_count_key),
|
||||
context.resources.getInteger(R.integer.passphrase_generator_word_count_default))
|
||||
}
|
||||
|
||||
fun setDefaultPassphraseWordCount(context: Context, passphraseWordCount: Int) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
|
||||
putInt(
|
||||
context.getString(R.string.passphrase_generator_word_count_key),
|
||||
passphraseWordCount
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultPassphraseWordCase(context: Context): PassphraseGenerator.WordCase {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return PassphraseGenerator.WordCase
|
||||
.getByOrdinal(prefs.getInt(context
|
||||
.getString(R.string.passphrase_generator_word_case_key),
|
||||
0)
|
||||
)
|
||||
}
|
||||
|
||||
fun setDefaultPassphraseWordCase(context: Context, wordCase: PassphraseGenerator.WordCase) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
|
||||
putInt(
|
||||
context.getString(R.string.passphrase_generator_word_case_key),
|
||||
wordCase.ordinal
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultPassphraseSeparator(context: Context): String {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getString(context.getString(R.string.passphrase_generator_separator_key),
|
||||
context.getString(R.string.passphrase_generator_separator_default)) ?: ""
|
||||
}
|
||||
|
||||
fun setDefaultPassphraseSeparator(context: Context, separator: String) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
|
||||
putString(
|
||||
context.getString(R.string.passphrase_generator_separator_key),
|
||||
separator
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultSearchParameters(context: Context): SearchParameters {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return SearchParameters().apply {
|
||||
caseSensitive = prefs.getBoolean(context.getString(R.string.search_option_case_sensitive_key),
|
||||
context.resources.getBoolean(R.bool.search_option_case_sensitive_default))
|
||||
isRegex = prefs.getBoolean(context.getString(R.string.search_option_regex_key),
|
||||
context.resources.getBoolean(R.bool.search_option_regex_default))
|
||||
searchInTitles = prefs.getBoolean(context.getString(R.string.search_option_title_key),
|
||||
context.resources.getBoolean(R.bool.search_option_title_default))
|
||||
searchInUsernames = prefs.getBoolean(context.getString(R.string.search_option_username_key),
|
||||
context.resources.getBoolean(R.bool.search_option_username_default))
|
||||
searchInPasswords = prefs.getBoolean(context.getString(R.string.search_option_password_key),
|
||||
context.resources.getBoolean(R.bool.search_option_password_default))
|
||||
searchInUrls = prefs.getBoolean(context.getString(R.string.search_option_url_key),
|
||||
context.resources.getBoolean(R.bool.search_option_url_default))
|
||||
searchInExpired = prefs.getBoolean(context.getString(R.string.search_option_expired_key),
|
||||
context.resources.getBoolean(R.bool.search_option_expired_default))
|
||||
searchInNotes = prefs.getBoolean(context.getString(R.string.search_option_note_key),
|
||||
context.resources.getBoolean(R.bool.search_option_note_default))
|
||||
searchInOTP = prefs.getBoolean(context.getString(R.string.search_option_otp_key),
|
||||
context.resources.getBoolean(R.bool.search_option_otp_default))
|
||||
searchInOther = prefs.getBoolean(context.getString(R.string.search_option_other_key),
|
||||
context.resources.getBoolean(R.bool.search_option_other_default))
|
||||
searchInUUIDs = prefs.getBoolean(context.getString(R.string.search_option_uuid_key),
|
||||
context.resources.getBoolean(R.bool.search_option_uuid_default))
|
||||
searchInTags = prefs.getBoolean(context.getString(R.string.search_option_tag_key),
|
||||
context.resources.getBoolean(R.bool.search_option_tag_default))
|
||||
searchInCurrentGroup = prefs.getBoolean(context.getString(R.string.search_option_current_group_key),
|
||||
context.resources.getBoolean(R.bool.search_option_current_group_default))
|
||||
searchInSearchableGroup = prefs.getBoolean(context.getString(R.string.search_option_searchable_group_key),
|
||||
context.resources.getBoolean(R.bool.search_option_searchable_group_default))
|
||||
searchInRecycleBin = prefs.getBoolean(context.getString(R.string.search_option_recycle_bin_key),
|
||||
context.resources.getBoolean(R.bool.search_option_recycle_bin_default))
|
||||
searchInTemplates = prefs.getBoolean(context.getString(R.string.search_option_templates_key),
|
||||
context.resources.getBoolean(R.bool.search_option_templates_default))
|
||||
}
|
||||
}
|
||||
|
||||
fun setDefaultSearchParameters(context: Context, searchParameters: SearchParameters) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
|
||||
putBoolean(context.getString(R.string.search_option_case_sensitive_key),
|
||||
searchParameters.caseSensitive)
|
||||
putBoolean(context.getString(R.string.search_option_regex_key),
|
||||
searchParameters.isRegex)
|
||||
putBoolean(context.getString(R.string.search_option_title_key),
|
||||
searchParameters.searchInTitles)
|
||||
putBoolean(context.getString(R.string.search_option_username_key),
|
||||
searchParameters.searchInUsernames)
|
||||
putBoolean(context.getString(R.string.search_option_password_key),
|
||||
searchParameters.searchInPasswords)
|
||||
putBoolean(context.getString(R.string.search_option_url_key),
|
||||
searchParameters.searchInUrls)
|
||||
putBoolean(context.getString(R.string.search_option_expired_key),
|
||||
searchParameters.searchInExpired)
|
||||
putBoolean(context.getString(R.string.search_option_note_key),
|
||||
searchParameters.searchInNotes)
|
||||
putBoolean(context.getString(R.string.search_option_otp_key),
|
||||
searchParameters.searchInOTP)
|
||||
putBoolean(context.getString(R.string.search_option_other_key),
|
||||
searchParameters.searchInOther)
|
||||
putBoolean(context.getString(R.string.search_option_uuid_key),
|
||||
searchParameters.searchInUUIDs)
|
||||
putBoolean(context.getString(R.string.search_option_tag_key),
|
||||
searchParameters.searchInTags)
|
||||
putBoolean(context.getString(R.string.search_option_current_group_key),
|
||||
searchParameters.searchInCurrentGroup)
|
||||
putBoolean(context.getString(R.string.search_option_searchable_group_key),
|
||||
searchParameters.searchInSearchableGroup)
|
||||
putBoolean(context.getString(R.string.search_option_recycle_bin_key),
|
||||
searchParameters.searchInRecycleBin)
|
||||
putBoolean(context.getString(R.string.search_option_templates_key),
|
||||
searchParameters.searchInTemplates)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun isClipboardNotificationsEnable(context: Context): Boolean {
|
||||
@@ -351,12 +545,6 @@ object PreferencesUtil {
|
||||
context.resources.getBoolean(R.bool.sort_recycle_bin_bottom_default))
|
||||
}
|
||||
|
||||
fun hideProtectedValue(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.hide_password_key),
|
||||
context.resources.getBoolean(R.bool.hide_password_default))
|
||||
}
|
||||
|
||||
fun fieldFontIsInVisibility(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.monospace_font_fields_enable_key),
|
||||
@@ -436,6 +624,7 @@ object PreferencesUtil {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.keyboard_search_share_key),
|
||||
context.resources.getBoolean(R.bool.keyboard_search_share_default))
|
||||
&& MagikeyboardService.activatedInSettings(context)
|
||||
}
|
||||
|
||||
fun isKeyboardSaveSearchInfoEnable(context: Context): Boolean {
|
||||
@@ -470,6 +659,12 @@ object PreferencesUtil {
|
||||
context.resources.getBoolean(R.bool.keyboard_previous_database_credentials_default))
|
||||
}
|
||||
|
||||
fun isKeyboardPreviousSearchEnable(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.keyboard_previous_search_key),
|
||||
context.resources.getBoolean(R.bool.keyboard_previous_search_default))
|
||||
}
|
||||
|
||||
fun isKeyboardPreviousFillInEnable(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.keyboard_previous_fill_in_key),
|
||||
@@ -488,12 +683,6 @@ object PreferencesUtil {
|
||||
context.resources.getBoolean(R.bool.autofill_close_database_default))
|
||||
}
|
||||
|
||||
fun isAutofillAutoSearchEnable(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.autofill_auto_search_key),
|
||||
context.resources.getBoolean(R.bool.autofill_auto_search_default))
|
||||
}
|
||||
|
||||
fun isAutofillInlineSuggestionsEnable(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.autofill_inline_suggestions_key),
|
||||
@@ -609,9 +798,6 @@ object PreferencesUtil {
|
||||
context.getString(R.string.lock_database_screen_off_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.lock_database_back_root_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.lock_database_show_button_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.password_length_key) -> editor.putInt(name, value.toInt())
|
||||
context.getString(R.string.list_password_generator_options_key) -> editor.putStringSet(name, getStringSetFromProperties(value))
|
||||
context.getString(R.string.hide_password_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.allow_copy_password_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.remember_database_locations_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.show_recent_files_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
@@ -638,10 +824,10 @@ object PreferencesUtil {
|
||||
context.getString(R.string.keyboard_key_vibrate_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.keyboard_key_sound_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.keyboard_previous_database_credentials_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.keyboard_previous_search_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.keyboard_previous_fill_in_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.keyboard_previous_lock_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.autofill_close_database_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.autofill_auto_search_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.autofill_inline_suggestions_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.autofill_manual_selection_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.autofill_save_search_info_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
@@ -653,6 +839,8 @@ object PreferencesUtil {
|
||||
context.getString(R.string.setting_style_brightness_key) -> editor.putString(name, value)
|
||||
context.getString(R.string.setting_icon_pack_choose_key) -> editor.putString(name, value)
|
||||
context.getString(R.string.show_entry_colors_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.hide_password_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.colorize_password_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.list_entries_show_username_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.list_groups_show_number_entries_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.show_otp_token_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
@@ -662,6 +850,14 @@ object PreferencesUtil {
|
||||
context.getString(R.string.hide_expired_entries_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.enable_education_screens_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
|
||||
context.getString(R.string.password_generator_length_key) -> editor.putInt(name, value.toInt())
|
||||
context.getString(R.string.password_generator_options_key) -> editor.putStringSet(name, getStringSetFromProperties(value))
|
||||
context.getString(R.string.password_generator_consider_chars_key) -> editor.putString(name, value)
|
||||
context.getString(R.string.password_generator_ignore_chars_key) -> editor.putString(name, value)
|
||||
context.getString(R.string.passphrase_generator_word_count_key) -> editor.putInt(name, value.toInt())
|
||||
context.getString(R.string.passphrase_generator_word_case_key) -> editor.putInt(name, value.toInt())
|
||||
context.getString(R.string.passphrase_generator_separator_key) -> editor.putString(name, value)
|
||||
|
||||
context.getString(R.string.sort_node_key) -> editor.putString(name, value)
|
||||
context.getString(R.string.sort_group_before_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
context.getString(R.string.sort_ascending_key) -> editor.putBoolean(name, value.toBoolean())
|
||||
|
||||
@@ -225,10 +225,14 @@ open class SettingsActivity
|
||||
commit()
|
||||
}
|
||||
|
||||
toolbar?.title = NestedSettingsFragment.retrieveTitle(resources, key)
|
||||
setTitle(key)
|
||||
hideOrShowLockButton(key)
|
||||
}
|
||||
|
||||
protected fun setTitle(key: NestedSettingsFragment.Screen) {
|
||||
toolbar?.title = NestedSettingsFragment.retrieveTitle(resources, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* To keep the current screen when activity is reloaded
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,7 @@ class SettingsAdvancedUnlockActivity : SettingsActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mTimeoutEnable = false
|
||||
setTitle(NestedSettingsFragment.Screen.ADVANCED_UNLOCK)
|
||||
}
|
||||
|
||||
override fun retrieveMainFragment(): Fragment {
|
||||
|
||||
@@ -69,7 +69,18 @@ object UriUtil {
|
||||
return null
|
||||
return when {
|
||||
isFileScheme(fileUri) -> fileUri.path?.let { FileOutputStream(it) }
|
||||
isContentScheme(fileUri) -> contentResolver.openOutputStream(fileUri, "rwt")
|
||||
isContentScheme(fileUri) -> {
|
||||
try {
|
||||
contentResolver.openOutputStream(fileUri, "wt")
|
||||
} catch (e: FileNotFoundException) {
|
||||
Log.e(TAG, "Unable to open stream in `wt` mode, retry in `rwt` mode.", e)
|
||||
// https://issuetracker.google.com/issues/180526528
|
||||
// Try with rwt to fix content provider issue
|
||||
val outStream = contentResolver.openOutputStream(fileUri, "rwt")
|
||||
Log.w(TAG, "`rwt` mode used.")
|
||||
outStream
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class AdvancedUnlockInfoView @JvmOverloads constructor(context: Context,
|
||||
private var unlockAnimatedVector: FingerPrintAnimatedVector? = null
|
||||
private var unlockTitleTextView: TextView? = null
|
||||
private var unlockMessageTextView: TextView? = null
|
||||
var unlockIconImageView: ImageView? = null
|
||||
private var unlockIconImageView: ImageView? = null
|
||||
|
||||
init {
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
defStyle: Int = 0)
|
||||
: FrameLayout(context, attrs, defStyle) {
|
||||
|
||||
private var passwordView: EditText
|
||||
private var passwordTextView: EditText
|
||||
private var keyFileSelectionView: KeyFileSelectionView
|
||||
private var checkboxPasswordView: CompoundButton
|
||||
private var checkboxKeyFileView: CompoundButton
|
||||
@@ -60,7 +60,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
|
||||
inflater?.inflate(R.layout.view_main_credentials, this)
|
||||
|
||||
passwordView = findViewById(R.id.password)
|
||||
passwordTextView = findViewById(R.id.password_text_view)
|
||||
keyFileSelectionView = findViewById(R.id.keyfile_selection)
|
||||
checkboxPasswordView = findViewById(R.id.password_checkbox)
|
||||
checkboxKeyFileView = findViewById(R.id.keyfile_checkox)
|
||||
@@ -75,8 +75,8 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
}
|
||||
}
|
||||
|
||||
passwordView.setOnEditorActionListener(onEditorActionListener)
|
||||
passwordView.addTextChangedListener(object : TextWatcher {
|
||||
passwordTextView.setOnEditorActionListener(onEditorActionListener)
|
||||
passwordTextView.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||
|
||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||
@@ -86,7 +86,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
checkboxPasswordView.isChecked = true
|
||||
}
|
||||
})
|
||||
passwordView.setOnKeyListener { _, _, keyEvent ->
|
||||
passwordTextView.setOnKeyListener { _, _, keyEvent ->
|
||||
var handled = false
|
||||
if (keyEvent.action == KeyEvent.ACTION_DOWN
|
||||
&& keyEvent?.keyCode == KeyEvent.KEYCODE_ENTER
|
||||
@@ -108,11 +108,11 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
|
||||
fun populatePasswordTextView(text: String?) {
|
||||
if (text == null || text.isEmpty()) {
|
||||
passwordView.setText("")
|
||||
passwordTextView.setText("")
|
||||
if (checkboxPasswordView.isChecked)
|
||||
checkboxPasswordView.isChecked = false
|
||||
} else {
|
||||
passwordView.setText(text)
|
||||
passwordTextView.setText(text)
|
||||
if (checkboxPasswordView.isChecked)
|
||||
checkboxPasswordView.isChecked = true
|
||||
}
|
||||
@@ -137,7 +137,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
fun getMainCredential(): MainCredential {
|
||||
return MainCredential().apply {
|
||||
this.masterPassword = if (checkboxPasswordView.isChecked)
|
||||
passwordView.text?.toString() else null
|
||||
passwordTextView.text?.toString() else null
|
||||
this.keyFileUri = if (checkboxKeyFileView.isChecked)
|
||||
keyFileSelectionView.uri else null
|
||||
}
|
||||
@@ -163,7 +163,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
*/
|
||||
fun retrieveCredentialForStorage(listener: CredentialStorageListener): ByteArray? {
|
||||
return when (mCredentialStorage) {
|
||||
CredentialStorage.PASSWORD -> listener.passwordToStore(passwordView.text?.toString())
|
||||
CredentialStorage.PASSWORD -> listener.passwordToStore(passwordTextView.text?.toString())
|
||||
CredentialStorage.KEY_FILE -> listener.keyfileToStore(keyFileSelectionView.uri)
|
||||
CredentialStorage.HARDWARE_KEY -> listener.hardwareKeyToStore()
|
||||
}
|
||||
@@ -176,15 +176,15 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
|
||||
}
|
||||
|
||||
fun requestPasswordFocus() {
|
||||
passwordView.requestFocusFromTouch()
|
||||
passwordTextView.requestFocusFromTouch()
|
||||
}
|
||||
|
||||
// Auto select the password field and open keyboard
|
||||
fun focusPasswordFieldAndOpenKeyboard() {
|
||||
passwordView.postDelayed({
|
||||
passwordView.requestFocusFromTouch()
|
||||
passwordTextView.postDelayed({
|
||||
passwordTextView.requestFocusFromTouch()
|
||||
val inputMethodManager = context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as? InputMethodManager?
|
||||
inputMethodManager?.showSoftInput(passwordView, InputMethodManager.SHOW_IMPLICIT)
|
||||
inputMethodManager?.showSoftInput(passwordTextView, InputMethodManager.SHOW_IMPLICIT)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
|
||||
156
app/src/main/java/com/kunzisoft/keepass/view/PassKeyView.kt
Normal file
156
app/src/main/java/com/kunzisoft/keepass/view/PassKeyView.kt
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2022 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.view
|
||||
|
||||
import android.content.Context
|
||||
import android.text.Editable
|
||||
import android.text.InputType
|
||||
import android.text.SpannableString
|
||||
import android.text.TextWatcher
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.password.PasswordGenerator
|
||||
import com.kunzisoft.keepass.password.PasswordEntropy
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
|
||||
class PassKeyView @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0)
|
||||
: FrameLayout(context, attrs, defStyle) {
|
||||
|
||||
private var mPasswordEntropyCalculator: PasswordEntropy? = null
|
||||
|
||||
private val passwordInputLayout: TextInputLayout
|
||||
private val passwordText: TextView
|
||||
private val passwordStrengthProgress: LinearProgressIndicator
|
||||
private val passwordEntropy: TextView
|
||||
|
||||
private var mViewHint: String = ""
|
||||
private var mMaxLines: Int = 3
|
||||
private var mShowPassword: Boolean = false
|
||||
|
||||
private var mPasswordTextWatcher: MutableList<TextWatcher> = mutableListOf()
|
||||
private val passwordTextWatcher = object : TextWatcher {
|
||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||
mPasswordTextWatcher.forEach {
|
||||
it.beforeTextChanged(charSequence, i, i1, i2)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||
mPasswordTextWatcher.forEach {
|
||||
it.onTextChanged(charSequence, i, i1, i2)
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
mPasswordTextWatcher.forEach {
|
||||
it.afterTextChanged(editable)
|
||||
}
|
||||
getEntropyStrength(editable.toString())
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
context.theme.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.PassKeyView,
|
||||
0, 0).apply {
|
||||
try {
|
||||
mViewHint = getString(R.styleable.PassKeyView_passKeyHint)
|
||||
?: context.getString(R.string.password)
|
||||
mMaxLines = getInteger(R.styleable.PassKeyView_passKeyMaxLines, mMaxLines)
|
||||
mShowPassword = getBoolean(R.styleable.PassKeyView_passKeyVisible,
|
||||
!PreferencesUtil.hideProtectedValue(context))
|
||||
} finally {
|
||||
recycle()
|
||||
}
|
||||
}
|
||||
|
||||
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
|
||||
inflater?.inflate(R.layout.view_passkey, this)
|
||||
|
||||
passwordInputLayout = findViewById(R.id.password_input_layout)
|
||||
passwordInputLayout?.hint = mViewHint
|
||||
passwordText = findViewById(R.id.password_text)
|
||||
if (mShowPassword) {
|
||||
passwordText?.inputType = passwordText.inputType or
|
||||
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
}
|
||||
passwordText?.maxLines = mMaxLines
|
||||
passwordText?.applyFontVisibility()
|
||||
passwordText.addTextChangedListener(passwordTextWatcher)
|
||||
passwordStrengthProgress = findViewById(R.id.password_strength_progress)
|
||||
passwordStrengthProgress?.apply {
|
||||
setIndicatorColor(PasswordEntropy.Strength.RISKY.color)
|
||||
progress = 0
|
||||
max = 100
|
||||
}
|
||||
passwordEntropy = findViewById(R.id.password_entropy)
|
||||
|
||||
mPasswordEntropyCalculator = PasswordEntropy {
|
||||
passwordText?.text?.toString()?.let { firstPassword ->
|
||||
getEntropyStrength(firstPassword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getEntropyStrength(passwordText: String) {
|
||||
mPasswordEntropyCalculator?.getEntropyStrength(passwordText) { entropyStrength ->
|
||||
passwordStrengthProgress.apply {
|
||||
post {
|
||||
setIndicatorColor(entropyStrength.strength.color)
|
||||
setProgressCompat(entropyStrength.estimationPercent, true)
|
||||
}
|
||||
}
|
||||
passwordEntropy.apply {
|
||||
post {
|
||||
text = PasswordEntropy.getStringEntropy(resources, entropyStrength.entropy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addTextChangedListener(textWatcher: TextWatcher) {
|
||||
mPasswordTextWatcher.add(textWatcher)
|
||||
}
|
||||
|
||||
fun removeTextChangedListener(textWatcher: TextWatcher) {
|
||||
mPasswordTextWatcher.remove(textWatcher)
|
||||
}
|
||||
|
||||
var passwordString: String
|
||||
get() {
|
||||
return passwordText.text.toString()
|
||||
}
|
||||
set(value) {
|
||||
val spannableString =
|
||||
if (PreferencesUtil.colorizePassword(context))
|
||||
PasswordGenerator.getColorizedPassword(value)
|
||||
else
|
||||
SpannableString(value)
|
||||
passwordText.text = spannableString
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import androidx.core.view.isVisible
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||
import com.kunzisoft.keepass.database.search.SearchParameters
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
|
||||
class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
@@ -30,7 +31,7 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
private var searchUsername: CompoundButton
|
||||
private var searchPassword: CompoundButton
|
||||
private var searchURL: CompoundButton
|
||||
private var searchExpires: CompoundButton
|
||||
private var searchExpired: CompoundButton
|
||||
private var searchNotes: CompoundButton
|
||||
private var searchOther: CompoundButton
|
||||
private var searchUUID: CompoundButton
|
||||
@@ -49,7 +50,7 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
this.searchInUsernames = searchUsername.isChecked
|
||||
this.searchInPasswords = searchPassword.isChecked
|
||||
this.searchInUrls = searchURL.isChecked
|
||||
this.excludeExpired = !(searchExpires.isChecked)
|
||||
this.searchInExpired = searchExpired.isChecked
|
||||
this.searchInNotes = searchNotes.isChecked
|
||||
this.searchInOther = searchOther.isChecked
|
||||
this.searchInUUIDs = searchUUID.isChecked
|
||||
@@ -69,12 +70,12 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
searchUsername.isChecked = value.searchInUsernames
|
||||
searchPassword.isChecked = value.searchInPasswords
|
||||
searchURL.isChecked = value.searchInUrls
|
||||
searchExpires.isChecked = !value.excludeExpired
|
||||
searchExpired.isChecked = value.searchInExpired
|
||||
searchNotes.isChecked = value.searchInNotes
|
||||
searchOther.isChecked = value.searchInOther
|
||||
searchUUID.isChecked = value.searchInUUIDs
|
||||
searchTag.isChecked = value.searchInTags
|
||||
searchGroupSearchable.isChecked = value.searchInRecycleBin
|
||||
searchGroupSearchable.isChecked = value.searchInSearchableGroup
|
||||
searchRecycleBin.isChecked = value.searchInRecycleBin
|
||||
searchTemplate.isChecked = value.searchInTemplates
|
||||
mOnParametersChangeListener = tempListener
|
||||
@@ -107,7 +108,7 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
searchUsername = findViewById(R.id.search_chip_username)
|
||||
searchPassword = findViewById(R.id.search_chip_password)
|
||||
searchURL = findViewById(R.id.search_chip_url)
|
||||
searchExpires = findViewById(R.id.search_chip_expires)
|
||||
searchExpired = findViewById(R.id.search_chip_expires)
|
||||
searchNotes = findViewById(R.id.search_chip_note)
|
||||
searchUUID = findViewById(R.id.search_chip_uuid)
|
||||
searchOther = findViewById(R.id.search_chip_other)
|
||||
@@ -116,6 +117,9 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
searchRecycleBin = findViewById(R.id.search_chip_recycle_bin)
|
||||
searchTemplate = findViewById(R.id.search_chip_template)
|
||||
|
||||
// Set search
|
||||
searchParameters = PreferencesUtil.getDefaultSearchParameters(context)
|
||||
|
||||
// Expand menu with button
|
||||
searchExpandButton.setOnClickListener {
|
||||
val isVisible = searchAdvanceFiltersContainer?.visibility == View.VISIBLE
|
||||
@@ -153,8 +157,8 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
searchParameters.searchInUrls = isChecked
|
||||
mOnParametersChangeListener?.invoke(searchParameters)
|
||||
}
|
||||
searchExpires.setOnCheckedChangeListener { _, isChecked ->
|
||||
searchParameters.excludeExpired = !isChecked
|
||||
searchExpired.setOnCheckedChangeListener { _, isChecked ->
|
||||
searchParameters.searchInExpired = isChecked
|
||||
mOnParametersChangeListener?.invoke(searchParameters)
|
||||
}
|
||||
searchNotes.setOnCheckedChangeListener { _, isChecked ->
|
||||
@@ -249,4 +253,8 @@ class SearchFiltersView @JvmOverloads constructor(context: Context,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun saveSearchParameters() {
|
||||
PreferencesUtil.setDefaultSearchParameters(context, searchParameters)
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
||||
if (templateAttribute.options.isAssociatedWithPasswordGenerator()) {
|
||||
setOnActionClickListener({
|
||||
mOnPasswordGenerationActionClickListener?.invoke(field)
|
||||
}, R.drawable.ic_generate_password_white_24dp)
|
||||
}, R.drawable.ic_random_white_24dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
||||
setCopyButtonState(TextFieldView.ButtonState.GONE)
|
||||
} else {
|
||||
label = otpElement.type.name
|
||||
value = otpElement.token
|
||||
value = otpElement.tokenString
|
||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||
setCopyButtonClickListener { _, _ ->
|
||||
mOnCopyActionClickListener?.invoke(Field(
|
||||
@@ -175,7 +175,7 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
||||
mLastOtpTokenView = this
|
||||
mOtpRunnable = Runnable {
|
||||
if (otpElement.shouldRefreshToken()) {
|
||||
value = otpElement.token
|
||||
value = otpElement.tokenString
|
||||
}
|
||||
if (mLastOtpTokenView == null) {
|
||||
mOnOtpElementUpdated?.invoke(null)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.kunzisoft.keepass.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.text.InputFilter
|
||||
import android.text.InputType
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.style.StyleSpan
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.ContextThemeWrapper
|
||||
@@ -19,6 +23,9 @@ import androidx.core.view.isVisible
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.template.TemplateField
|
||||
import com.kunzisoft.keepass.password.PasswordGenerator
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
|
||||
class TextEditFieldView @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
@@ -123,7 +130,13 @@ class TextEditFieldView @JvmOverloads constructor(context: Context,
|
||||
return valueView.text?.toString() ?: ""
|
||||
}
|
||||
set(value) {
|
||||
valueView.setText(value)
|
||||
val spannableString =
|
||||
if (PreferencesUtil.colorizePassword(context)
|
||||
&& TemplateField.isStandardPasswordName(context, label))
|
||||
PasswordGenerator.getColorizedPassword(value)
|
||||
else
|
||||
SpannableString(value)
|
||||
valueView.setText(spannableString)
|
||||
}
|
||||
|
||||
override var default: String = ""
|
||||
@@ -164,7 +177,11 @@ class TextEditFieldView @JvmOverloads constructor(context: Context,
|
||||
fun setProtection(protection: Boolean) {
|
||||
if (protection) {
|
||||
labelView.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE
|
||||
valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||
val visibilityTag = if (PreferencesUtil.hideProtectedValue(context))
|
||||
InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||
else
|
||||
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
valueView.inputType = valueView.inputType or visibilityTag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,18 @@
|
||||
package com.kunzisoft.keepass.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.text.InputFilter
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.style.StyleSpan
|
||||
import android.text.util.Linkify
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.View
|
||||
import android.view.View.OnClickListener
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.widget.AppCompatImageButton
|
||||
@@ -36,7 +41,10 @@ import androidx.core.text.util.LinkifyCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.template.TemplateField
|
||||
import com.kunzisoft.keepass.model.EntryInfo.Companion.APPLICATION_ID_FIELD_NAME
|
||||
import com.kunzisoft.keepass.password.PasswordGenerator
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
|
||||
|
||||
@@ -191,7 +199,13 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
||||
return valueView.text.toString()
|
||||
}
|
||||
set(value) {
|
||||
valueView.text = value
|
||||
val spannableString =
|
||||
if (PreferencesUtil.colorizePassword(context)
|
||||
&& TemplateField.isStandardPasswordName(context, label))
|
||||
PasswordGenerator.getColorizedPassword(value)
|
||||
else
|
||||
SpannableString(value)
|
||||
valueView.text = spannableString
|
||||
changeProtectedValueParameters()
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ class EntryViewModel: ViewModel() {
|
||||
val onAttachmentAction : LiveData<EntryAttachmentState?> get() = _onAttachmentAction
|
||||
private val _onAttachmentAction = MutableLiveData<EntryAttachmentState?>()
|
||||
|
||||
val sectionSelected : LiveData<EntrySection> get() = _sectionSelected
|
||||
private val _sectionSelected = MutableLiveData<EntrySection>()
|
||||
|
||||
val historySelected : LiveData<EntryHistory> get() = _historySelected
|
||||
private val _historySelected = SingleLiveEvent<EntryHistory>()
|
||||
|
||||
@@ -124,6 +127,10 @@ class EntryViewModel: ViewModel() {
|
||||
_historySelected.value = EntryHistory(NodeIdUUID(item.id), null, item, position)
|
||||
}
|
||||
|
||||
fun selectSection(section: EntrySection) {
|
||||
_sectionSelected.value = section
|
||||
}
|
||||
|
||||
data class EntryInfoHistory(var mainEntryId: NodeId<UUID>,
|
||||
var historyPosition: Int,
|
||||
val template: Template,
|
||||
@@ -135,6 +142,16 @@ class EntryViewModel: ViewModel() {
|
||||
var entryInfo: EntryInfo,
|
||||
var historyPosition: Int = -1)
|
||||
|
||||
enum class EntrySection(var position: Int) {
|
||||
MAIN(0), ADVANCED(1);
|
||||
|
||||
companion object {
|
||||
fun getEntrySectionByPosition(position: Int): EntrySection {
|
||||
return if (position == 1) ADVANCED else MAIN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = EntryViewModel::class.java.name
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2022 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.viewmodels
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class KeyGeneratorViewModel: ViewModel() {
|
||||
|
||||
val keyGenerated : LiveData<String> get() = _keyGenerated
|
||||
private val _keyGenerated = MutableLiveData<String>()
|
||||
|
||||
val keyGeneratedValidated : LiveData<Void?> get() = _keyGeneratedValidated
|
||||
private val _keyGeneratedValidated = SingleLiveEvent<Void?>()
|
||||
val requireKeyGeneration : LiveData<Void?> get() = _requireKeyGeneration
|
||||
private val _requireKeyGeneration = SingleLiveEvent<Void?>()
|
||||
|
||||
val passwordGeneratedValidated : LiveData<Void?> get() = _passwordGeneratedValidated
|
||||
private val _passwordGeneratedValidated = SingleLiveEvent<Void?>()
|
||||
val requirePasswordGeneration : LiveData<Void?> get() = _requirePasswordGeneration
|
||||
private val _requirePasswordGeneration = SingleLiveEvent<Void?>()
|
||||
|
||||
val passphraseGeneratedValidated : LiveData<Void?> get() = _passphraseGeneratedValidated
|
||||
private val _passphraseGeneratedValidated = SingleLiveEvent<Void?>()
|
||||
val requirePassphraseGeneration : LiveData<Void?> get() = _requirePassphraseGeneration
|
||||
private val _requirePassphraseGeneration = SingleLiveEvent<Void?>()
|
||||
|
||||
fun setKeyGenerated(passKey: String) {
|
||||
_keyGenerated.value = passKey
|
||||
}
|
||||
|
||||
fun validateKeyGenerated() {
|
||||
_keyGeneratedValidated.call()
|
||||
}
|
||||
|
||||
fun validatePasswordGenerated() {
|
||||
_passwordGeneratedValidated.call()
|
||||
}
|
||||
|
||||
fun validatePassphraseGenerated() {
|
||||
_passphraseGeneratedValidated.call()
|
||||
}
|
||||
|
||||
fun requireKeyGeneration() {
|
||||
_requireKeyGeneration.call()
|
||||
}
|
||||
|
||||
fun requirePasswordGeneration() {
|
||||
_requirePasswordGeneration.call()
|
||||
}
|
||||
|
||||
fun requirePassphraseGeneration() {
|
||||
_requirePassphraseGeneration.call()
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,11 @@ class SingleLiveEvent<T> : MutableLiveData<T>() {
|
||||
}
|
||||
|
||||
// Observe the internal MutableLiveData
|
||||
super.observe(owner, { t ->
|
||||
super.observe(owner) { t ->
|
||||
if (mPending.compareAndSet(true, false)) {
|
||||
observer.onChanged(t)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
|
||||
12
app/src/main/res/drawable-v21/background_image.xml
Normal file
12
app/src/main/res/drawable-v21/background_image.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:color="@color/white"
|
||||
tools:targetApi="lollipop">
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval">
|
||||
<solid android:color="?attr/colorPrimary"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,12 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:color="@color/white"
|
||||
tools:targetApi="lollipop">
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval">
|
||||
<solid android:color="?attr/colorAccent"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/green_lighter"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/green_light"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
@@ -1,5 +1,9 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/>
|
||||
</vector>
|
||||
|
||||
12
app/src/main/res/drawable/ic_time_white_24dp.xml
Normal file
12
app/src/main/res/drawable/ic_time_white_24dp.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_view_list_white_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_view_list_white_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M3,14h4v-4H3V14zM3,19h4v-4H3V19zM3,9h4V5H3V9zM8,14h13v-4H8V14zM8,19h13v-4H8V19zM8,5v4h13V5H8z"/>
|
||||
</vector>
|
||||
@@ -25,6 +25,9 @@
|
||||
android:right="0dp"
|
||||
android:top="12dp"
|
||||
android:bottom="12dp"/>
|
||||
<stroke android:width="1dp" android:color="@color/grey_blue_slight" />
|
||||
<solid android:color="@color/grey_blue_slight"/>
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/grey_blue_slight" />
|
||||
<solid
|
||||
android:color="@color/grey_blue_slight"/>
|
||||
</shape>
|
||||
31
app/src/main/res/drawable/key_border.xml
Normal file
31
app/src/main/res/drawable/key_border.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2022 Jeremy Jamet / Kunzisoft.
|
||||
|
||||
This file is part of KeePassDX.
|
||||
|
||||
KeePassDX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners
|
||||
android:radius="12dp" />
|
||||
<padding
|
||||
android:left="0dp"
|
||||
android:right="0dp"
|
||||
android:top="12dp"
|
||||
android:bottom="12dp"/>
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/grey_blue_slighter" />
|
||||
</shape>
|
||||
@@ -136,6 +136,30 @@
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/entry_content_tab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="120dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:background="?attr/cardBackgroundTransparentColor"
|
||||
app:tabIconTint="?android:attr/textColor"
|
||||
app:tabMode="fixed">
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:id="@+id/entry_content_tab_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:icon="@drawable/ic_view_list_white_24dp" />
|
||||
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:id="@+id/entry_content_tab_advanced"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:icon="@drawable/ic_time_white_24dp" />
|
||||
|
||||
</com.google.android.material.tabs.TabLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
66
app/src/main/res/layout/activity_key_generator.xml
Normal file
66
app/src/main/res/layout/activity_key_generator.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2022 Jeremy Jamet / Kunzisoft.
|
||||
|
||||
This file is part of KeePassDX.
|
||||
|
||||
KeePassDX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/key_generator_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/key_generator_coordinator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/toolbar">
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/key_generator_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<com.kunzisoft.keepass.view.ToolbarAction
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_gravity="bottom"
|
||||
android:theme="?attr/toolbarActionAppearance"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/key_generator_validation"
|
||||
style="@style/KeepassDXStyle.Fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/validate"
|
||||
android:src="@drawable/ic_check_white_24dp"
|
||||
app:fabSize="mini"
|
||||
app:layout_constraintTop_toTopOf="@+id/toolbar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<include
|
||||
layout="@layout/view_button_lock"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -60,13 +60,35 @@
|
||||
android:minHeight="144dp"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary">
|
||||
<ImageView
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_gravity="center"
|
||||
android:padding="0dp"
|
||||
android:contentDescription="@string/about"
|
||||
android:src="@drawable/ic_launcher_foreground"/>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center">
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/activity_password_advanced_unlock_button"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="12dp"
|
||||
android:contentDescription="@string/about"
|
||||
android:elevation="8dp"
|
||||
android:src="@drawable/ic_app_white_24dp"
|
||||
android:background="@drawable/background_image"
|
||||
android:backgroundTint="@color/green_light"/>
|
||||
<TextView
|
||||
android:id="@+id/activity_password_advanced_unlock_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:orientation="vertical"
|
||||
android:text="@string/education_unlock_title"
|
||||
style="@style/KeepassDXStyle.TextAppearance.Secondary.TextOnPrimary"
|
||||
android:gravity="center" />
|
||||
</LinearLayout>
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment_advanced_unlock_container_view"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -17,153 +17,168 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/card_view_margin_vertical"
|
||||
android:paddingBottom="@dimen/card_view_margin_vertical">
|
||||
android:paddingBottom="48dp">
|
||||
|
||||
<com.kunzisoft.keepass.view.TemplateView
|
||||
android:id="@+id/entry_template"
|
||||
<LinearLayout
|
||||
android:id="@+id/entry_section_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/entry_attachments_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/cardViewStyle">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
<com.kunzisoft.keepass.view.TemplateView
|
||||
android:id="@+id/entry_template"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<!-- Binary files -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_attachments_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_attachments"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/entry_attachments_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/cardViewStyle">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/entry_attachments_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Created -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_created_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_created"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_created"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||
|
||||
<!-- Modified -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_modified_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_modified"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_modified"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||
|
||||
<!-- Accessed -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:visibility="gone"
|
||||
android:id="@+id/entry_accessed_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_accessed"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/entry_history"
|
||||
android:name="com.kunzisoft.keepass.activities.fragments.EntryHistoryFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<!-- TODO Custom data
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/entry_custom_data_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/cardViewStyle">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_custom_data_label"
|
||||
android:layout_width="match_parent"
|
||||
style="?attr/cardViewStyle">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/custom_data"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_custom_data"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
-->
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/entry_UUID_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/cardViewStyle">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- UUID -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_UUID_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_UUID"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<!-- Binary files -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_UUID_reference"
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/entry_attachments_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textIsSelectable="true"
|
||||
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||
</HorizontalScrollView>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
android:text="@string/entry_attachments"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
|
||||
</LinearLayout>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/entry_attachments_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/entry_section_advanced"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/entry_UUID_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/cardViewStyle">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- UUID -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_UUID_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_UUID"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_UUID_reference"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textIsSelectable="true"
|
||||
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||
</HorizontalScrollView>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- TODO Custom data
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/entry_custom_data_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/cardViewStyle">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_custom_data_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/custom_data"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_custom_data"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
-->
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/cardViewStyle">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Created -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_created_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_created"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_created"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||
|
||||
<!-- Modified -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_modified_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_modified"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_modified"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||
|
||||
<!-- Accessed -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:visibility="gone"
|
||||
android:id="@+id/entry_accessed_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_accessed"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/entry_history"
|
||||
android:name="com.kunzisoft.keepass.activities.fragments.EntryHistoryFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
167
app/src/main/res/layout/fragment_generate_passphrase.xml
Normal file
167
app/src/main/res/layout/fragment_generate_passphrase.xml
Normal file
@@ -0,0 +1,167 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
|
||||
This file is part of KeePassDX.
|
||||
|
||||
KeePassDX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<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="match_parent"
|
||||
android:importantForAutofill="noExcludeDescendants"
|
||||
android:paddingBottom="@dimen/card_view_margin_vertical"
|
||||
tools:targetApi="o">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="68dp"
|
||||
android:background="?attr/colorPrimary" />
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginEnd="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginLeft="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginRight="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginTop="@dimen/default_margin"
|
||||
android:layout_marginBottom="@dimen/card_view_margin_vertical"
|
||||
android:layout_gravity="bottom"
|
||||
app:cardCornerRadius="4dp">
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/card_view_padding">
|
||||
|
||||
<com.kunzisoft.keepass.view.PassKeyView
|
||||
android:id="@+id/passphrase_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toStartOf="@+id/passphrase_copy_button"
|
||||
android:layout_toLeftOf="@+id/passphrase_copy_button"
|
||||
app:passKeyHint="@string/passphrase"
|
||||
app:passKeyMaxLines="7"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/passphrase_copy_button"
|
||||
style="@style/KeepassDXStyle.ImageButton.Simple"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@+id/passphrase_view"
|
||||
android:layout_alignBottom="@+id/passphrase_view"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:contentDescription="@string/menu_copy"
|
||||
android:src="@drawable/ic_content_copy_white_24dp" />
|
||||
</RelativeLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</FrameLayout>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/ScrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginEnd="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginLeft="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginRight="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginTop="@dimen/card_view_margin_vertical"
|
||||
android:layout_marginBottom="@dimen/card_view_margin_vertical"
|
||||
app:cardCornerRadius="4dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider_word_count"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/word_count"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_toStartOf="@+id/word_count"
|
||||
android:layout_toLeftOf="@+id/word_count"
|
||||
android:contentDescription="@string/content_description_password_length"
|
||||
android:stepSize="1"
|
||||
android:value="@integer/passphrase_generator_word_count_default"
|
||||
android:valueFrom="@integer/passphrase_generator_word_count_min"
|
||||
android:valueTo="@integer/passphrase_generator_word_count_max"
|
||||
app:thumbColor="?attr/chipFilterBackgroundColor"
|
||||
app:trackColorActive="?attr/chipFilterBackgroundColor"
|
||||
app:trackColorInactive="?attr/chipFilterBackgroundColorDisabled" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/word_count"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:inputType="number"
|
||||
android:maxLength="2"
|
||||
android:maxLines="1" />
|
||||
|
||||
</RelativeLayout>
|
||||
<TextView
|
||||
android:id="@+id/character_count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Character count: 51" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/word_case"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="12dp"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/word_separator_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@+id/word_case"
|
||||
android:layout_toRightOf="@+id/word_case">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/word_separator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/word_separator" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
@@ -23,319 +23,241 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_margin"
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAutofill="noExcludeDescendants"
|
||||
android:paddingBottom="@dimen/card_view_margin_vertical"
|
||||
tools:targetApi="o">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/password_input_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/password_copy_button"
|
||||
android:importantForAccessibility="no"
|
||||
android:importantForAutofill="no"
|
||||
app:endIconMode="password_toggle"
|
||||
app:endIconTint="?attr/colorAccent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:hint="@string/hint_generated_password"
|
||||
android:importantForAccessibility="no"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textPassword|textMultiLine"
|
||||
android:maxLines="3"
|
||||
tools:ignore="TextFields" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/password_copy_button"
|
||||
style="@style/KeepassDXStyle.ImageButton.Simple"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:contentDescription="@string/menu_copy"
|
||||
android:src="@drawable/ic_content_copy_white_24dp" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/generate_password_button"
|
||||
android:layout_margin="@dimen/button_margin"
|
||||
android:layout_height="68dp"
|
||||
android:background="?attr/colorPrimary" />
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp"
|
||||
android:drawableEnd="@drawable/ic_generate_password_white_24dp"
|
||||
android:drawableRight="@drawable/ic_generate_password_white_24dp"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/generate_password" />
|
||||
</LinearLayout>
|
||||
android:layout_marginStart="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginEnd="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginLeft="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginRight="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginTop="@dimen/default_margin"
|
||||
android:layout_marginBottom="@dimen/card_view_margin_vertical"
|
||||
android:layout_gravity="bottom"
|
||||
app:cardCornerRadius="4dp">
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/card_view_padding">
|
||||
|
||||
<com.kunzisoft.keepass.view.PassKeyView
|
||||
android:id="@+id/password_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toStartOf="@+id/password_copy_button"
|
||||
android:layout_toLeftOf="@+id/password_copy_button" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/password_copy_button"
|
||||
style="@style/KeepassDXStyle.ImageButton.Simple"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@+id/password_view"
|
||||
android:layout_alignBottom="@+id/password_view"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:contentDescription="@string/menu_copy"
|
||||
android:src="@drawable/ic_content_copy_white_24dp" />
|
||||
</RelativeLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</FrameLayout>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/ScrollView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/length_label"
|
||||
android:text="@string/length"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/length"
|
||||
android:layout_width="50dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/length_label"
|
||||
android:maxLines="1"
|
||||
android:maxLength="3"
|
||||
android:inputType="number"
|
||||
android:text="@string/default_password_length"
|
||||
android:hint="@string/hint_length"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSeekBar
|
||||
android:id="@+id/seekbar_length"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/length"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignTop="@+id/length"
|
||||
android:layout_toEndOf="@+id/length"
|
||||
android:layout_toRightOf="@+id/length"
|
||||
android:contentDescription="@string/content_description_password_length"
|
||||
app:min="@string/min_password_length"
|
||||
android:progress="@string/default_password_length"
|
||||
android:max="@string/max_password_length"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
android:layout_marginStart="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginEnd="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginLeft="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginRight="@dimen/card_view_margin_horizontal"
|
||||
android:layout_marginTop="@dimen/card_view_margin_vertical"
|
||||
android:layout_marginBottom="@dimen/card_view_margin_vertical"
|
||||
app:cardCornerRadius="4dp">
|
||||
<LinearLayout
|
||||
android:id="@+id/RelativeLayout"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginEnd="20dp">
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/card_view_padding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
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>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<CheckBox
|
||||
android:id="@+id/cb_lowercase"
|
||||
android:layout_width="wrap_content"
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider_length"
|
||||
android:layout_width="match_parent"
|
||||
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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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_alignBottom="@+id/length"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_toStartOf="@+id/length"
|
||||
android:layout_toLeftOf="@+id/length"
|
||||
android:contentDescription="@string/content_description_password_length"
|
||||
android:stepSize="1"
|
||||
android:value="@integer/password_generator_length_default"
|
||||
android:valueFrom="@integer/password_generator_length_min"
|
||||
android:valueTo="@integer/password_generator_length_max"
|
||||
app:thumbColor="?attr/chipFilterBackgroundColor"
|
||||
app:trackColorActive="?attr/chipFilterBackgroundColor"
|
||||
app:trackColorInactive="?attr/chipFilterBackgroundColorDisabled" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/length"
|
||||
android:layout_width="50dp"
|
||||
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" />
|
||||
android:inputType="number"
|
||||
android:maxLength="3"
|
||||
android:maxLines="1" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/password_filters"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipSpacingVertical="16dp"
|
||||
android:paddingTop="@dimen/default_margin"
|
||||
android:paddingBottom="@dimen/default_margin">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/upperCase_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/visual_uppercase"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/lowerCase_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/visual_lowercase"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/digits_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/visual_digits"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/minus_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/visual_minus"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/underline_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/visual_underline"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/space_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/space"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/special_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/visual_special"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/brackets_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/visual_brackets"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/extendedASCII_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/visual_extended"/>
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<CheckBox
|
||||
android:id="@+id/cb_extended"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/consider_chars_filter_layout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/ignore_chars_filter_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/consider_chars_filter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/consider_chars_filter"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/ignore_chars_filter_layout"
|
||||
app:layout_constraintStart_toEndOf="@+id/consider_chars_filter_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/ignore_chars_filter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/ignore_chars_filter"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/password_advanced"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipSpacingVertical="16dp"
|
||||
android:paddingTop="@dimen/default_margin"
|
||||
android:paddingBottom="@dimen/default_margin">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/atLeastOne_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/extended_ASCII"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
<TextView
|
||||
android:checked="true"
|
||||
android:text="@string/at_least_one_char"/>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/excludeAmbiguous_filter"
|
||||
style="@style/KeepassDXStyle.Chip.Filter"
|
||||
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_extended"
|
||||
android:layout_toRightOf="@+id/cb_extended"
|
||||
android:text="@string/visual_extended" />
|
||||
</RelativeLayout>
|
||||
android:checked="true"
|
||||
android:text="@string/exclude_ambiguous_chars"/>
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
@@ -71,25 +71,11 @@
|
||||
android:text="@string/password"/>
|
||||
|
||||
<!-- Password Input -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/password_input_layout"
|
||||
<com.kunzisoft.keepass.view.PassKeyView
|
||||
android:id="@+id/password_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:importantForAccessibility="no"
|
||||
android:importantForAutofill="no"
|
||||
app:endIconMode="password_toggle"
|
||||
app:endIconTint="?attr/colorAccent">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/pass_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:importantForAccessibility="no"
|
||||
android:importantForAutofill="yes"
|
||||
android:autofillHints="newPassword"
|
||||
android:maxLines="1"
|
||||
android:hint="@string/hint_pass"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
app:passKeyVisible="false"/>
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/password_repeat_input_layout"
|
||||
android:layout_width="match_parent"
|
||||
@@ -100,14 +86,14 @@
|
||||
app:endIconMode="password_toggle"
|
||||
app:endIconTint="?attr/colorAccent">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/pass_conf_password"
|
||||
android:id="@+id/password_confirmation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:ems="10"
|
||||
android:importantForAccessibility="no"
|
||||
android:importantForAutofill="yes"
|
||||
android:autofillHints="newPassword"
|
||||
android:maxLines="1"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textPassword|textMultiLine"
|
||||
android:maxLines="3"
|
||||
android:hint="@string/hint_conf_pass"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
@@ -23,15 +23,15 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/icon_picker_tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/icon_picker_pager"
|
||||
android:id="@+id/tabs_view_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -61,7 +61,9 @@
|
||||
android:layout_toLeftOf="@+id/item_attachment_size_container"
|
||||
android:layout_toEndOf="@+id/item_attachment_broken"
|
||||
android:layout_toRightOf="@+id/item_attachment_broken"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="2"
|
||||
tools:text="BinaryFile.attach" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
tools:text="5136" />
|
||||
tools:text="513 651" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -20,18 +20,79 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:background="@color/grey_blue_deep"
|
||||
android:orientation="vertical">
|
||||
<LinearLayout
|
||||
android:id="@+id/magikeyboard_entry_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="@drawable/key_border"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/magikeyboard_entry_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:textSize="16sp"
|
||||
tools:text="Git account signup"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:textColor="@color/white"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/magikeyboard_database_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
<ImageView
|
||||
android:id="@+id/magikeyboard_database_color"
|
||||
android:layout_width="6dp"
|
||||
android:layout_height="6dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/content_description_database_color"
|
||||
android:src="@drawable/background_icon" />
|
||||
<TextView
|
||||
android:id="@+id/magikeyboard_database_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:textSize="11sp"
|
||||
tools:text="Database A"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:textColor="@color/grey_blue_slighter"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/magikeyboard_entry_text"
|
||||
android:id="@+id/magikeyboard_package_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/grey_blue_deep"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/white"/>
|
||||
android:textSize="12sp"
|
||||
tools:text="com.kunzisoft.keepass"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:textColor="@color/grey_blue_slighter"/>
|
||||
<com.kunzisoft.keepass.magikeyboard.KeyboardView
|
||||
android:id="@+id/magikeyboard_view"
|
||||
style="@style/KeepassDXStyle.Keyboard"
|
||||
|
||||
@@ -34,12 +34,12 @@
|
||||
android:id="@+id/keyboard_popup_close"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:contentDescription="@string/content_description_keyboard_close_fields"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_margin="4dp"
|
||||
android:padding="8dp"
|
||||
android:background="@drawable/key_background"
|
||||
android:contentDescription="@string/content_description_keyboard_close_fields"
|
||||
android:src="@drawable/ic_close_white_24dp" />
|
||||
</RelativeLayout>
|
||||
@@ -17,19 +17,20 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<RelativeLayout
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/keyboard_popup_field_item_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/keyboard_popup_field_item_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/key_background"
|
||||
android:layout_margin="5dp"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
tools:text="Sample"
|
||||
android:textColor="@color/white"/>
|
||||
</RelativeLayout>
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/key_background"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="4dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
tools:text="Sample"
|
||||
android:textColor="@color/white"/>
|
||||
@@ -10,7 +10,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:fabSize="mini"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_margin="9dp"
|
||||
android:contentDescription="@string/lock"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/ic_lock_white_padding_24dp"
|
||||
|
||||
@@ -36,19 +36,16 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@+id/password_checkbox"
|
||||
android:layout_toEndOf="@+id/password_checkbox"
|
||||
android:importantForAccessibility="no"
|
||||
android:importantForAutofill="no"
|
||||
app:endIconMode="password_toggle"
|
||||
app:endIconTint="?attr/colorAccent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/password"
|
||||
android:id="@+id/password_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp"
|
||||
android:hint="@string/password"
|
||||
android:inputType="textPassword"
|
||||
android:importantForAccessibility="no"
|
||||
android:importantForAutofill="yes"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
|
||||
52
app/src/main/res/layout/view_passkey.xml
Normal file
52
app/src/main/res/layout/view_passkey.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/password_input_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:importantForAccessibility="no"
|
||||
android:importantForAutofill="no"
|
||||
app:endIconMode="password_toggle"
|
||||
app:endIconTint="?attr/colorAccent"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/password_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:hint="@string/hint_pass"
|
||||
android:importantForAccessibility="no"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textPassword|textMultiLine"
|
||||
android:maxLines="3"
|
||||
tools:ignore="TextFields" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/password_strength_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
app:trackCornerRadius="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/password_input_layout"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/password_entropy"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Entropy: 72.50 bit"
|
||||
android:textSize="11sp"
|
||||
android:layout_margin="4dp"
|
||||
android:textColor="?attr/colorAccent"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/password_input_layout"
|
||||
app:layout_constraintEnd_toEndOf="@+id/password_input_layout" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
28
app/src/main/res/menu/key_generator.xml
Normal file
28
app/src/main/res/menu/key_generator.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||
|
||||
This file is part of KeePassDX.
|
||||
|
||||
KeePassDX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/menu_generate"
|
||||
android:icon="@drawable/ic_random_white_24dp"
|
||||
android:title="@string/generate_password"
|
||||
android:orderInCategory="8"
|
||||
app:iconTint="?attr/colorControlNormal"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@@ -20,12 +20,12 @@
|
||||
<string name="homepage">الصفحة الرئيسة</string>
|
||||
<string name="accept">قبول</string>
|
||||
<string name="add_group">إضافة مجموعة</string>
|
||||
<string name="encryption">التشفير</string>
|
||||
<string name="encryption_algorithm">خوارزمية التشفير</string>
|
||||
<string name="encryption">التعميّة</string>
|
||||
<string name="encryption_algorithm">خوارزمية التعميّة</string>
|
||||
<string name="application">التطبيق</string>
|
||||
<string name="brackets">الأقواس</string>
|
||||
<string name="extended_ASCII">تمديد ASCII</string>
|
||||
<string name="allow">السماح</string>
|
||||
<string name="allow">سماح</string>
|
||||
<string name="clipboard_cleared">مُسِحت الحافظة</string>
|
||||
<string name="clipboard_error_title">خطأ في الحافظة</string>
|
||||
<string name="clipboard_error_clear">تعذَّر مسح الحافظة</string>
|
||||
@@ -104,14 +104,14 @@
|
||||
<string name="education_entry_new_field_title">إضافة حقول مخصصة</string>
|
||||
<string name="education_field_copy_title">نسخ حقل</string>
|
||||
<string name="education_lock_title">تأمين قاعدة البيانات</string>
|
||||
<string name="feedback">ردود الفعل</string>
|
||||
<string name="feedback">أرسل انطباعاتك</string>
|
||||
<string name="about_description">التنفيذ لمُدير كلمات المرور «كي باس» على نظام أندرويد</string>
|
||||
<string name="add_entry">إضافة إدخال</string>
|
||||
<string name="edit_entry">تحرير الإدخال</string>
|
||||
<string name="add_entry">أضف مدخل</string>
|
||||
<string name="edit_entry">تحرير مدخل</string>
|
||||
<string name="key_derivation_function">وظيفة اشتقاق المفتاح</string>
|
||||
<string name="app_timeout">المهلة</string>
|
||||
<string name="app_timeout_summary">مدة الخمول قبل قفل قاعدة البيانات</string>
|
||||
<string name="file_manager_install_description">المحرر الذي يمتلك صلاحتي ACTION_CREATE_DOCUMENT و ACTION_OPEN_DOCUMENT ضروري لانشاء, وفتح وحفض قواعد البيانات.</string>
|
||||
<string name="file_manager_install_description">مدير الملفات الذي يمكنه القيام بالإجراءين ACTION_CREATE_DOCUMENT و ACTION_OPEN_DOCUMENT ضروري لانشاء, وفتح وحفض قواعد البيانات.</string>
|
||||
<string name="clipboard_error">بعض الأجهزة لا تسمح للتطبيقات باستعمال الحافظة.</string>
|
||||
<string name="clipboard_timeout">مهلة الحافظة</string>
|
||||
<string name="clipboard_timeout_summary">مدة التخزين في الحافظة(إذا كان جهازك يدعمها)</string>
|
||||
@@ -141,7 +141,7 @@
|
||||
<string name="invalid_db_sig">تعذر تمييز نسق قاعدة البيانات.</string>
|
||||
<string name="keyfile_is_empty">ملف المفتاح فارغ.</string>
|
||||
<string name="list_entries_show_username_title">أظهر أسماء المستخدمين</string>
|
||||
<string name="list_entries_show_username_summary">أظهر أسماء المستخدمين في قوائم المدخلات</string>
|
||||
<string name="list_entries_show_username_summary">اعرض اسماء المستخدمين في قوائم المدخلات</string>
|
||||
<string name="hint_generated_password">كلمة السر الموَلدة</string>
|
||||
<string name="hint_keyfile">الملف المفتاحي</string>
|
||||
<string name="hide_password_title">اخفاء كلمات السر</string>
|
||||
@@ -215,7 +215,7 @@
|
||||
<string name="keyboard_key_sound_title">صوت عند اللمس</string>
|
||||
<string name="allow_no_password_title">"إسمح بالفتح دون كلمة سر "</string>
|
||||
<string name="enable_read_only_title">محمي من التعديل</string>
|
||||
<string name="enable_read_only_summary">افتح قاعدة البيانات للقراءة فقط افتراضيا</string>
|
||||
<string name="enable_read_only_summary">افتح قاعدة البيانات في وضع القراءة افتراضيا</string>
|
||||
<string name="enable_education_screens_title">شاشات تعليمية</string>
|
||||
<string name="reset_education_screens_summary">أعد عرض كل المعلومات التعليمية</string>
|
||||
<string name="reset_education_screens_text">إعادة تعيين الشاشات التلميحات</string>
|
||||
@@ -236,7 +236,7 @@
|
||||
<string name="reset_education_screens_title">إعادة تعيين الشاشات التعليمية</string>
|
||||
<string name="education_search_title">البحث من خلال الإدخالات</string>
|
||||
<string name="content_description_open_file">افتح الملف</string>
|
||||
<string name="content_description_add_entry">إضافة إدخال</string>
|
||||
<string name="content_description_add_entry">إضافة مدخلة</string>
|
||||
<string name="content_description_add_group">إضافة مجموعة</string>
|
||||
<string name="content_description_file_information">معلومات الملف</string>
|
||||
<string name="entry_password_generator">مولد كلمة السر</string>
|
||||
@@ -254,8 +254,8 @@
|
||||
<string name="content_description_password_length">طول كلمة السر</string>
|
||||
<string name="entry_add_field">أضف حقل</string>
|
||||
<string name="content_description_remove_field">أزل حقل</string>
|
||||
<string name="error_move_entry_here">لا يمكن نقل إدخال هنا.</string>
|
||||
<string name="error_copy_entry_here">لا يمكن نسخ إدخال هنا.</string>
|
||||
<string name="error_move_entry_here">يتعذر نقل مدخل إلى هنا.</string>
|
||||
<string name="error_copy_entry_here">يتعذر نسخ مدخال إلى هنا.</string>
|
||||
<string name="list_groups_show_number_entries_title">عرض عدد المدخلات</string>
|
||||
<string name="list_groups_show_number_entries_summary">عرض عدد المدخلات في المجموعة</string>
|
||||
<string name="content_description_update_from_list">تحديث</string>
|
||||
@@ -302,7 +302,7 @@
|
||||
<string name="menu_delete_entry_history">احذف السجل</string>
|
||||
<string name="menu_restore_entry_history">استعادة السجل</string>
|
||||
<string name="menu_empty_recycle_bin">أفرغ سلة المحذوفات</string>
|
||||
<string name="menu_save_database">اِحفظ قاعدة البيانات</string>
|
||||
<string name="menu_save_database">اِحفظ البيانات</string>
|
||||
<string name="menu_master_key_settings">إعدادات المفتاح الرئيسي</string>
|
||||
<string name="menu_security_settings">إعدادات الأمن</string>
|
||||
<string name="creating_database">ينشئ قاعدة البيانات…</string>
|
||||
@@ -357,8 +357,6 @@
|
||||
<string name="autofill_web_domain_blocklist_title">قائمة النطاقات المحظورة</string>
|
||||
<string name="autofill_application_id_blocklist_summary">منع الملء التلقائي للتطبيقات الموجودة في القائمة</string>
|
||||
<string name="autofill_application_id_blocklist_title">قائمة التطبيقات المحظورة</string>
|
||||
<string name="autofill_auto_search_summary">يقترح تلقائيا نتائج البحث في النطاقات ومعرفات التطبيقات</string>
|
||||
<string name="autofill_auto_search_title">البحث التلقائي</string>
|
||||
<string name="content_description_repeat_toggle_password_visibility">بدِّل ظهور كلمة السر</string>
|
||||
<string name="hide_expired_entries_summary">لن تعرض المدخلات منتهية الصلاحية</string>
|
||||
<string name="education_read_only_summary">غيِّر وضع الجلسة.
|
||||
@@ -458,7 +456,7 @@
|
||||
<string name="error_rebuild_list">يتعذر إعادة بناء القائمة بشكل صحيح.</string>
|
||||
<string name="menu_keystore_remove_key">احذف رمز فك القفل المتقدم</string>
|
||||
<string name="menu_form_filling_settings">تعبئة الحقول</string>
|
||||
<string name="menu_reload_database">أعد تحميل قاعدة البيانات</string>
|
||||
<string name="menu_reload_database">أعد تحميل البيانات</string>
|
||||
<string name="menu_external_icon">أيقونة خارجية</string>
|
||||
<string name="registration_mode">وضع التسجيل</string>
|
||||
<string name="import_app_properties_title">استورد خصائص التطبيق</string>
|
||||
@@ -468,7 +466,7 @@
|
||||
<string name="error_import_app_properties">خطأ أثناء استيراد خصائص التطبيق</string>
|
||||
<string name="error_export_app_properties">خطأ أثناء تصدير خصائص التطبيق</string>
|
||||
<string name="warning_database_info_changed">غُيِّرت معلومات قاعدة البيانات من خارج هذا التطبيق.</string>
|
||||
<string name="warning_database_info_changed_options">اكتب فوق التعديلات الخارجية عن طريق حفظ قاعدة البيانات أو أعد تحميلها لجلب آخر التغييرات.</string>
|
||||
<string name="warning_database_info_changed_options">ادمج البيانات أو استبدل التعديلات الخارجية بحفظ قاعدة البيانات أو أعد تحميلها لجلب آخر التغييرات.</string>
|
||||
<string name="open_advanced_unlock_prompt_store_credential">افتح محث فك القفل المتقدم لتخزين بيانات الاعتماد</string>
|
||||
<string name="open_advanced_unlock_prompt_unlock_database">افتح محث فك القفل المتقدم لفتح قاعدة البيانات</string>
|
||||
<string name="credential_before_click_advanced_unlock_button">اكتب كلمة السر، وأنقر هذا الزر.</string>
|
||||
@@ -486,4 +484,22 @@
|
||||
<string name="autofill_save_search_info_title">احفظ معلومات البحث</string>
|
||||
<string name="autofill_ask_to_save_data_title">اسأل لحفظ البيانات</string>
|
||||
<string name="content_description_database_color">لون قاعدة البيانات</string>
|
||||
<string name="menu_merge_from">ادمج من…</string>
|
||||
<string name="show_uuid_summary">يعرض \"المعرف العام\" المرتبط بمُدخل او بمجموعة</string>
|
||||
<string name="expired">انتهت المهلة</string>
|
||||
<string name="tags">الوسوم</string>
|
||||
<string name="menu_merge_database">ادمج البيانات</string>
|
||||
<string name="current_group">المجموعة الحالية</string>
|
||||
<string name="search_filters">مرشحات البحث</string>
|
||||
<string name="warning_file_too_big">يفترض بقاعدة البيانات أن تحوي ملفات صغيرة الحجم ( كمفاتيح PGP).
|
||||
\n
|
||||
\nبرفع هذا الملف قد يزداد حجم قاعدة البيانات ويضعف أداءها.</string>
|
||||
<string name="error_move_group_here">يتعذر نقل المجموعة إلى هنا.</string>
|
||||
<string name="menu_save_copy_to">احفظ نسخة إلى…</string>
|
||||
<string name="searchable">يمكن البحث عنه</string>
|
||||
<string name="custom_data">بيانات مخصصة</string>
|
||||
<string name="case_sensitive">حساسة لحالة الأحرف</string>
|
||||
<string name="regex">تعابير نمطية</string>
|
||||
<string name="enable_keep_screen_on_title">أبق الشاشة شغّالة</string>
|
||||
<string name="enable_education_screens_summary">أبرز العناصر لتعلم طريقة عمل التطبيق</string>
|
||||
</resources>
|
||||
@@ -29,17 +29,17 @@
|
||||
<string name="application">Aplikace</string>
|
||||
<string name="menu_app_settings">Nastavení aplikace</string>
|
||||
<string name="brackets">Závorky</string>
|
||||
<string name="file_manager_install_description">K založení, otevření a uložení databázových souborů je potřebný správce souborů, který akceptuje akci intentu ACTION_CREATE_DOCUMENT a ACTION_OPEN_DOCUMENT.</string>
|
||||
<string name="file_manager_install_description">K založení, otevření a uložení databázových souborů je potřebný správce souborů, který přijímá akci záměru ACTION_CREATE_DOCUMENT a ACTION_OPEN_DOCUMENT.</string>
|
||||
<string name="clipboard_cleared">Schránka vyčištěna</string>
|
||||
<string name="clipboard_error_title">Chyba schránky</string>
|
||||
<string name="clipboard_error">Některá zařízení nedovolují aplikacím používat schránku.</string>
|
||||
<string name="clipboard_error_clear">Nelze vyprázdnit schránku</string>
|
||||
<string name="clipboard_error_clear">Nedaří se vyprázdnit schránku</string>
|
||||
<string name="clipboard_timeout">Časový limit schránky</string>
|
||||
<string name="clipboard_timeout_summary">Doba uchování ve schránce (je-li podporována zařízením)</string>
|
||||
<string name="clipboard_timeout_summary">Doba uchování ve schránce (pokud je zařízením podporována)</string>
|
||||
<string name="select_to_copy">Vyberte zkopírovat %1$s do schránky</string>
|
||||
<string name="retrieving_db_key">Načítám klíč databáze…</string>
|
||||
<string name="retrieving_db_key">Načítání klíč databáze…</string>
|
||||
<string name="database">Databáze</string>
|
||||
<string name="decrypting_db">Dešifrování obsahu databáze…</string>
|
||||
<string name="decrypting_db">Rozšifrovávání obsahu databáze…</string>
|
||||
<string name="default_checkbox">Použít jako výchozí databázi</string>
|
||||
<string name="digits">Číslice</string>
|
||||
<string name="select_database_file">Otevřít existující databázi</string>
|
||||
@@ -61,15 +61,15 @@
|
||||
<string name="error_can_not_handle_uri">KeePassDX nemůže zpracovat toto URI.</string>
|
||||
<string name="error_file_not_create">Soubor se nepodařilo vytvořit</string>
|
||||
<string name="error_invalid_db">Databázi se nepodařilo načíst.</string>
|
||||
<string name="error_invalid_path">Ujistěte se, že cesta je správná.</string>
|
||||
<string name="error_no_name">Zadejte jméno.</string>
|
||||
<string name="error_invalid_path">Ujistěte se, že je popis umístění správný.</string>
|
||||
<string name="error_no_name">Zadejte název.</string>
|
||||
<string name="error_nokeyfile">Vyberte soubor s klíčem.</string>
|
||||
<string name="error_out_of_memory">Nedostatek paměti k načtení celé databáze.</string>
|
||||
<string name="error_out_of_memory">Nedostatek paměti pro načtení celé databáze.</string>
|
||||
<string name="error_pass_gen_type">Je třeba zvolit alespoň jeden způsob vytváření hesla.</string>
|
||||
<string name="error_pass_match">Hesla se neshodují.</string>
|
||||
<string name="error_rounds_too_large">Příliš vysoký „Počet průchodů“. Nastavuji na 2147483648.</string>
|
||||
<string name="error_pass_match">Zadání hesla se neshodují.</string>
|
||||
<string name="error_rounds_too_large">Příliš vysoký „Počet průchodů“. Nastavuje se na 2147483648.</string>
|
||||
<string name="error_string_key">Je třeba, aby každý řetězec měl název kolonky.</string>
|
||||
<string name="error_wrong_length">Do pole „Délka“ zadejte celé kladné číslo.</string>
|
||||
<string name="error_wrong_length">Do kolonky „Délka“ zadejte celé kladné číslo.</string>
|
||||
<string name="field_name">Název kolonky</string>
|
||||
<string name="field_value">Hodnota kolonky</string>
|
||||
<string name="file_browser">Správce souborů</string>
|
||||
@@ -144,7 +144,7 @@
|
||||
<string name="error_autofill_enable_service">Službu automatického vyplňování se nepodařilo zapnout.</string>
|
||||
<string name="file_not_found_content">Soubor nenalezen. Zkuste jej otevřít ze správce souborů.</string>
|
||||
<string name="list_entries_show_username_title">Zobrazit uživatelská jména</string>
|
||||
<string name="list_entries_show_username_summary">V seznamech záznamů zobrazit uživatelská jména</string>
|
||||
<string name="list_entries_show_username_summary">Zobrazí uživatelská jména v seznamech záznamů</string>
|
||||
<string name="copy_field">Kopie %1$s</string>
|
||||
<string name="menu_form_filling_settings">Vyplňování formulářů</string>
|
||||
<string name="menu_copy">Zkopírovat</string>
|
||||
@@ -223,14 +223,14 @@
|
||||
<string name="magic_keyboard_title">Klávesnice Magikeyboard</string>
|
||||
<string name="magic_keyboard_explanation_summary">Aktivovat vlastní klávesnici, která snadno vyplní hesla a další položky identity</string>
|
||||
<string name="allow_no_password_title">Umožnit bez hlavního klíče</string>
|
||||
<string name="allow_no_password_summary">Povolit klepnutí na \"Otevřít\", i když není vybráno žádné heslo</string>
|
||||
<string name="allow_no_password_summary">Povolit klepnutí na „Otevřít“, i když není vybráno žádné heslo</string>
|
||||
<string name="enable_read_only_title">Chráněno před zápisem</string>
|
||||
<string name="enable_read_only_summary">Ve výchozím stavu otevřít databázi pouze pro čtení</string>
|
||||
<string name="enable_education_screens_title">Vzdělávací nápovědy</string>
|
||||
<string name="enable_education_screens_summary">Zvýraznit prvky k pochopení práce s aplikací</string>
|
||||
<string name="reset_education_screens_title">Nastavit vzdělávací nápovědy do výchozího stavu</string>
|
||||
<string name="reset_education_screens_summary">Opět zobrazit všechny vzdělávací informace</string>
|
||||
<string name="reset_education_screens_text">Nastavit vzdělávací nápovědy do výchozího stavu</string>
|
||||
<string name="reset_education_screens_text">Znovu zobrazit výukové nápovědy</string>
|
||||
<string name="education_create_database_title">Vytvořit databázový soubor</string>
|
||||
<string name="education_create_database_summary">Vytvořte svůj první soubor pro správu hesel.</string>
|
||||
<string name="education_select_database_title">Otevřít existující databázi</string>
|
||||
@@ -244,15 +244,15 @@
|
||||
<string name="education_entry_edit_title">Upravit záznam</string>
|
||||
<string name="education_entry_edit_summary">Přidejte ke svému záznamu vlastní kolonky. Společná data mohou být sdílena mezi různými kolonkami záznamu odkazem.</string>
|
||||
<string name="education_generate_password_title">Vytvořit silné heslo</string>
|
||||
<string name="education_generate_password_summary">Generujte silné heslo pro svůj záznam, definujte je podle kritérií formuláře, a nezapomeňte na bezpečné heslo.</string>
|
||||
<string name="education_generate_password_summary">Nechte si vytvořit odolné heslo pro svůj záznam, definujte je podle kritérií formuláře, a nezapomeňte na bezpečné heslo.</string>
|
||||
<string name="education_entry_new_field_title">Přidat vlastní kolonky</string>
|
||||
<string name="education_entry_new_field_summary">Registrovat další kolonku, zadat hodnotu a volitelně ji ochránit.</string>
|
||||
<string name="education_unlock_title">Odemknout databázi</string>
|
||||
<string name="education_read_only_title">Ochraňte svou databázi před zápisem</string>
|
||||
<string name="education_read_only_summary">Změnit režim otevírání pro dané sezení.
|
||||
\n
|
||||
\nV režimu \"pouze pro čtení\" zabráníte nechtěným změnám v databázi.
|
||||
\nV režimu \"zápisu\" je možné přidávat, mazat nebo měnit všechny prvky podle libosti.</string>
|
||||
\nV režimu „pouze pro čtení“ zabráníte nechtěným změnám v databázi.
|
||||
\nV režimu „umožněné změny“ je možné přidávat, mazat nebo měnit všechny prvky podle libosti.</string>
|
||||
<string name="education_field_copy_title">Zkopírovat kolonku</string>
|
||||
<string name="education_field_copy_summary">Zkopírované kolonky lze vkládat podle libosti.
|
||||
\n
|
||||
@@ -262,20 +262,20 @@
|
||||
<string name="education_sort_title">Řazení položek</string>
|
||||
<string name="education_sort_summary">Vyberte řazení položek a skupin.</string>
|
||||
<string name="education_donation_title">Zapojit se</string>
|
||||
<string name="education_donation_summary">Zapojte se a pomozte zvýšit stabilitu, bezpečnost a doplnění dalších funkcí.</string>
|
||||
<string name="education_donation_summary">Zapojte se a pomozte zvýšit stabilitu, zabezpečení a doplnění dalších funkcí.</string>
|
||||
<string name="html_text_ad_free">Na rozdíl od mnoha aplikací pro správu hesel je tato <strong>bez reklam</strong>, je <strong>svobodný software pod copyleft licencí</strong> a nesbírá žádné osobní údaje na svých serverech bez ohledu na to, jakou verzi používáte.</string>
|
||||
<string name="html_text_buy_pro">Zakoupením varianty \"pro\" získáte přístup k tomuto <strong>vizuálnímu stylu</strong> a hlavně pomůžete <strong>uskutečnění komunitních projektů.</strong></string>
|
||||
<string name="html_text_buy_pro">Zakoupením varianty „pro“ získáte přístup k tomuto <strong>vizuálnímu stylu</strong> a hlavně pomůžete <strong>uskutečnění komunitních projektů.</strong></string>
|
||||
<string name="html_text_feature_generosity">Tento <strong>vizuální styl</strong> je k dispozici díky vaší štědrosti.</string>
|
||||
<string name="html_text_donation">Pro zajištění svobody nás všech a pokračování aktivity počítáme s Vaším <strong>přispěním.</strong></string>
|
||||
<string name="html_text_dev_feature">Tato funkce je <strong>ve vývoji</strong> a potřebuje Váš <strong>příspěvek</strong>, aby byla brzy k dispozici.</string>
|
||||
<string name="html_text_dev_feature_buy_pro">Zakoupením <strong>pro</strong> varianty,</string>
|
||||
<string name="html_text_dev_feature_contibute"><strong>Zapojením se</strong>,</string>
|
||||
<string name="html_text_dev_feature_contibute"><strong>Podpořením vývoje</strong>,</string>
|
||||
<string name="html_text_dev_feature_encourage">povzbudíte vývojáře k doplnění <strong>nových funkcí</strong> a <strong>opravám chyb</strong> dle vašich připomínek.</string>
|
||||
<string name="html_text_dev_feature_thanks">Mockrát děkujeme za Váš příspěvek.</string>
|
||||
<string name="html_text_dev_feature_thanks">Mnohokrát děkujeme za Váš příspěvek.</string>
|
||||
<string name="html_text_dev_feature_work_hard">Tvrdě pracujeme na brzkém vydání této funkce.</string>
|
||||
<string name="html_text_dev_feature_upgrade">Pamatujte na aktualizaci aplikace instalováním nových verzí.</string>
|
||||
<string name="download">Stáhnout</string>
|
||||
<string name="contribute">Přispět</string>
|
||||
<string name="contribute">Podpořit vývoj</string>
|
||||
<string name="style_choose_title">Vzhled aplikace</string>
|
||||
<string name="style_choose_summary">Motiv vzhledu aplikace</string>
|
||||
<string name="icon_pack_choose_title">Sada ikon</string>
|
||||
@@ -316,14 +316,14 @@
|
||||
<string name="content_description_add_entry">Přidat záznam</string>
|
||||
<string name="content_description_add_group">Přidat skupinu</string>
|
||||
<string name="content_description_file_information">Informace o souboru</string>
|
||||
<string name="content_description_password_checkbox">Checkbox hesla</string>
|
||||
<string name="content_description_keyfile_checkbox">Checkbox souboru s klíčem</string>
|
||||
<string name="content_description_password_checkbox">Zaškrtávací kolonka hesla</string>
|
||||
<string name="content_description_keyfile_checkbox">Zaškrtávací kolonka souboru s klíčem</string>
|
||||
<string name="content_description_repeat_toggle_password_visibility">Opakovat přepnutí viditelnosti hesla</string>
|
||||
<string name="content_description_entry_icon">Ikona záznamu</string>
|
||||
<string name="entry_password_generator">Generátor hesel</string>
|
||||
<string name="content_description_password_length">Délka hesla</string>
|
||||
<string name="entry_add_field">Přidat pole</string>
|
||||
<string name="content_description_remove_field">Odebrat pole</string>
|
||||
<string name="entry_add_field">Přidat kolonku</string>
|
||||
<string name="content_description_remove_field">Odebrat kolonku</string>
|
||||
<string name="entry_UUID">UUID</string>
|
||||
<string name="error_move_entry_here">Sem záznam přesunout nelze.</string>
|
||||
<string name="error_copy_entry_here">Sem záznam zkopírovat nelze.</string>
|
||||
@@ -331,8 +331,8 @@
|
||||
<string name="list_groups_show_number_entries_summary">Zobrazit počet záznamů ve skupině</string>
|
||||
<string name="content_description_background">Pozadí</string>
|
||||
<string name="content_description_update_from_list">Aktualizovat</string>
|
||||
<string name="content_description_keyboard_close_fields">Zavřít pole</string>
|
||||
<string name="error_create_database_file">Nelze vytvořit databázi s tímto heslem a souborem klíče.</string>
|
||||
<string name="content_description_keyboard_close_fields">Zavřít kolonky</string>
|
||||
<string name="error_create_database_file">Nepodařilo se vytvořit databázi s tímto heslem a souborem klíče.</string>
|
||||
<string name="menu_advanced_unlock_settings">Rozšířené odemknutí</string>
|
||||
<string name="biometric">Biometrika</string>
|
||||
<string name="biometric_auto_open_prompt_title">Automaticky otevřít pobídku</string>
|
||||
@@ -345,15 +345,15 @@
|
||||
<string name="entry_setup_otp">Nastavit heslo na jedno použití (OTP)</string>
|
||||
<string name="otp_type">Typ OTP</string>
|
||||
<string name="otp_secret">Tajnost</string>
|
||||
<string name="otp_period">Interval (vteřiny)</string>
|
||||
<string name="otp_period">Interval (sekundy)</string>
|
||||
<string name="otp_counter">Čítač</string>
|
||||
<string name="otp_digits">Číslice</string>
|
||||
<string name="otp_algorithm">Algoritmus</string>
|
||||
<string name="entry_otp">OTP</string>
|
||||
<string name="error_invalid_OTP">Neplatná OTP tajnost.</string>
|
||||
<string name="error_disallow_no_credentials">Nejméně jeden přihlašovací údaj musí být zadán.</string>
|
||||
<string name="error_disallow_no_credentials">Je třeba aby byl zadán alespoň jeden přihlašovací údaj.</string>
|
||||
<string name="error_copy_group_here">Sem skupinu kopírovat nelze.</string>
|
||||
<string name="error_otp_secret_key">Tajný klíč musí mít formát Base32.</string>
|
||||
<string name="error_otp_secret_key">Je třeba, aby tajný klíč byl ve formátu Base32.</string>
|
||||
<string name="error_otp_counter">Čítač musít být mezi %1$d a %2$d.</string>
|
||||
<string name="error_otp_period">Interval musít být mezi %1$d a %2$d vteřinami.</string>
|
||||
<string name="error_otp_digits">Token musí obsahovat mezi %1$d a %2$d číslicemi.</string>
|
||||
@@ -384,7 +384,7 @@
|
||||
<string name="compression_none">Žádná</string>
|
||||
<string name="compression_gzip">Gzip</string>
|
||||
<string name="device_keyboard_setting_title">Nastavení klávesnice zařízení</string>
|
||||
<string name="error_save_database">Nebylo možno uložit databázi.</string>
|
||||
<string name="error_save_database">Databázi nebylo možné uložit.</string>
|
||||
<string name="menu_save_database">Uložit databázi</string>
|
||||
<string name="menu_empty_recycle_bin">Vysypat koš</string>
|
||||
<string name="command_execution">Provádění příkazu…</string>
|
||||
@@ -399,14 +399,14 @@
|
||||
<string name="keyboard_auto_go_action_title">Akce auto-klávesy</string>
|
||||
<string name="keyboard_auto_go_action_summary">Akce klávesy \"Jít\" po stisknutí klávesy \"Kolonka\"</string>
|
||||
<string name="download_attachment">Stáhnout %1$s</string>
|
||||
<string name="download_initialization">Zahájení…</string>
|
||||
<string name="download_initialization">Zahajování…</string>
|
||||
<string name="download_progression">Probíhá: %1$d%%</string>
|
||||
<string name="download_finalization">Dokončování…</string>
|
||||
<string name="download_complete">Kompletní!</string>
|
||||
<string name="hide_expired_entries_title">Skrýt propadlé záznamy</string>
|
||||
<string name="hide_expired_entries_summary">Propadlé záznamy nebudou ukázány</string>
|
||||
<string name="download_complete">Dokončeno!</string>
|
||||
<string name="hide_expired_entries_title">Skrýt záznamy kterým skončila platnost</string>
|
||||
<string name="hide_expired_entries_summary">Nejsou zobrazovány záznamy kterým skončila platnost</string>
|
||||
<string name="contact">Kontakt</string>
|
||||
<string name="contribution">Příspěvky</string>
|
||||
<string name="contribution">Podpoření vývoje</string>
|
||||
<string name="feedback">Zpětná vazba</string>
|
||||
<string name="auto_focus_search_title">Snadné hledání</string>
|
||||
<string name="auto_focus_search_summary">Při otevření databáze žádat hledání</string>
|
||||
@@ -424,18 +424,16 @@
|
||||
<string name="html_about_contribution">Abychom si <strong>udrželi svoji svobodu</strong>, <strong>mohli opravovat chyby</strong>, <strong>přidávat nové funkce</strong> a <strong>byli pořád aktivní</strong>, počítáme s Vaším <strong>přispěním</strong>.</string>
|
||||
<string name="error_create_database">Nepodařilo se vytvořit soubor databáze.</string>
|
||||
<string name="entry_add_attachment">Přidat přílohu</string>
|
||||
<string name="discard">Zavrhnout</string>
|
||||
<string name="discard_changes">Zavrhnout změny\?</string>
|
||||
<string name="discard">Zahodit</string>
|
||||
<string name="discard_changes">Zahodit změny\?</string>
|
||||
<string name="validate">Zkontrolovat</string>
|
||||
<string name="education_setup_OTP_summary">Nastavit správu One-Time hesla (HOTP / TOTP) pro založení tokenu požadovaného pro dvoufázové ověření (2FA).</string>
|
||||
<string name="education_setup_OTP_title">Nastavit OTP</string>
|
||||
<string name="autofill_auto_search_summary">Automaticky navrhnout výsledky hledání z webové domény nebo ID aplikace</string>
|
||||
<string name="autofill_auto_search_title">Samočinné hledání</string>
|
||||
<string name="lock_database_show_button_summary">Zobrazí tlačítko zámku v uživatelském rozhraní</string>
|
||||
<string name="lock_database_show_button_title">Zobrazit tlačítko zámku</string>
|
||||
<string name="autofill_preference_title">Nastavení samovyplnění</string>
|
||||
<string name="warning_database_link_revoked">Přístup k souboru zrušenému správcem souborů</string>
|
||||
<string name="error_label_exists">Tento štítek již existuje.</string>
|
||||
<string name="error_label_exists">Tento štítek už existuje.</string>
|
||||
<string name="keyboard_search_share_summary">Při sdílení URL s KeePassDX filtrovat záznamy podle URL domény</string>
|
||||
<string name="keyboard_search_share_title">Prohledat sdílené info</string>
|
||||
<string name="autofill_block_restart">Aktivovat zamezení restartováním aplikace obsahující formulář.</string>
|
||||
@@ -473,7 +471,7 @@
|
||||
<string name="show_uuid_summary">Ukáže UUID propojené se záznamem nebo skupinou</string>
|
||||
<string name="show_uuid_title">Ukázat UUID</string>
|
||||
<string name="autofill_read_only_save">Uložení dat není povoleno, je-li databáze v režimu pouze pro čtení.</string>
|
||||
<string name="autofill_ask_to_save_data_summary">Zeptat se na uložení dat, jakmile byl formulář přezkoušen</string>
|
||||
<string name="autofill_ask_to_save_data_summary">Po dokončení vyplňování formuláře se zeptat na uložení dat</string>
|
||||
<string name="autofill_ask_to_save_data_title">Zeptat se před uložením</string>
|
||||
<string name="autofill_save_search_info_summary">Pokuste se uložit údaje hledání, když manuálně vybíráte položku</string>
|
||||
<string name="autofill_save_search_info_title">Uložit výsledky vyhledávání</string>
|
||||
@@ -505,7 +503,7 @@
|
||||
<string name="advanced_unlock_prompt_not_initialized">Nelze inicializovat pobídku pro rozšířené odemknutí.</string>
|
||||
<string name="advanced_unlock_scanning_error">Chyba při rozšířeném odemknutí: %1$s</string>
|
||||
<string name="advanced_unlock_not_recognized">Otisk pro rozšířené odemknutí nebyl rozpoznán</string>
|
||||
<string name="advanced_unlock_invalid_key">Nelze načíst klíč rozšířeného odemknutí. Prosím, smažte jej a opakujte proces rozpoznání odemknutí.</string>
|
||||
<string name="advanced_unlock_invalid_key">Nedaří se načíst klíč rozšířeného odemknutí. Prosím, smažte ho a opakujte proces rozpoznání odemknutí.</string>
|
||||
<string name="advanced_unlock_prompt_extract_credential_message">Načíst důvěrný údaj pomocí dat rozšířeného odemknutí</string>
|
||||
<string name="advanced_unlock_prompt_extract_credential_title">Otevřít pomocí rozšířeného odemykání</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Varování: Pokud použijete rozpoznání rozšířeného odemknutí, musíte si i nadále pamatovat hlavní heslo.</string>
|
||||
@@ -528,8 +526,8 @@
|
||||
<string name="autofill_inline_suggestions_title">Návrhy inline</string>
|
||||
<string name="autofill_inline_suggestions_summary">Pokusí ze zobrazit návrhy samovyplnění přímo z kompatibilní klávesnice</string>
|
||||
<string name="warning_database_revoked">Přístup k souboru odebrán správcem souborů, uzavřete databázi a nově ji otevřete z jejího adresáře.</string>
|
||||
<string name="menu_reload_database">Databázi nově načíst</string>
|
||||
<string name="warning_database_info_changed_options">Přepsat externí změny uložením databáze nebo databázi včetně posledních změn nově načíst.</string>
|
||||
<string name="menu_reload_database">Znovu načíst data</string>
|
||||
<string name="warning_database_info_changed_options">Sloučit data, přepsat externí změny uložením databáze nebo databázi znovu načíst včetně nejnovějších změn.</string>
|
||||
<string name="warning_database_info_changed">Informace obsažená ve Vašem databázovém souboru by změněna mimo aplikaci.</string>
|
||||
<string name="unit_gibibyte">GiB</string>
|
||||
<string name="unit_mebibyte">MiB</string>
|
||||
@@ -543,7 +541,7 @@
|
||||
<string name="style_brightness_title">Jas motivu</string>
|
||||
<string name="error_upload_file">Během nahrávání souboru došlo k chybě.</string>
|
||||
<string name="error_file_to_big">Soubor, který se pokoušíte nahrát, je příliš velký.</string>
|
||||
<string name="content_description_otp_information">Info o jednorázovém hesle</string>
|
||||
<string name="content_description_otp_information">Info o jednorázovém heslu</string>
|
||||
<string name="properties">Vlastnosti</string>
|
||||
<string name="error_export_app_properties">Během exportu vlastností aplikace došlo k chybě</string>
|
||||
<string name="success_export_app_properties">Vlastnosti aplikace byly exportovány</string>
|
||||
@@ -557,20 +555,20 @@
|
||||
<string name="error_start_database_action">Během akce v databázi došlo k chybě.</string>
|
||||
<string name="error_remove_file">Při odstraňování dat soboru došlo k chybě.</string>
|
||||
<string name="error_duplicate_file">Datový soubor již existuje.</string>
|
||||
<string name="error_move_group_here">Sem skupinu přesunout nemůžete.</string>
|
||||
<string name="error_move_group_here">Sem skupinu nelze přesunout.</string>
|
||||
<string name="error_word_reserved">Toto slovo je rezervováno a nelze je použít.</string>
|
||||
<string name="templates">Předlohy</string>
|
||||
<string name="templates_group_uuid_title">Skupina předloh</string>
|
||||
<string name="templates_group_enable_summary">K vyplnění polí záznamu použijte dynamické předlohy</string>
|
||||
<string name="templates_group_enable_title">Využití předloh</string>
|
||||
<string name="version">Verze</string>
|
||||
<string name="template">Předloha</string>
|
||||
<string name="template">Šablona</string>
|
||||
<string name="standard">Standard</string>
|
||||
<string name="membership">Členství</string>
|
||||
<string name="secure_note">Bezpečná poznámka</string>
|
||||
<string name="international_bank_account_number">IBAN</string>
|
||||
<string name="bank_identifier_code">SWIFT / BIC</string>
|
||||
<string name="bank_name">Jméno banky</string>
|
||||
<string name="bank_name">Název banky</string>
|
||||
<string name="bank">Banka</string>
|
||||
<string name="account">Účet</string>
|
||||
<string name="seed">Seed</string>
|
||||
@@ -589,9 +587,9 @@
|
||||
<string name="personal_identification_number">PIN</string>
|
||||
<string name="card_verification_value">CVV</string>
|
||||
<string name="number">Číslo</string>
|
||||
<string name="holder">Majitel</string>
|
||||
<string name="debit_credit_card">Debitní / Kreditní karta</string>
|
||||
<string name="template_group_name">Předlohy</string>
|
||||
<string name="holder">Držitel</string>
|
||||
<string name="debit_credit_card">Debetní / kreditní karta</string>
|
||||
<string name="template_group_name">Šablony</string>
|
||||
<string name="show_otp_token_summary">Ukáže OTP tokeny v seznamu záznamů</string>
|
||||
<string name="show_otp_token_title">Ukázat OTP token</string>
|
||||
<string name="menu_external_icon">Externí ikona</string>
|
||||
@@ -601,7 +599,7 @@
|
||||
<string name="hint_icon_name">Jméno symbolu</string>
|
||||
<string name="warning_exact_alarm">Nepovolili jste aplikaci použít přesný alarm. Výsledkem je, že funkce požadující časovač nebudou provedeny v přesný okamžik.</string>
|
||||
<string name="permission">Povolení</string>
|
||||
<string name="tags">Tagy</string>
|
||||
<string name="tags">Štítky</string>
|
||||
<string name="menu_merge_database">Sloučit databázi</string>
|
||||
<string name="warning_database_info_reloaded">Opětovné načtení databáze smaže všechny lokálně změněné data.</string>
|
||||
<string name="warning_keyfile_integrity">Hash souboru není garantován protože Android umožňuje změnu souboru v běhu. Změňte koncovku souboru na .bin pro opravu integrity.</string>
|
||||
@@ -612,7 +610,18 @@
|
||||
<string name="case_sensitive">Rozlišování velkých a malých písmen</string>
|
||||
<string name="current_group">Tato skupina</string>
|
||||
<string name="menu_merge_from">Sloučit z …</string>
|
||||
<string name="custom_data">Vlastní data</string>
|
||||
<string name="custom_data">Uživatelsky určené údaje</string>
|
||||
<string name="search_filters">Vyhledat filtry</string>
|
||||
<string name="menu_save_copy_to">Uložit kopii do …</string>
|
||||
<string name="content_description_entry_background_color">Zadejte barvu pozadí</string>
|
||||
<string name="content_description_entry_foreground_color">Zadejte barvu popředí</string>
|
||||
<string name="expired">Platnost skončila</string>
|
||||
<string name="show_entry_colors_summary">Zobrazí barvy popředí a pozadí v záznamu</string>
|
||||
<string name="show_entry_colors_title">Barvy záznamu</string>
|
||||
<string name="regex">Běžný výraz</string>
|
||||
<string name="enable_keep_screen_on_summary">Při zobrazování položky zabránit zhasnutí obrazovky</string>
|
||||
<string name="navigation_drawer_open">Otevření navigačního panelu</string>
|
||||
<string name="navigation_drawer_close">Zavření navigačního panelu</string>
|
||||
<string name="inherited">Převzít</string>
|
||||
<string name="auto_type_sequence">Posloupnost automatického vyplňování</string>
|
||||
</resources>
|
||||
@@ -429,8 +429,6 @@
|
||||
<string name="discard">Kassér</string>
|
||||
<string name="discard_changes">Kasser ændringer\?</string>
|
||||
<string name="validate">Valider</string>
|
||||
<string name="autofill_auto_search_summary">Foreslår automatisk søgeresultater fra webdomænet eller applikations-id</string>
|
||||
<string name="autofill_auto_search_title">Automatisk søgning</string>
|
||||
<string name="lock_database_show_button_summary">Viser låseknappen i brugergrænsefladen</string>
|
||||
<string name="lock_database_show_button_title">Vis låseknap</string>
|
||||
<string name="autofill_preference_title">Indstillinger for automatisk udfyldning</string>
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
<string name="autofill_sign_in_prompt">Mit KeePassDX anmelden</string>
|
||||
<string name="set_autofill_service_title">Standarddienst für automatisches Ausfüllen festlegen</string>
|
||||
<string name="autofill_explanation_summary">Automatisches Ausfüllen aktivieren, um Formulare schnell in anderen Apps auszufüllen</string>
|
||||
<string name="autofill_select_entry">Eintrag auswählen…</string>
|
||||
<string name="autofill_select_entry">Eintrag auswählen …</string>
|
||||
<string name="clipboard">Zwischenablage</string>
|
||||
<string name="biometric_delete_all_key_title">Verschlüsselungsschlüssel löschen</string>
|
||||
<string name="biometric_delete_all_key_summary">Alle Verschlüsselungsschlüssel löschen, die mit der modernen Entsperrerkennung zusammenhängen</string>
|
||||
@@ -252,7 +252,7 @@
|
||||
<string name="education_donation_summary">Mithelfen, um Stabilität und Sicherheit zu verbessern sowie weitere Funktionen zu ermöglichen.</string>
|
||||
<string name="html_text_ad_free">Anders als viele andere Passwortmanager ist dieser <strong>werbefrei</strong>, <strong>quelloffen</strong> und unter einer <strong>Copyleft-Lizenz</strong>. Es werden keine persönlichen Daten gesammelt, in welcher Form auch immer, unabhängig von der verwendeten Version (kostenlos oder Pro).</string>
|
||||
<string name="html_text_buy_pro">Mit dem Kauf der Pro-Version erhalten Sie Zugriff auf diesen <strong>visuellen Stil</strong> und unterstützen insbesondere <strong>die Umsetzung gemeinschaftlicher Projekte.</strong></string>
|
||||
<string name="html_text_feature_generosity">Dieser <strong>visuelle Stil</strong> wurde wegen Ihrer Großzügigkeit freigeschaltet.</string>
|
||||
<string name="html_text_feature_generosity">Dieser <strong>visuelle Stil</strong> ist dank Ihrer Großzügigkeit verfügbar.</string>
|
||||
<string name="html_text_donation">Um unsere Freiheit zu bewahren und immer aktiv zu bleiben, zählen wir auf Ihren <strong>Beitrag.</strong></string>
|
||||
<string name="html_text_dev_feature">Diese Funktion ist <strong>in Entwicklung</strong> und erfordert <strong>Ihren Beitrag</strong>, um bald verfügbar zu sein.</string>
|
||||
<string name="html_text_dev_feature_buy_pro">Durch den Kauf der <strong>Pro-Version</strong>,</string>
|
||||
@@ -317,7 +317,7 @@
|
||||
<string name="show_recent_files_summary">Speicherort zuletzt verwendeter Datenbanken anzeigen</string>
|
||||
<string name="hide_broken_locations_title">Defekte Datenbankverknüpfungen ausblenden</string>
|
||||
<string name="hide_broken_locations_summary">Nicht funktionierende Verknüpfungen in der Liste der zuletzt verwendeten Datenbanken ausblenden</string>
|
||||
<string name="do_not_kill_app">App nicht beenden …</string>
|
||||
<string name="do_not_kill_app">App nicht beenden …</string>
|
||||
<string name="lock_database_back_root_summary">Datenbank sperren, wenn auf dem Hauptbildschirm der Zurück-Button gedrückt wird</string>
|
||||
<string name="clear_clipboard_notification_title">Beim Schließen löschen</string>
|
||||
<string name="recycle_bin">Papierkorb</string>
|
||||
@@ -445,8 +445,6 @@
|
||||
<string name="discard">Verwerfen</string>
|
||||
<string name="discard_changes">Änderungen verwerfen\?</string>
|
||||
<string name="validate">Validieren</string>
|
||||
<string name="autofill_auto_search_summary">Suchergebnisse automatisch nach Web-Domain oder Anwendungs-ID vorschlagen</string>
|
||||
<string name="autofill_auto_search_title">Automatische Suche</string>
|
||||
<string name="autofill_manual_selection_title">Manuelle Auswahl</string>
|
||||
<string name="autofill_manual_selection_summary">Manuelle Auswahl des Datenbankeintrags ermöglichen</string>
|
||||
<string name="lock_database_show_button_summary">Zeigt die Sperrtaste in der Benutzeroberfläche an</string>
|
||||
@@ -455,7 +453,7 @@
|
||||
<string name="warning_database_link_revoked">Zugriff auf die Datei durch den Dateimanager widerrufen</string>
|
||||
<string name="error_label_exists">Diese Bezeichnung existiert bereits.</string>
|
||||
<string name="keyboard_search_share_summary">Beim Teilen einer URL mit KeePassDX die Einträge nach dieser URL filtern</string>
|
||||
<string name="keyboard_search_share_title">Gemeinsame Infos durchsuchen</string>
|
||||
<string name="keyboard_search_share_title">Gemeinsame Infos auswählen</string>
|
||||
<string name="autofill_block_restart">Starten Sie die Anwendung, die das Formular enthält, neu, um die Sperrung zu aktivieren.</string>
|
||||
<string name="autofill_block">Automatisches Ausfüllen sperren</string>
|
||||
<string name="autofill_web_domain_blocklist_summary">Liste der Domains, auf denen ein automatisches Ausfüllen verhindert wird</string>
|
||||
@@ -470,8 +468,8 @@
|
||||
<string name="keyboard_change">Tastatur wechseln</string>
|
||||
<string name="keyboard_previous_fill_in_title">Auto-Key-Aktion</string>
|
||||
<string name="keyboard_previous_database_credentials_title">Datenbank-Anmeldebildschirm</string>
|
||||
<string name="keyboard_previous_fill_in_summary">Nach Ausführung der automatischen Tastenaktion automatisch zur vorherigen Tastatur wechseln</string>
|
||||
<string name="keyboard_previous_database_credentials_summary">Automatisches Zurückschalten zur vorherigen Tastatur auf dem Datenbank-Anmeldebildschirm</string>
|
||||
<string name="keyboard_previous_fill_in_summary">Nach dem Ausführen der automatischen Tastenaktion automatisch zur vorherigen Tastatur wechseln</string>
|
||||
<string name="keyboard_previous_database_credentials_summary">Auf dem Datenbank-Anmeldebildschirm automatisch zur vorherigen Tastatur wechseln</string>
|
||||
<string name="education_add_attachment_summary">Laden Sie einen Anhang für Ihren Eintrag hoch, um wichtige externe Daten zu speichern.</string>
|
||||
<string name="content_description_credentials_information">Anmeldeinformationen</string>
|
||||
<string name="data">Daten</string>
|
||||
@@ -492,7 +490,7 @@
|
||||
<string name="show_uuid_title">UUID anzeigen</string>
|
||||
<string name="autofill_read_only_save">Das Speichern von Daten ist für eine als schreibgeschützt geöffnete Datenbank nicht zulässig.</string>
|
||||
<string name="autofill_close_database_title">Datenbank schließen</string>
|
||||
<string name="keyboard_previous_lock_summary">Nach dem Sperren der Datenbank automatisch zur vorherigen Tastatur zurückschalten</string>
|
||||
<string name="keyboard_previous_lock_summary">Nach dem Sperren der Datenbank automatisch zur vorherigen Tastatur wechseln</string>
|
||||
<string name="keyboard_previous_lock_title">Datenbank sperren</string>
|
||||
<string name="notification">Benachrichtigung</string>
|
||||
<string name="biometric_security_update_required">Biometrisches Sicherheitsupdate erforderlich.</string>
|
||||
@@ -507,10 +505,10 @@
|
||||
<string name="autofill_save_search_info_title">Suchinformationen speichern</string>
|
||||
<string name="autofill_close_database_summary">Datenbank nach Auswahl eines Eintrags zum automatischen Ausfüllen schließen</string>
|
||||
<string name="keyboard_save_search_info_summary">Nachdem eine URL mit KeePassDX geteilt und ein Eintrag gewählt wurde, wird versucht, sich diesen Eintrag für die weitere Nutzung zu merken</string>
|
||||
<string name="keyboard_save_search_info_title">Gemeinsam genutzte Informationen speichern</string>
|
||||
<string name="keyboard_save_search_info_title">Gemeinsame Informationen speichern</string>
|
||||
<string name="warning_empty_recycle_bin">Sollen alle ausgewählten Knoten wirklich aus dem Papierkorb gelöscht werden\?</string>
|
||||
<string name="error_field_name_already_exists">Der Feldname existiert bereits.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Warnung: Sie müssen sich immer noch an Ihr Masterpasswort erinnern, wenn Sie die moderne Entsperrerkennung verwenden.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Sie müssen sich immer noch an Ihre Anmeldedaten erinnern, wenn Sie die moderne Entsperrerkennung verwenden.</string>
|
||||
<string name="open_advanced_unlock_prompt_store_credential">Zum Speichern der Anmeldeinformationen Dialog zum modernen Entsperren öffnen</string>
|
||||
<string name="open_advanced_unlock_prompt_unlock_database">Dialog zum modernen Entsperren der Datenbank öffnen</string>
|
||||
<string name="menu_keystore_remove_key">Schlüssel für modernes Entsperren löschen</string>
|
||||
@@ -633,9 +631,33 @@
|
||||
<string name="search_filters">Suchfilter</string>
|
||||
<string name="current_group">Aktuelle Gruppe</string>
|
||||
<string name="case_sensitive">Groß-/Kleinschreibung beachten</string>
|
||||
<string name="menu_merge_from">Zusammenführen von …</string>
|
||||
<string name="menu_save_copy_to">Kopie speichern unter …</string>
|
||||
<string name="menu_merge_from">Zusammenführen von …</string>
|
||||
<string name="menu_save_copy_to">Kopie speichern unter …</string>
|
||||
<string name="content_description_nav_header">Navigationskopfzeile</string>
|
||||
<string name="navigation_drawer_close">Navigationsleiste schließen</string>
|
||||
<string name="navigation_drawer_open">Navigationsleiste öffnen</string>
|
||||
<string name="expired">Abgelaufen</string>
|
||||
<string name="warning_database_already_opened">Eine Datenbank ist bereits geöffnet, schließen Sie diese, um die neue zu öffnen</string>
|
||||
<string name="advanced_unlock_keystore_warning">Diese Funktion speichert verschlüsselte Anmeldedaten im sicheren Schlüsselspeicher Ihres Geräts.
|
||||
\n
|
||||
\nAbhängig von der nativen API-Implementierung des Betriebssystems ist sie möglicherweise nicht voll funktionsfähig.
|
||||
\nÜberprüfen Sie die Kompatibilität und Sicherheit des Schlüsselspeichers mit dem Hersteller Ihres Geräts und dem Ersteller des von Ihnen verwendeten ROMs.</string>
|
||||
<string name="content_description_passphrase_word_count">Passphrase-Wortanzahl</string>
|
||||
<string name="passphrase">Passphrase</string>
|
||||
<string name="colorize_password_title">Passwörter einfärben</string>
|
||||
<string name="colorize_password_summary">Passwortzeichen nach Typ einfärben</string>
|
||||
<string name="keyboard_previous_search_title">Suchbildschirm</string>
|
||||
<string name="keyboard_previous_search_summary">Auf dem Suchbildschirm automatisch zur vorherigen Tastatur wechseln</string>
|
||||
<string name="entropy">Entropie: %1$s Bit</string>
|
||||
<string name="entropy_high">Entropie: Hoch</string>
|
||||
<string name="entropy_calculate">Entropie: Berechnen …</string>
|
||||
<string name="at_least_one_char">Mindestens ein Zeichen von jedem</string>
|
||||
<string name="consider_chars_filter">Zeichen berücksichtigen</string>
|
||||
<string name="word_separator">Trennzeichen</string>
|
||||
<string name="ignore_chars_filter">Zeichen ignorieren</string>
|
||||
<string name="lower_case">Kleinbuchstaben</string>
|
||||
<string name="upper_case">GROẞBUCHSTABEN</string>
|
||||
<string name="character_count">Anzahl der Zeichen: %1$d</string>
|
||||
<string name="exclude_ambiguous_chars">Mehrdeutige Zeichen ausschließen</string>
|
||||
<string name="title_case">Groß-/Kleinschreibung des Titels</string>
|
||||
</resources>
|
||||
@@ -413,8 +413,6 @@
|
||||
<string name="contribution">Συνεισφορά</string>
|
||||
<string name="education_setup_OTP_summary">Ρύθμιση διαχείρισης μίας-χρήσης κωδικού πρόσβασης (HOTP / TOTP) για δημιουργία token που ζητήθηκε για έλεγχο ταυτότητας δύο παραγόντων (2FA).</string>
|
||||
<string name="education_setup_OTP_title">Ρύθμιση OTP</string>
|
||||
<string name="autofill_auto_search_summary">Προτείνετε αυτόματα αποτελέσματα αναζήτησης από τον τομέα ιστού ή το αναγνωριστικό εφαρμογής</string>
|
||||
<string name="autofill_auto_search_title">Αυτόματη αναζήτηση</string>
|
||||
<string name="lock_database_show_button_summary">Εμφανίζει το κουμπί κλειδώματος στη διεπαφή χρήστη</string>
|
||||
<string name="lock_database_show_button_title">Εμφάνιση κουμπιού κλειδώματος</string>
|
||||
<string name="autofill_preference_title">Ρυθμίσεις Αυτόματης Συμπλήρωσης</string>
|
||||
@@ -492,7 +490,7 @@
|
||||
<string name="error_registration_read_only">Η αποθήκευση ενός νέου αντικειμένου δεν επιτρέπεται σε μια βάση δεδομένων μόνο για ανάγνωση</string>
|
||||
<string name="error_field_name_already_exists">Το όνομα πεδίου υπάρχει ήδη.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_title">Προηγμένο ξεκλείδωμα αναγνώρισης</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Προειδοποίηση: Θα πρέπει να θυμάστε τον κύριο κωδικό πρόσβασης εάν χρησιμοποιείτε προηγμένο ξεκλείδωμα αναγνώρισης.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Πρέπει ακόμα να θυμάστε τα κύρια διαπιστευτήριά σας εάν χρησιμοποιείτε σύνθετη αναγνώριση ξεκλειδώματος.</string>
|
||||
<string name="advanced_unlock_prompt_extract_credential_title">Ανοίξτε τη βάση δεδομένων με προηγμένο ξεκλείδωμα αναγνώρισης</string>
|
||||
<string name="open_advanced_unlock_prompt_store_credential">Ανοίξτε τη προηγμένη προτροπή ξεκλειδώματος για αποθήκευση διαπιστευτηρίων</string>
|
||||
<string name="open_advanced_unlock_prompt_unlock_database">Ανοίξτε τη προηγμένη προτροπή ξεκλειδώματος για να ξεκλειδώσετε τη βάση δεδομένων</string>
|
||||
@@ -625,4 +623,28 @@
|
||||
<string name="current_group">Τρέχουσα ομάδα</string>
|
||||
<string name="regex">Κοινή έκφραση</string>
|
||||
<string name="menu_save_copy_to">Αποθήκευση αντιγράφου στο …</string>
|
||||
<string name="expired">Ληγμένο</string>
|
||||
<string name="warning_database_already_opened">Μια βάση δεδομένων είναι ήδη ανοιχτή, κλείστε την πρώτα για να ανοίξετε τη νέα</string>
|
||||
<string name="advanced_unlock_keystore_warning">Αυτή η δυνατότητα θα αποθηκεύσει κρυπτογραφημένα δεδομένα διαπιστευτηρίων στο ασφαλές KeyStore της συσκευής σας.
|
||||
\n
|
||||
\nΑνάλογα με την εγγενή υλοποίηση API του λειτουργικού συστήματος, ενδέχεται να μην είναι πλήρως λειτουργικό.
|
||||
\nΕλέγξτε τη συμβατότητα και την ασφάλεια του KeyStore με τον κατασκευαστή της συσκευής σας και τον δημιουργό της ROM που χρησιμοποιείτε.</string>
|
||||
<string name="passphrase">Συνθηματική φράση</string>
|
||||
<string name="colorize_password_summary">Χρωματίστε τους χαρακτήρες του κωδικού πρόσβασης ανά τύπο</string>
|
||||
<string name="keyboard_previous_search_title">Οθόνη αναζήτησης</string>
|
||||
<string name="keyboard_previous_search_summary">Αυτόματη εναλλαγή στο προηγούμενο πληκτρολόγιο στην οθόνη αναζήτησης</string>
|
||||
<string name="entropy">Εντροπία: %1$s bit</string>
|
||||
<string name="entropy_calculate">Εντροπία: Υπολογισμός…</string>
|
||||
<string name="title_case">Γράμματα Τίτλου</string>
|
||||
<string name="colorize_password_title">Χρωματίστε τους κωδικούς πρόσβασης</string>
|
||||
<string name="entropy_high">Εντροπία: Υψηλή</string>
|
||||
<string name="consider_chars_filter">Σκεφτείτε χαρακτήρες</string>
|
||||
<string name="exclude_ambiguous_chars">Εξαιρέστε διφορούμενους χαρακτήρες</string>
|
||||
<string name="content_description_passphrase_word_count">Πλήθος λέξεων συνθηματικής φράσης</string>
|
||||
<string name="at_least_one_char">Τουλάχιστον ένας χαρακτήρας από το καθένα</string>
|
||||
<string name="ignore_chars_filter">Αγνοήστε χαρακτήρες</string>
|
||||
<string name="lower_case">πεζά γράμματα</string>
|
||||
<string name="upper_case">ΚΕΦΑΛΑΙΑ ΓΡΑΜΜΑΤΑ</string>
|
||||
<string name="word_separator">Διαχωριστής</string>
|
||||
<string name="character_count">Αριθμός χαρακτήρων: %1$d</string>
|
||||
</resources>
|
||||
@@ -502,8 +502,6 @@
|
||||
<string name="autofill_ask_to_save_data_title">Pedir que se guarden los datos</string>
|
||||
<string name="autofill_save_search_info_summary">Intente guardar la información de la búsqueda cuando haga una selección de entrada manual</string>
|
||||
<string name="autofill_save_search_info_title">Guardar la información de la búsqueda</string>
|
||||
<string name="autofill_auto_search_summary">Sugerir automáticamente los resultados de la búsqueda desde el dominio web o el ID de la aplicación</string>
|
||||
<string name="autofill_auto_search_title">Búsqueda automática</string>
|
||||
<string name="autofill_close_database_summary">Cerrar la base de datos después de una selección de autocompletado</string>
|
||||
<string name="autofill_close_database_title">Cerrar la base de datos</string>
|
||||
<string name="enter">Entrar</string>
|
||||
@@ -627,4 +625,5 @@
|
||||
<string name="menu_merge_from">Fusionar desde …</string>
|
||||
<string name="menu_save_copy_to">Guardar una copia en …</string>
|
||||
<string name="inherited">Heredar</string>
|
||||
<string name="expired">Caducada</string>
|
||||
</resources>
|
||||
@@ -449,8 +449,6 @@
|
||||
<string name="discard">Abandonner</string>
|
||||
<string name="discard_changes">Abandonner les modifications \?</string>
|
||||
<string name="validate">Valider</string>
|
||||
<string name="autofill_auto_search_summary">Suggérer automatiquement des résultats de recherche à partir du domaine Web ou de l’identifiant de l’application</string>
|
||||
<string name="autofill_auto_search_title">Recherche automatique</string>
|
||||
<string name="lock_database_show_button_summary">Affiche le bouton de verrouillage dans l’interface utilisateur</string>
|
||||
<string name="lock_database_show_button_title">Afficher le bouton de verrouillage</string>
|
||||
<string name="autofill_preference_title">Paramètres de remplissage automatique</string>
|
||||
@@ -462,8 +460,8 @@
|
||||
<string name="autofill_web_domain_blocklist_title">Liste de blocage de domaine Web</string>
|
||||
<string name="autofill_application_id_blocklist_summary">Liste de blocage qui empêche le remplissage automatique des applications</string>
|
||||
<string name="autofill_application_id_blocklist_title">Liste de blocage d’application</string>
|
||||
<string name="keyboard_search_share_summary">Lorsqu\'une adresse web est partagée avec KeePassDX, filtrer automatiquement les entrées contenant le domaine</string>
|
||||
<string name="keyboard_search_share_title">Rechercher les informations partagées</string>
|
||||
<string name="keyboard_search_share_summary">Lorsque des infos sont partagées avec KeePassDX, filtre les entrées en utilisant ces infos pour alimenter le Magiclavier</string>
|
||||
<string name="keyboard_search_share_title">Sélectionner les infos partagées</string>
|
||||
<string name="filter">Filtre</string>
|
||||
<string name="subdomain_search_summary">Recherche des domaines Web avec des contraintes de sous-domaines</string>
|
||||
<string name="subdomain_search_title">Recherche de sous-domaine</string>
|
||||
@@ -490,13 +488,13 @@
|
||||
<string name="autofill_read_only_save">L’enregistrement des données n’est pas autorisé pour une base de données ouverte en lecture seule.</string>
|
||||
<string name="autofill_ask_to_save_data_summary">Demande de sauvegarde des données à la fin du remplissage d\'un formulaire</string>
|
||||
<string name="autofill_ask_to_save_data_title">Demander à enregistrer des données</string>
|
||||
<string name="autofill_save_search_info_summary">Essayer d’enregistrer les informations de recherche lors de la sélection manuelle d’une entrée</string>
|
||||
<string name="autofill_save_search_info_summary">Essaye d’enregistrer les informations de recherche lors de la sélection manuelle d’une entrée</string>
|
||||
<string name="autofill_save_search_info_title">Enregistrer les infos de recherche</string>
|
||||
<string name="autofill_close_database_summary">Ferme la base de données après une sélection de remplissage automatique</string>
|
||||
<string name="autofill_close_database_title">Fermer la base de données</string>
|
||||
<string name="keyboard_previous_lock_summary">Revient automatiquement au clavier précédent après le verrouillage de la base de données</string>
|
||||
<string name="keyboard_previous_lock_title">Verrouiller la base de données</string>
|
||||
<string name="keyboard_save_search_info_summary">Essayer d’enregistrer l\'association entre une adresse web partagée à KeePassDX et l\'entrée sélectionnée manuellement</string>
|
||||
<string name="keyboard_save_search_info_summary">Après avoir partagé des infos avec KeePassDX, lorsqu\'une entrée est sélectionnée, essaye d\'enregistrer les infos dans l\'entrée pour faciliter les utilisations futures</string>
|
||||
<string name="keyboard_save_search_info_title">Enregistrer les infos partagées</string>
|
||||
<string name="notification">Notification</string>
|
||||
<string name="biometric_security_update_required">Mise à jour de sécurité biométrique requise.</string>
|
||||
@@ -517,7 +515,7 @@
|
||||
<string name="advanced_unlock_invalid_key">Impossible de lire la clé de déverrouillage avancé. Veuillez la supprimer et répéter la procédure de reconnaissance de déverrouillage.</string>
|
||||
<string name="advanced_unlock_prompt_extract_credential_message">Extraire les identifiants de la base de données avec des données de déverrouillage avancées</string>
|
||||
<string name="advanced_unlock_prompt_extract_credential_title">Ouvrir la base de données avec la reconnaissance de déverrouillage avancée</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Attention : vous devez toujours vous souvenir de votre mot de passe principal si vous utilisez le déverrouillage avancé.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Vous devez toujours vous souvenir de votre identifiant principal si vous utilisez le déverrouillage avancé.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_title">Reconnaissance de déverrouillage avancée</string>
|
||||
<string name="open_advanced_unlock_prompt_store_credential">Ouvrez l\'invite de déverrouillage avancé pour stocker les informations d\'identification</string>
|
||||
<string name="open_advanced_unlock_prompt_unlock_database">Ouvrez l\'invite de déverrouillage avancé pour déverrouiller la base de données</string>
|
||||
@@ -636,4 +634,27 @@
|
||||
<string name="custom_data">Données personnalisées</string>
|
||||
<string name="case_sensitive">Sensible à la casse</string>
|
||||
<string name="expired">Expiré</string>
|
||||
<string name="warning_database_already_opened">Une base de données est déjà ouverte, fermez-la d\'abord pour ouvrir la nouvelle base</string>
|
||||
<string name="advanced_unlock_keystore_warning">Cette fonction permet de stocker des données d\'identification chiffrées dans le KeyStore sécurisé de votre appareil.
|
||||
\n
|
||||
\nSelon l\'implémentation de l\'API native du système d\'exploitation, il se peut qu\'elle ne soit pas entièrement fonctionnelle.
|
||||
\nVérifiez la compatibilité et la sécurité du KeyStore auprès du fabricant de votre appareil et du créateur de la ROM que vous utilisez.</string>
|
||||
<string name="content_description_passphrase_word_count">Nombre de mots</string>
|
||||
<string name="keyboard_previous_search_title">Écran de recherche</string>
|
||||
<string name="entropy">Entropie : %1$s bit</string>
|
||||
<string name="entropy_high">Entropie : Haute</string>
|
||||
<string name="at_least_one_char">Au moins un caractère de chaque</string>
|
||||
<string name="exclude_ambiguous_chars">Exclure les caractères ambigus</string>
|
||||
<string name="ignore_chars_filter">Ignorer les caractères</string>
|
||||
<string name="lower_case">minuscule</string>
|
||||
<string name="upper_case">MAJUSCULE</string>
|
||||
<string name="title_case">1ère Lettre Majuscule</string>
|
||||
<string name="keyboard_previous_search_summary">Retour automatique au clavier précédent sur l\'écran de recherche</string>
|
||||
<string name="colorize_password_title">Coloriser les mots de passe</string>
|
||||
<string name="entropy_calculate">Entropie : Calcule…</string>
|
||||
<string name="passphrase">Phrase de passe</string>
|
||||
<string name="colorize_password_summary">Coloriser les caractères du mot de passe par type</string>
|
||||
<string name="consider_chars_filter">Considérer les caractères</string>
|
||||
<string name="word_separator">Séparateur</string>
|
||||
<string name="character_count">Nombre de caractères : %1$d</string>
|
||||
</resources>
|
||||
@@ -16,37 +16,37 @@
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<resources>
|
||||
<string name="feedback">प्प्रतिपुष्टिा</string>
|
||||
--><resources>
|
||||
<string name="feedback">प्रतिक्रिया</string>
|
||||
<string name="homepage">होमपेज</string>
|
||||
<string name="about_description">एंड्रॉयड पर आधारित KeePass पासवर्ड मैनेजर</string>
|
||||
<string name="accept">स्वीकार</string>
|
||||
<string name="accept">स्वीकार करें</string>
|
||||
<string name="add_entry">रिकॉर्ड जोड़ें</string>
|
||||
<string name="edit_entry">रिकॉर्ड संपादित करें</string>
|
||||
<string name="add_group">समूह जोड़ें</string>
|
||||
<string name="encryption">एन्क्रिप्शन</string>
|
||||
<string name="encryption_algorithm">एन्क्रिप्शन एल्गोरिथम</string>
|
||||
<string name="key_derivation_function">कुंजी उत्पत्ति कृत्य</string>
|
||||
<string name="app_timeout">ऐप का समय समाप्त</string>
|
||||
<string name="app_timeout_summary">ऐप लॉक होने से पहले निष्क्रियता</string>
|
||||
<string name="app_timeout">समय समाप्त</string>
|
||||
<string name="app_timeout_summary">डाटाबेस के बन्द होने से पहले तक का समय</string>
|
||||
<string name="application">ऐप</string>
|
||||
<string name="brackets">कोष्ठक</string>
|
||||
<string name="extended_ASCII">विस्तारित ASCII</string>
|
||||
<string name="allow">अनुमति दें</string>
|
||||
<string name="clipboard_cleared">क्लिपबोर्ड साफ कर दिया</string>
|
||||
<string name="clipboard_cleared">क्लिपबोर्ड साफ़ किया गया</string>
|
||||
<string name="clipboard_error_title">क्लिपबोर्ड एरर</string>
|
||||
<string name="clipboard_error">सैमसंग के कुछ एंड्रॉइड फोन क्लिपबोर्ड का उपयोग नहीं करने देंगे।</string>
|
||||
<string name="clipboard_error">कुछ डिवाइस ऐप्स को क्लिपबोर्ड का प्रयोग करने की अनुमति नहीं देंगे।</string>
|
||||
<string name="clipboard_error_clear">क्लिपबोर्ड को साफ़ नहीं किया जा सका</string>
|
||||
<string name="clipboard_timeout">क्लिपबोर्ड टाइमआउट</string>
|
||||
<string name="clipboard_timeout_summary">क्लिपबोर्ड में भंडारण की अवधि</string>
|
||||
<string name="clipboard_timeout_summary">क्लिपबोर्ड में भंडारण की अवधि (यदि आपके डिवाइस द्वारा समर्थित है)</string>
|
||||
<string name="select_to_copy">क्लिपबोर्ड पर %1$s को कॉपी करने के लिए चयन करें</string>
|
||||
<string name="retrieving_db_key">डेटाबेस कुंजी पुनर्प्राप्त कर रहा है…</string>
|
||||
<string name="retrieving_db_key">डेटाबेस कुंजी पुनर्प्राप्त की जा रही है…</string>
|
||||
<string name="database">डेटाबेस</string>
|
||||
<string name="decrypting_db">डेटाबेस सामग्री डिक्रिप्टिंग…</string>
|
||||
<string name="decrypting_db">डेटाबेस सामग्री डिक्रिप्ट कर रहे है…</string>
|
||||
<string name="default_checkbox">डिफ़ॉल्ट डेटाबेस के रूप में उपयोग करें</string>
|
||||
<string name="digits">अंक</string>
|
||||
<string name="html_about_licence">KeePassDX © %1$d Kunzisoft बिल्कुल कोई वारंटी के साथ आता है । यह लाइब्रे सॉफ्टवेयर है, और जीपीएल संस्करण 3 या बाद की शर्तों के तहत इसे पुनर्वितरित करने के लिए आपका स्वागत है।</string>
|
||||
<string name="html_about_licence">KeePassDX © %1$d Kunzisoft <strong>ओपन सोर्स</strong> है और <strong>बिना किसी विज्ञापन</strong> के उपलब्ध है।
|
||||
\nइसे <strong>GPLv3</strong> लाइसेंस के अन्तर्गत बिना किसी वारन्टि के प्रदान किया गया है।</string>
|
||||
<string name="entry_accessed">एक्सेस किया गया</string>
|
||||
<string name="entry_cancel">रद्द करें</string>
|
||||
<string name="entry_notes">टिप्पणियाँ</string>
|
||||
@@ -63,13 +63,13 @@
|
||||
<string name="entry_user_name">उपयोगकर्ता का नाम</string>
|
||||
<string name="error_can_not_handle_uri">KeePassDX में इस URI को संभाल नहीं सका।</string>
|
||||
<string name="error_file_not_create">फाइल नहीं बना सका</string>
|
||||
<string name="error_invalid_db">डाटाबेस नहीं पढ़ सका।</string>
|
||||
<string name="error_invalid_db">डेटाबेस को पढ़ा नहीं जा सका।</string>
|
||||
<string name="error_invalid_path">सुनिश्चित करें कि रास्ता सही है।</string>
|
||||
<string name="error_no_name">एक नाम दर्ज करें।</string>
|
||||
<string name="error_nokeyfile">कीफाइल का चयन करें।</string>
|
||||
<string name="error_out_of_memory">आपके पूरे डेटाबेस को लोड करने के लिए कोई मेमोरी नहीं।</string>
|
||||
<string name="error_nokeyfile">कीफ़ाइल का चयन करें।</string>
|
||||
<string name="error_out_of_memory">आपके पूरे डेटाबेस को लोड करने के लिए मेमोरी नहीं है।</string>
|
||||
<string name="error_load_database">आपके डेटाबेस को लोड नहीं किया जा सका।</string>
|
||||
<string name="error_load_database_KDF_memory">कुंजी लोड नहीं कर सका। केडीएफ \"मेमोरी उपयोग\" को कम करने का प्रयास करें।</string>
|
||||
<string name="error_load_database_KDF_memory">कुंजी लोड नहीं कर सके। KDF \"मेमोरी उपयोग\" को कम करने का प्रयास करें।</string>
|
||||
<string name="error_pass_gen_type">कम से कम एक पासवर्ड जनरेशन प्रकार का चयन करना होगा।</string>
|
||||
<string name="error_pass_match">पासवर्ड मेल नहीं खाते हैं।</string>
|
||||
<string name="error_rounds_too_large">\"परिवर्तन राउंड\" बहुत अधिक है। 2147483648 पर सेट हो रहा है।</string>
|
||||
@@ -85,15 +85,15 @@
|
||||
<string name="content_description_repeat_toggle_password_visibility">पासवर्ड दृश्यता टॉगल दोहराएं</string>
|
||||
<string name="content_description_entry_icon">प्रवेश आइकन</string>
|
||||
<string name="entry_password_generator">पासवर्ड जनरेटर</string>
|
||||
<string name="content_description_password_length">पासवर्ड की लंबाई</string>
|
||||
<string name="content_description_password_length">पासवर्ड की लम्बाई</string>
|
||||
<string name="entry_add_field">फ़ील्ड जोड़ें</string>
|
||||
<string name="content_description_remove_field">फ़ील्ड निकालें</string>
|
||||
<string name="content_description_remove_field">फ़ील्ड हटाये</string>
|
||||
<string name="entry_UUID">UUID</string>
|
||||
<string name="file_manager_install_description">"डेटाबेस फाइल को बनाने खोलने और सेव करने के लिए आपके फोन में फाइल मैनेजर ऐप होना चाहिए जोकि दिए गए एक्शन को सपोर्ट कर सके ACTION_CREATE_DOCUMENT और ACTION_OPEN_DOCUMENT"</string>
|
||||
<string name="file_manager_install_description">डेटाबेस फाइल को बनाने, खोलने और सेव करने के लिए आपके फोन में फाइल मैनेजर ऐप होना चाहिए जो ACTION_CREATE_DOCUMENT और ACTION_OPEN_DOCUMENT कार्यों को करने मे सक्षम हो।</string>
|
||||
<string name="content_description_background">पृष्ठभूमि</string>
|
||||
<string name="content_description_update_from_list">अपडेट करें</string>
|
||||
<string name="content_description_remove_from_list">निकालें</string>
|
||||
<string name="content_description_keyboard_close_fields">बंद क्षेत्र</string>
|
||||
<string name="content_description_remove_from_list">हटाएँ</string>
|
||||
<string name="content_description_keyboard_close_fields">फ़ील्ड बंद करें</string>
|
||||
<string name="master_key">मास्टर कुंजी</string>
|
||||
<string name="security">सुरक्षा</string>
|
||||
<string name="entry_history">इतिहास</string>
|
||||
@@ -105,6 +105,65 @@
|
||||
<string name="otp_digits">अंक</string>
|
||||
<string name="otp_algorithm">एल्गोरिथ्म</string>
|
||||
<string name="entry_otp">ओटीप</string>
|
||||
<string name="error_invalid_OTP">अमान्य ओटीपी गुप्त।</string>
|
||||
<string name="error_invalid_OTP">अमान्य ओटीपी रहस्य।</string>
|
||||
<string name="error_disallow_no_credentials">कम से कम एक क्रेडेंशियल सेट किया जाना चाहिए।</string>
|
||||
<string name="email">ईमेल</string>
|
||||
<string name="email_address">ईमेल पता</string>
|
||||
<string name="ssid">SSID</string>
|
||||
<string name="error_otp_digits">टोकन में %1$d से %2$d अंक होने चाहिए।</string>
|
||||
<string name="style_choose_summary">ऐप में इस्तेमाल की गई थीम</string>
|
||||
<string name="style_brightness_summary">हल्की या गहरी थीम चुनें</string>
|
||||
<string name="error_move_group_here">आप यहा किसी समूह को स्थानांतरित नहीं कर सकते है।</string>
|
||||
<string name="content_description_database_color">डाटाबेस का रंग</string>
|
||||
<string name="error_otp_secret_key">गुप्त कुंजी Base32 स्वरूप में होनी आवश्यक है।</string>
|
||||
<string name="wireless">वाई-फाई</string>
|
||||
<string name="hide_expired_entries_title">समाप्त हो चुकी प्रविष्टियो को छुपाएं</string>
|
||||
<string name="icon_pack_choose_title">आइकन पैक</string>
|
||||
<string name="contribution">योगदान</string>
|
||||
<string name="error_copy_entry_here">आप यहां प्रविष्टि की प्रतिलिपि नहीं बना सकते।</string>
|
||||
<string name="error_copy_group_here">आप यहाँ समूह की प्रतिलिपि नहीं कर सकते हैं।</string>
|
||||
<string name="error_create_database_file">इस पासवर्ड और कीफ़ाइल के साथ डेटाबेस बनाने में असमर्थ।</string>
|
||||
<string name="template_group_name">प्रारूप</string>
|
||||
<string name="discard_changes">परिवर्तनों को निरस्त करें\?</string>
|
||||
<string name="content_description_otp_information">वन-टाइम पासवर्ड की जानकारी</string>
|
||||
<string name="personal_identification_number">PIN</string>
|
||||
<string name="name">नाम</string>
|
||||
<string name="error_word_reserved">यह शब्द आरक्षित है और इसका उपयोग नहीं किया जा सकता है।</string>
|
||||
<string name="allow_copy_password_summary">प्रविष्टि पासवर्ड और संरक्षित फ़ील्ड को क्लिपबोर्ड पर कॉपी करने की अनुमति दें</string>
|
||||
<string name="card_verification_value">CVV</string>
|
||||
<string name="error_create_database">डेटाबेस फ़ाइल बनाने में असमर्थ।</string>
|
||||
<string name="error_otp_period">अवधि %1$d और %2$d सेकैंड के बीच होनी चाहिए।</string>
|
||||
<string name="public_key">सार्वजनिक कुंजी</string>
|
||||
<string name="current_group">वर्तमान समूह</string>
|
||||
<string name="error_move_entry_here">आप यहा किसी प्रविष्टि को स्थानांतरित नहीं कर सकते है।</string>
|
||||
<string name="error_save_database">डेटाबेस को सेव नहीं किया जा सका।</string>
|
||||
<string name="error_otp_counter">काउंटर %1$d और %2$d के बीच में होना चाहिए।</string>
|
||||
<string name="unit_gibibyte">GiB</string>
|
||||
<string name="hide_expired_entries_summary">समाप्त हो चुकी प्रविष्टियों को नहीं दिखाया गया है</string>
|
||||
<string name="unit_kibibyte">KiB</string>
|
||||
<string name="unit_mebibyte">MiB</string>
|
||||
<string name="style_choose_title">ऐप थीम</string>
|
||||
<string name="icon_pack_choose_summary">ऐप में इस्तेमाल किया गया आइकन पैक</string>
|
||||
<string name="content_description_credentials_information">क्रेडेंशियल्स कि जानकारी</string>
|
||||
<string name="contact">संपर्क करें</string>
|
||||
<string name="validate">सत्यापित करें</string>
|
||||
<string name="discard">निरस्त करें</string>
|
||||
<string name="entry_add_attachment">अटेचमेंट लगाये</string>
|
||||
<string name="debit_credit_card">डेबिट / क्रेडिट कार्ड</string>
|
||||
<string name="holder">धारक</string>
|
||||
<string name="bank">बैंक</string>
|
||||
<string name="bank_name">बैंक का नाम</string>
|
||||
<string name="bank_identifier_code">SWIFT / BIC</string>
|
||||
<string name="international_bank_account_number">IBAN</string>
|
||||
<string name="about">के बारे मे</string>
|
||||
<string name="download_canceled">रद्द!</string>
|
||||
<string name="unit_byte">B</string>
|
||||
<string name="show_entry_colors_title">प्रविष्टि के रंग</string>
|
||||
<string name="error_rebuild_list">सूची का ठीक से पुनर्निर्माण करने में असमर्थ।</string>
|
||||
<string name="error_file_to_big">आप जिस फ़ाइल को अपलोड करने का प्रयास कर रहे हैं वह बहुत बड़ी है।</string>
|
||||
<string name="error_remove_file">फ़ाइल डेटा को हटाते समय त्रुटि हुई।</string>
|
||||
<string name="error_field_name_already_exists">फ़ील्ड का नाम पहले से मौजूद है।</string>
|
||||
<string name="error_database_uri_null">डेटाबेस URI को पुनःप्राप्त नहीं किया जा सका।</string>
|
||||
<string name="error_upload_file">फ़ाइल डेटा को अपलोड करते समय एक त्रुटि हुई।</string>
|
||||
<string name="error_duplicate_file">फ़ाइल डेटा पहले से उपलब्ध है।</string>
|
||||
</resources>
|
||||
@@ -17,7 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
--><resources>
|
||||
<string name="about_description">Android implementacija KeePass upravitelja lozinki</string>
|
||||
<string name="about_description">Android implementacija KeePass upravljača lozinki</string>
|
||||
<string name="accept">Prihvati</string>
|
||||
<string name="add_entry">Dodaj unos</string>
|
||||
<string name="edit_entry">Uredi unos</string>
|
||||
@@ -31,13 +31,13 @@
|
||||
<string name="application">Aplikacija</string>
|
||||
<string name="brackets">Zagrade</string>
|
||||
<string name="extended_ASCII">Prošireni ASCII</string>
|
||||
<string name="file_manager_install_description">Za stvaranje, otvaranje i spremanje datoteka baze podataka potreban je upravitelj datoteka koji prihvaća zahtjeve ACTION_CREATE_DOCUMENT i ACTION_OPEN_DOCUMENT.</string>
|
||||
<string name="file_manager_install_description">Za stvaranje, otvaranje i spremanje datoteka baze podataka potreban je upravljač datoteka koji prihvaća zahtjeve ACTION_CREATE_DOCUMENT i ACTION_OPEN_DOCUMENT.</string>
|
||||
<string name="allow">Dozvoli</string>
|
||||
<string name="clipboard_cleared">Međuspremnik ispražnjen</string>
|
||||
<string name="clipboard_error_title">Greška međuspremnika</string>
|
||||
<string name="clipboard_error">Neki uređaji neće dopustiti aplikacijama korištenje međuspremnika.</string>
|
||||
<string name="clipboard_error_clear">Nije moguće isprazniti međuspremnik</string>
|
||||
<string name="clipboard_timeout_summary">Trajanje pohrane u međuspremniku (ako uređaj to podržava)</string>
|
||||
<string name="clipboard_timeout_summary">Trajanje spremišta u međuspremniku (ako uređaj to podržava)</string>
|
||||
<string name="content_description_background">Pozadina</string>
|
||||
<string name="content_description_open_file">Otvori datoteku</string>
|
||||
<string name="content_description_add_node">Dodaj čvor</string>
|
||||
@@ -85,7 +85,7 @@
|
||||
<string name="entry_url">URL</string>
|
||||
<string name="entry_user_name">Korisničko ime</string>
|
||||
<string name="error_nokeyfile">Odaberi datoteku ključa.</string>
|
||||
<string name="error_pass_gen_type">Barem jedan tip generiranja lozinke mora biti odabran.</string>
|
||||
<string name="error_pass_gen_type">Barem jedna vrsta generiranja lozinke mora biti odabrana.</string>
|
||||
<string name="error_pass_match">Lozinke se ne poklapaju.</string>
|
||||
<string name="error_wrong_length">Upiši pozitivan cijeli broj u polje „Duljina”.</string>
|
||||
<string name="error_otp_secret_key">Tajni ključ mora biti u Base32 formatu.</string>
|
||||
@@ -93,7 +93,7 @@
|
||||
<string name="error_otp_digits">Token mora sadržavati %1$d do %2$d znamenki.</string>
|
||||
<string name="field_name">Ime polja</string>
|
||||
<string name="field_value">Vrijednost polja</string>
|
||||
<string name="file_browser">Upravitelj datoteka</string>
|
||||
<string name="file_browser">Upravljač datoteka</string>
|
||||
<string name="generate_password">Generiraj lozinku</string>
|
||||
<string name="hint_conf_pass">Potvrdi lozinku</string>
|
||||
<string name="hint_generated_password">Generirana lozinka</string>
|
||||
@@ -258,7 +258,7 @@
|
||||
<string name="menu_file_selection_read_only">Zaštićeno od pisanja</string>
|
||||
<string name="progress_title">Rad u tijeku …</string>
|
||||
<string name="read_only">Zaštićeno od pisanja</string>
|
||||
<string name="read_only_warning">Ovisno o upravitelju datoteka, KeePassDX možda neće moći pisati u tvoje spremište.</string>
|
||||
<string name="read_only_warning">Ovisno o upravljaču datoteka, KeePassDX možda neće moći pisati u tvoje spremište.</string>
|
||||
<string name="contains_duplicate_uuid_procedure">Riješiti problem generiranjem novih UUID-ova za duplikate\?</string>
|
||||
<string name="root">Korijen</string>
|
||||
<string name="memory_usage_explanation">Količina memorije koju će koristiti funkcija za generiranje ključeva.</string>
|
||||
@@ -336,7 +336,6 @@
|
||||
<string name="homepage">Početna stranica</string>
|
||||
<string name="remember_keyfile_locations_title">Zapamti mjesto datoteke ključa</string>
|
||||
<string name="unavailable_feature_version">Uređaj koristi Android verziju %1$s, ali potrebna je verzija %2$s ili novija.</string>
|
||||
<string name="autofill_auto_search_summary">Automatski predloži rezultate pretrage od web domene ili ID-a aplikacije</string>
|
||||
<string name="hide_broken_locations_summary">Sakrij pokvarene poveznice u popisu nedavnih baza podataka</string>
|
||||
<string name="html_text_dev_feature">Ova se funkcija nalazi <strong>u razvoju</strong> i treba tvoj <strong>doprinos</strong> kako bi uskoro bila dostupna.</string>
|
||||
<string name="education_generate_password_title">Stvori sigurnu lozinku</string>
|
||||
@@ -346,7 +345,6 @@
|
||||
<string name="autofill_application_id_blocklist_title">Popis blokiranja aplikacija</string>
|
||||
<string name="validate">Provjeri valjanost</string>
|
||||
<string name="education_entry_edit_title">Uredi unos</string>
|
||||
<string name="autofill_auto_search_title">Automatska pretraga</string>
|
||||
<string name="html_text_dev_feature_work_hard">Trudimo se brzo izdati ovu funkciju.</string>
|
||||
<string name="html_text_dev_feature_encourage">potičeš programere da stvore <strong>nove funkcije</strong> i <strong>isprave greške</strong> na osnovi tvojih napomena.</string>
|
||||
<string name="keyboard_auto_go_action_summary">Radnja tipke „Idi” nakon pritiskanja tipke „Polje”</string>
|
||||
@@ -372,7 +370,7 @@
|
||||
<string name="html_text_dev_feature_upgrade">Redovito aktualiziraj aplikaciju instaliranjem najnovijih verzija.</string>
|
||||
<string name="autofill_block_restart">Za aktiviranje blokiranja, ponovo pokreni aplikaciju koja sadrži obrazac.</string>
|
||||
<string name="education_sort_summary">Odaberi način razvrstavanja unosa i grupa.</string>
|
||||
<string name="warning_database_link_revoked">Pristup datoteci koju je opozvao upravitelj datoteka</string>
|
||||
<string name="warning_database_link_revoked">Pristup datoteci opozvan od upravljača datoteka</string>
|
||||
<string name="download_attachment">Preuzmi %1$s</string>
|
||||
<string name="keyboard_entry_timeout_title">Istek vremena</string>
|
||||
<string name="auto_focus_search_summary">Pokreni pretragu prilikom otvaranja baze podataka</string>
|
||||
@@ -387,7 +385,7 @@
|
||||
<string name="education_entry_new_field_title">Dodaj prilagođena polja</string>
|
||||
<string name="education_lock_summary">Zaključaj bazu podataka brzo, aplikaciju možeš postaviti tako da bazu nakon nekog vremena zaključa i kad se ekran isključi.</string>
|
||||
<string name="show_recent_files_summary">Prikaži mjesto nedavnih baza podataka</string>
|
||||
<string name="education_advanced_unlock_summary">Za brzo otključavanje baze podataka, poveži lozinku sa skeniranom biometrijom.</string>
|
||||
<string name="education_advanced_unlock_summary">Za brzo otključavanje baze podataka, poveži lozinku sa skeniranom biometrijom ili podacima za prijavu uređaja.</string>
|
||||
<string name="html_text_donation">Kako bismo zadržali našu slobodu i uvijek bili aktivni, računamo na tvoj<strong>doprinos.</strong></string>
|
||||
<string name="kdf_explanation">Za stvaranje ključa za algoritam šifriranja, glavni ključ se transformira pomoću funkcije za generiranje ključeva koja sadrži slučajnu komponentu.</string>
|
||||
<string name="lock_database_back_root_summary">Zaključaj bazu podataka kad korisnik pritisne gumb za natrag na glavnom ekranu</string>
|
||||
@@ -471,7 +469,7 @@
|
||||
<string name="autofill_read_only_save">Spremanje podataka nije dopušteno za bazu podataka koja je otvorena u zaštićenom stanju.</string>
|
||||
<string name="show_uuid_summary">Prikazuje UUID povezan s unosom ili grupom</string>
|
||||
<string name="show_uuid_title">Prikaži UUID</string>
|
||||
<string name="autofill_ask_to_save_data_summary">Zatraži spremanje podataka kad se obrazac provjeri</string>
|
||||
<string name="autofill_ask_to_save_data_summary">Zatraži spremanje podataka kad se obrazac ispuni</string>
|
||||
<string name="autofill_ask_to_save_data_title">Zatraži spremanje podataka</string>
|
||||
<string name="autofill_save_search_info_title">Spremi podatke pretrage</string>
|
||||
<string name="autofill_close_database_summary">Zatvori bazu podataka nakon odabira automatskog ispunjavanja</string>
|
||||
@@ -488,9 +486,9 @@
|
||||
<string name="search_mode">Modus pretrage</string>
|
||||
<string name="error_field_name_already_exists">Ime polja već postoji.</string>
|
||||
<string name="advanced_unlock_delete_all_key_warning">Izbrisati sve ključeve šifriranja povezane s naprednim prepoznavanjem otključavanja\?</string>
|
||||
<string name="credential_before_click_advanced_unlock_button">Upiši lozinku i zatim pritisni ovaj gumb.</string>
|
||||
<string name="credential_before_click_advanced_unlock_button">Upiši lozinku, zatim pritisni ovaj gumb.</string>
|
||||
<string name="advanced_unlock_prompt_extract_credential_title">Otvori bazu podataka pomoću naprednog prepoznavanja otključavanja</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Upozorenje: Ako koristiš prepoznavanje naprednog otključavanja morat ćeš i dalje znati glavnu lozinku.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Ako koristiš prepoznavanje naprednog otključavanja morat ćeš i dalje znati glavnu lozinku.</string>
|
||||
<string name="menu_keystore_remove_key">Izbriši ključ naprednog otključavanja</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_title">Napredno prepoznavanje otključavanja</string>
|
||||
<string name="advanced_unlock_prompt_not_initialized">Nije moguće pokrenuti prozor naprednog otključavanja.</string>
|
||||
@@ -521,9 +519,9 @@
|
||||
<string name="autofill_inline_suggestions_title">Umetnuti prijedlozi</string>
|
||||
<string name="autofill_inline_suggestions_keyboard">Prijedlozi za automatsko popunjavanje su dodani.</string>
|
||||
<string name="autofill_inline_suggestions_summary">Pokušaj prikazivanja prijedloga za automatsko popunjavanje izravno s kompatibilne tipkovnice</string>
|
||||
<string name="warning_database_revoked">Pristup datoteci koju je opozvao upravljač datoteka. Zatvori bazu podataka i ponovo je otvori s njezinog mjesta.</string>
|
||||
<string name="warning_database_revoked">Pristup datoteci opozvan od upravljača datoteka. Zatvori bazu podataka i ponovo je otvori s njezinog mjesta.</string>
|
||||
<string name="warning_database_info_changed_options">Sjedini podatke, prepiši vanjske promjene spremanjem baze podataka ili je ponovo učitaj s najnovijim promjenama.</string>
|
||||
<string name="warning_database_info_changed">Podaci u datoteci tvoje baze podataka izmijenjeni su izvan programa.</string>
|
||||
<string name="warning_database_info_changed">Podaci u datoteci tvoje baze podataka izmijenjeni su izvan aplikacije.</string>
|
||||
<string name="menu_reload_database">Ponovo učitaj podatke</string>
|
||||
<string name="error_otp_type">Ovaj obrazac ne prepoznaje postojeću vrstu jednokratne lozinke. Provjera valjanosti možda više neće pravilno generirati token.</string>
|
||||
<string name="unit_gibibyte">GiB</string>
|
||||
@@ -594,7 +592,7 @@
|
||||
<string name="menu_external_icon">Vanjska ikona</string>
|
||||
<string name="seed">Tajna fraza</string>
|
||||
<string name="hint_icon_name">Ime ikone</string>
|
||||
<string name="warning_exact_alarm">Nisi dopustio/la programu da koristi točan alarm. Kao rezultat toga, značajke koje zahtijevaju tajmer neće biti obavljene s točnim vremenom.</string>
|
||||
<string name="warning_exact_alarm">Nisi dopustio/la aplikaciji da koristi točan alarm. Zbog toga funkije koje zahtijevaju tajmer neće biti obavljene s točnim vremenom.</string>
|
||||
<string name="permission">Dozvola</string>
|
||||
<string name="tags">Oznake</string>
|
||||
<string name="menu_merge_database">Sjedini podatke</string>
|
||||
@@ -607,4 +605,40 @@
|
||||
<string name="content_description_entry_foreground_color">Prednja boja unosa</string>
|
||||
<string name="content_description_database_color">Boja baze podataka</string>
|
||||
<string name="content_description_entry_background_color">Pozadinska boja unosa</string>
|
||||
<string name="expired">Isteklo</string>
|
||||
<string name="search_filters">Filtri pretrage</string>
|
||||
<string name="current_group">Trenutačna grupa</string>
|
||||
<string name="case_sensitive">Razlikovanje velikih/malih slova</string>
|
||||
<string name="searchable">Pretraživo</string>
|
||||
<string name="inherited">Naslijedi</string>
|
||||
<string name="custom_data">Prilagođeni podaci</string>
|
||||
<string name="auto_type_sequence">Slijed automatskog popunjavanja</string>
|
||||
<string name="regex">Regularni izraz</string>
|
||||
<string name="menu_merge_from">Sjedini iz …</string>
|
||||
<string name="menu_save_copy_to">Spremi kopiju u …</string>
|
||||
<string name="navigation_drawer_open">Ploča navigacije otvorena</string>
|
||||
<string name="navigation_drawer_close">Ploča navigacije zatvorena</string>
|
||||
<string name="advanced_unlock_keystore_warning">Ova će funckija spremiti šifrirane podatke za prijavu u sigurni KeyStore tvog uređaja.
|
||||
\n
|
||||
\nOvisno o izvornoj API implementaciji operacijskog sustava, funcionalsnost možda neće biti potpuna.
|
||||
\nProvjeri kompatibilnost i sigurnost KeyStorea kod proizvođača tvog uređaja i kreatora ROM-a koji koristiš.</string>
|
||||
<string name="warning_database_already_opened">Baza podataka je već otvorena. Za otvaranje nove, najprije je zatvori</string>
|
||||
<string name="content_description_passphrase_word_count">Broj riječi dugačke lozinke</string>
|
||||
<string name="colorize_password_title">Oboji lozinke</string>
|
||||
<string name="colorize_password_summary">Oboje znakove lozinke prema vrsti</string>
|
||||
<string name="passphrase">Dugačka lozinka</string>
|
||||
<string name="entropy">Entropija: %1$s bit</string>
|
||||
<string name="entropy_high">Entropija: visoka</string>
|
||||
<string name="entropy_calculate">Entropija: Izračunaj …</string>
|
||||
<string name="at_least_one_char">Barem jedan znak od svakog</string>
|
||||
<string name="exclude_ambiguous_chars">Isključi višeznačne znakove</string>
|
||||
<string name="consider_chars_filter">Uzmi u obzir znakove</string>
|
||||
<string name="keyboard_previous_search_title">Ekran pretrage</string>
|
||||
<string name="keyboard_previous_search_summary">Automatski se vrati na prethodnu tipkovnicu na ekranu pretrage</string>
|
||||
<string name="word_separator">Znak razdvajanja</string>
|
||||
<string name="ignore_chars_filter">Zanemari znakove</string>
|
||||
<string name="lower_case">mala slova</string>
|
||||
<string name="title_case">Velika Početna Slova</string>
|
||||
<string name="character_count">Broj znakova: %1$d</string>
|
||||
<string name="upper_case">VELIKA SLOVA</string>
|
||||
</resources>
|
||||
@@ -314,8 +314,6 @@
|
||||
<string name="error_create_database_file">Ezzel a jelszó és kulcsfájl kombinációval nem hozható létre adatbázis.</string>
|
||||
<string name="education_setup_OTP_summary">Állítson be egyszer használatos jelszókezelést (HOTP/TOTP), hogy tokent állítson elő a kétfaktoros hitelesítéshez (2FA).</string>
|
||||
<string name="education_setup_OTP_title">OTP beállítása</string>
|
||||
<string name="autofill_auto_search_summary">Keresési találatok automatikus javaslata a webes domain vagy az alkalmazásazonosító alapján</string>
|
||||
<string name="autofill_auto_search_title">Automatikus keresés</string>
|
||||
<string name="clear_clipboard_notification_summary">Az adatbázis zárolása, ha a vágólap ideje lejár, vagy ha a használata után bezárja az értesítést</string>
|
||||
<string name="clear_clipboard_notification_title">Zárolás bezáráskor</string>
|
||||
<string name="lock_database_show_button_summary">Megjeleníti a zárolás gombot a felhasználói felületen</string>
|
||||
|
||||
@@ -329,8 +329,6 @@
|
||||
<string name="autofill_save_search_info_summary">Cobalah untuk menyimpan informasi pencarian saat membuat pilihan entri manual</string>
|
||||
<string name="autofill_save_search_info_title">Simpan info pencarian</string>
|
||||
<string name="autofill_inline_suggestions_title">Saran sebaris</string>
|
||||
<string name="autofill_auto_search_summary">Secara otomatis menyarankan hasil pencarian dari domain web atau ID aplikasi</string>
|
||||
<string name="autofill_auto_search_title">Pencarian otomatis</string>
|
||||
<string name="autofill_close_database_summary">Menutup database setelah pilihan isi-auto</string>
|
||||
<string name="autofill_close_database_title">Tutup database</string>
|
||||
<string name="enter">Enter</string>
|
||||
|
||||
@@ -417,8 +417,6 @@
|
||||
<string name="education_setup_OTP_title">Imposta password usa e getta (OTP)</string>
|
||||
<string name="enable_auto_save_database_summary">Salva il database dopo ogni azione importante (in modalità «Modificabile»)</string>
|
||||
<string name="enable_auto_save_database_title">Salvataggio automatico del database</string>
|
||||
<string name="autofill_auto_search_summary">Suggerisci automaticamente risultati dal dominio web o ID dell\'applicazione</string>
|
||||
<string name="autofill_auto_search_title">Ricerca automatica</string>
|
||||
<string name="keyboard_auto_go_action_summary">Azione del tasto \"Vai\" dopo aver premuto un tasto \"Campo\"</string>
|
||||
<string name="keyboard_auto_go_action_title">Tasto di azione automatica</string>
|
||||
<string name="device_keyboard_setting_title">Impostazioni della tastiera del dispositivo</string>
|
||||
|
||||
@@ -388,8 +388,6 @@
|
||||
<string name="keyboard_previous_lock_summary">データベースのロック後、切り替え前のキーボードへ自動的に戻します</string>
|
||||
<string name="autofill_close_database_title">データベースを閉じる</string>
|
||||
<string name="autofill_close_database_summary">自動入力の選択後、データベースを閉じます</string>
|
||||
<string name="autofill_auto_search_title">自動検索</string>
|
||||
<string name="autofill_auto_search_summary">ウェブドメインまたはアプリケーション ID から検索結果を自動的に提案します</string>
|
||||
<string name="autofill_application_id_blocklist_title">アプリケーションのブロックリスト</string>
|
||||
<string name="autofill_save_search_info_title">検索情報の保存</string>
|
||||
<string name="autofill_save_search_info_summary">手動でエントリーを選択したとき、検索情報の保存を試みます</string>
|
||||
|
||||
@@ -152,7 +152,6 @@
|
||||
<string name="about_description">KeePass പാസ്വേഡ് മാനേജറിന്റെ Android നടപ്പിലാക്കൽ</string>
|
||||
<string name="download_complete">പൂർത്തിയാക്കി!</string>
|
||||
<string name="education_create_database_title">നിങ്ങളുടെ ഡാറ്റാബേസ് ഫയൽ സൃഷ്ടിക്കുക</string>
|
||||
<string name="autofill_auto_search_title">സ്വയം തിരയൽ</string>
|
||||
<string name="keyboard_change">കീബോർഡ് മാറ്റുക</string>
|
||||
<string name="keyboard_label">Magikeyboard (KeePassDX)</string>
|
||||
<string name="compression_gzip">Gzip</string>
|
||||
|
||||
@@ -383,7 +383,6 @@
|
||||
<string name="entry_add_attachment">Legg til vedlegg</string>
|
||||
<string name="contribution">Bidrag</string>
|
||||
<string name="contact">Kontakt</string>
|
||||
<string name="autofill_auto_search_title">Autosøk</string>
|
||||
<string name="enable_auto_save_database_summary">Lagre database etter hver viktige handling i (i \"modifiserbart\" modus)</string>
|
||||
<string name="download_initialization">Igangsetter…</string>
|
||||
<string name="hide_expired_entries_summary">Utløpte oppføringer er skjult</string>
|
||||
@@ -436,7 +435,6 @@
|
||||
<string name="keyboard_previous_lock_title">Lås databasen</string>
|
||||
<string name="keyboard_previous_lock_summary">Bytt automatisk tilbake til forrige tastatur etter å ha låst databasen</string>
|
||||
<string name="autofill_close_database_summary">Lukk databasen etter et autofyllvalg</string>
|
||||
<string name="autofill_auto_search_summary">Foreslå automatisk søkeresultater fra webdomenet eller applikasjons-IDen</string>
|
||||
<string name="autofill_save_search_info_summary">Prøv å lagre søkeinformasjon når du foretar et manuelt valg</string>
|
||||
<string name="autofill_block">Blokker autofyll</string>
|
||||
<string name="autofill_block_restart">Start appen på nytt som inneholder skjemaet for å aktivere blokkeringen.</string>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
<string name="error_pass_gen_type">Je moet minimaal één soort wachtwoordgenerering kiezen.</string>
|
||||
<string name="error_pass_match">De wachtwoorden komen niet overeen.</string>
|
||||
<string name="error_rounds_too_large">\"Cycli-waarde\" te groot. Deze wordt ingesteld op 2147483648.</string>
|
||||
<string name="error_wrong_length">Voer een positief geheel getal in in het veld \"Lengte\".</string>
|
||||
<string name="error_wrong_length">Voer in het veld \"Lengte\" een positief geheel getal in.</string>
|
||||
<string name="file_browser">Bestandsbeheer</string>
|
||||
<string name="generate_password">Wachtwoord genereren</string>
|
||||
<string name="hint_conf_pass">Wachtwoord bevestigen</string>
|
||||
@@ -168,7 +168,7 @@
|
||||
<string name="parallelism_explanation">Het aantal parallellen (aantal threads) dat de afleidingsfunctie mag gebruiken.</string>
|
||||
<string name="sort_menu">Sorteren</string>
|
||||
<string name="sort_ascending">Laagste eerst ↓</string>
|
||||
<string name="sort_groups_before">Groepen vóór losse items sorteren</string>
|
||||
<string name="sort_groups_before">Groepen vooraan plaatsen</string>
|
||||
<string name="sort_recycle_bin_bottom">Prullenbak onderaan plaatsen</string>
|
||||
<string name="sort_title">Titel</string>
|
||||
<string name="sort_username">Gebruikersnaam</string>
|
||||
@@ -184,8 +184,8 @@
|
||||
<string name="database_history">Geschiedenis</string>
|
||||
<string name="menu_appearance_settings">Uiterlijk</string>
|
||||
<string name="general">Algemeen</string>
|
||||
<string name="autofill">Auto-aanvullen</string>
|
||||
<string name="autofill_service_name">KeePassDX auto-aanvullendienst</string>
|
||||
<string name="autofill">Automatisch aanvullen</string>
|
||||
<string name="autofill_service_name">KeePassDX dienst automatisch aanvullen</string>
|
||||
<string name="autofill_sign_in_prompt">Inloggen met KeePassDX</string>
|
||||
<string name="set_autofill_service_title">Dienst automatisch aanvullen</string>
|
||||
<string name="autofill_explanation_summary">Schakel de dienst in om formulieren in andere apps in te vullen</string>
|
||||
@@ -286,7 +286,7 @@
|
||||
<string name="style_choose_summary">Thema gebruikt in de app</string>
|
||||
<string name="icon_pack_choose_title">Pictogrammenpakket</string>
|
||||
<string name="icon_pack_choose_summary">Gebruikt pictogrammenpakket</string>
|
||||
<string name="build_label">Build %1$s</string>
|
||||
<string name="build_label">Editie %1$s</string>
|
||||
<string name="keyboard_name">Magikeyboard</string>
|
||||
<string name="keyboard_label">Magikeyboard (KeePassDX)</string>
|
||||
<string name="keyboard_setting_label">Instellingen Magikeyboard</string>
|
||||
@@ -403,8 +403,6 @@
|
||||
<string name="download_attachment">Download %1$s</string>
|
||||
<string name="education_setup_OTP_summary">Stel eenmalig wachtwoordbeheer (HOTP / TOTP) in om een token te genereren voor tweefactorauthenticatie (2FA).</string>
|
||||
<string name="enable_auto_save_database_title">Automatisch opslaan</string>
|
||||
<string name="autofill_auto_search_summary">Automatisch zoekresultaten voorstellen vanuit het webdomein of de toepassings-ID</string>
|
||||
<string name="autofill_auto_search_title">Automatisch zoeken</string>
|
||||
<string name="recycle_bin_group_title">Prullenbak</string>
|
||||
<string name="lock_database_show_button_summary">Toont de vergrendelknop in de gebruikersinterface</string>
|
||||
<string name="lock_database_show_button_title">Vergrendelknop tonen</string>
|
||||
@@ -440,7 +438,7 @@
|
||||
<string name="autofill_block_restart">Start de app met het formulier opnieuw op om de blokkering te activeren.</string>
|
||||
<string name="autofill_block">Blokkering van automatisch invullen</string>
|
||||
<string name="autofill_web_domain_blocklist_summary">Lijst die het automatisch invullen bij webdomeinen voorkomt</string>
|
||||
<string name="autofill_web_domain_blocklist_title">Webdomein blokkeringslijst</string>
|
||||
<string name="autofill_web_domain_blocklist_title">Webdomein-blokkeringslijst</string>
|
||||
<string name="autofill_application_id_blocklist_summary">Lijst die het automatisch invullen bij apps voorkomt</string>
|
||||
<string name="autofill_application_id_blocklist_title">Applicatieblokkering</string>
|
||||
<string name="filter">Filteren</string>
|
||||
@@ -474,7 +472,7 @@
|
||||
<string name="show_uuid_summary">Toont de UUID die is gekoppeld aan een item of een groep</string>
|
||||
<string name="show_uuid_title">UUID tonen</string>
|
||||
<string name="autofill_read_only_save">Het opslaan van gegevens is niet toegestaan voor een database die is geopend als alleen-lezen.</string>
|
||||
<string name="autofill_ask_to_save_data_summary">Vraag om gegevens op te slaan wanneer een formulier is gevalideerd</string>
|
||||
<string name="autofill_ask_to_save_data_summary">Vraag om gegevens op te slaan wanneer het invullen van een formulier is voltooid</string>
|
||||
<string name="autofill_ask_to_save_data_title">Vragen om gegevens op te slaan</string>
|
||||
<string name="autofill_save_search_info_summary">Probeer zoekinformatie op te slaan bij het maken van een handmatige selectie</string>
|
||||
<string name="autofill_save_search_info_title">Zoekinformatie opslaan</string>
|
||||
@@ -517,7 +515,7 @@
|
||||
<string name="advanced_unlock_invalid_key">Kan de geavanceerde ontgrendelingssleutel niet lezen. Verwijder deze en herhaal de herkenningsprocedure voor het ontgrendelen.</string>
|
||||
<string name="advanced_unlock_prompt_extract_credential_message">Databasegegevens uitpakken met geavanceerde ontgrendelingsgegevens</string>
|
||||
<string name="advanced_unlock_prompt_extract_credential_title">Open database met geavanceerde ontgrendelingsherkenning</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Waarschuwing: je moet nog steeds je hoofdwachtwoord onthouden als je geavanceerde ontgrendelingsherkenning gebruikt.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Je moet nog steeds je hoofdwachtwoord onthouden als je geavanceerde ontgrendelingsherkenning gebruikt.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_title">Geavanceerde ontgrendelingsherkenning</string>
|
||||
<string name="open_advanced_unlock_prompt_store_credential">Open de geavanceerde ontgrendelingsprompt om inloggegevens op te slaan</string>
|
||||
<string name="open_advanced_unlock_prompt_unlock_database">Open de geavanceerde ontgrendelingsprompt om de database te ontgrendelen</string>
|
||||
@@ -614,4 +612,22 @@
|
||||
<string name="show_entry_colors_title">Itemkleuren</string>
|
||||
<string name="show_entry_colors_summary">Toont voorgrond- en achtergrondkleuren in een item</string>
|
||||
<string name="wireless">Wi-Fi</string>
|
||||
<string name="auto_type_sequence">Auto-Type tekenreeks</string>
|
||||
<string name="expired">Verlopen</string>
|
||||
<string name="menu_save_copy_to">Kopie opslaan naar…</string>
|
||||
<string name="searchable">Doorzoekbaar</string>
|
||||
<string name="inherited">Overerving</string>
|
||||
<string name="custom_data">Aangepaste gegevens</string>
|
||||
<string name="case_sensitive">Hoofdlettergevoelig</string>
|
||||
<string name="regex">Reguliere expressie</string>
|
||||
<string name="search_filters">Zoekfilters</string>
|
||||
<string name="current_group">Huidige groep</string>
|
||||
<string name="menu_merge_from">Samenvoegen van…</string>
|
||||
<string name="navigation_drawer_open">Navigatiepaneel openen</string>
|
||||
<string name="navigation_drawer_close">Navigatiepaneel sluiten</string>
|
||||
<string name="advanced_unlock_keystore_warning">Deze functie slaat versleutelde gegevens op in de beveiligde KeyStore van uw apparaat.
|
||||
\n
|
||||
\nAfhankelijk van de specifieke API-implementatie van het besturingssysteem, is dit mogelijk niet volledig functioneel.
|
||||
\nControleer de compatibiliteit en beveiliging van de KeyStore met de fabrikant van uw apparaat en de maker van de ROM die u gebruikt.</string>
|
||||
<string name="warning_database_already_opened">Er is al een database geopend, sluit deze eerst alvorens een nieuwe te openen</string>
|
||||
</resources>
|
||||
@@ -19,7 +19,7 @@
|
||||
--><resources>
|
||||
<string name="feedback">Informacje zwrotne</string>
|
||||
<string name="homepage">Strona główna</string>
|
||||
<string name="about_description">Implementacja Android dla menedżera haseł KeePass</string>
|
||||
<string name="about_description">Implementacja menedżera haseł KeePass w systemie Android</string>
|
||||
<string name="accept">Akceptuj</string>
|
||||
<string name="add_entry">Dodaj wpis</string>
|
||||
<string name="add_group">Dodaj grupę</string>
|
||||
@@ -83,7 +83,7 @@
|
||||
<string name="lowercase">Małe litery</string>
|
||||
<string name="hide_password_title">Ukryj hasła</string>
|
||||
<string name="hide_password_summary">Maskuj hasła (***) domyślnie</string>
|
||||
<string name="about">O programie</string>
|
||||
<string name="about">Informacje</string>
|
||||
<string name="menu_change_key_settings">Zmień klucz główny</string>
|
||||
<string name="settings">Ustawienia</string>
|
||||
<string name="menu_database_settings">Ustawienia bazy danych</string>
|
||||
@@ -114,7 +114,7 @@
|
||||
<string name="underline">Podkreślenie</string>
|
||||
<string name="unsupported_db_version">Nieobsługiwana wersja bazy danych.</string>
|
||||
<string name="uppercase">Wielkie litery</string>
|
||||
<string name="education_unlock_summary">prowadź hasło i/lub plik klucza, aby odblokować bazę danych.
|
||||
<string name="education_unlock_summary">Wprowadź hasło i/lub plik klucza, aby odblokować bazę danych.
|
||||
\n
|
||||
\nUtwórz kopię zapasową pliku bazy danych w bezpiecznym miejscu po każdej zmianie.</string>
|
||||
<string-array name="list_size_options">
|
||||
@@ -163,8 +163,8 @@
|
||||
<string name="parallelism_explanation">Stopień równoległości (tj. liczba wątków) wykorzystywany przez funkcję wyprowadzania klucza.</string>
|
||||
<string name="sort_menu">Sortuj</string>
|
||||
<string name="sort_ascending">Od najniższej ↓</string>
|
||||
<string name="sort_username">Nazwa Użytkownika</string>
|
||||
<string name="sort_creation_time">Utwórz</string>
|
||||
<string name="sort_username">Nazwa użytkownika</string>
|
||||
<string name="sort_creation_time">Utworzenie</string>
|
||||
<string name="sort_last_modify_time">Modyfikacja</string>
|
||||
<string name="sort_last_access_time">Dostęp</string>
|
||||
<string name="warning">Ostrzeżenie</string>
|
||||
@@ -196,8 +196,8 @@
|
||||
<string name="lock">Blokada</string>
|
||||
<string name="lock_database_screen_off_title">Blokada ekranu</string>
|
||||
<string name="lock_database_screen_off_summary">Zablokuj bazę danych po kilku sekundach od wyłączenia ekranu</string>
|
||||
<string name="advanced_unlock">Odcisk palca</string>
|
||||
<string name="biometric_unlock_enable_title">Skanowanie odcisków palców</string>
|
||||
<string name="advanced_unlock">Zaawansowane odblokowywanie</string>
|
||||
<string name="biometric_unlock_enable_title">Odblokowywanie biometryczne</string>
|
||||
<string name="biometric_unlock_enable_summary">Umożliwia zeskanowanie danych biometrycznych w celu otwarcia bazy danych</string>
|
||||
<string name="biometric_delete_all_key_title">Usuń klucze szyfrowania</string>
|
||||
<string name="biometric_delete_all_key_summary">Usuń wszystkie klucze szyfrujące związane z zaawansowanym rozpoznawaniem odblokowania</string>
|
||||
@@ -260,20 +260,20 @@
|
||||
\n
|
||||
\nUżyj preferowanej metody wypełniania formularzy.</string>
|
||||
<string name="education_lock_title">Zablokuj bazę danych</string>
|
||||
<string name="education_lock_summary">Szybko zablokuj bazę danych, możesz skonfigurować aplikację, by po chwili ją zablokować, i gdy ekran się wyłączy.</string>
|
||||
<string name="education_lock_summary">Szybko zablokuj bazę danych, możesz skonfigurować aplikację tak, aby blokowała ją po pewnym czasie i po wyłączeniu ekranu.</string>
|
||||
<string name="education_sort_title">Sortowanie elementów</string>
|
||||
<string name="education_sort_summary">Wybierz sposób sortowania wpisów i grup.</string>
|
||||
<string name="education_donation_title">Weź udział</string>
|
||||
<string name="education_donation_summary">Pomóż zwiększyć stabilność, bezpieczeństwo i dodawanie kolejnych funkcji.</string>
|
||||
<string name="html_text_ad_free">W przeciwieństwie do wielu aplikacji do zarządzania hasłami, ta jest wolna od <strong>reklam</strong>, <strong>jest wolnym oprogramowaniem copyleftted</strong> i nie zbiera danych osobowych na swoich serwerach, bez względu na to, jakiej wersji używasz.</string>
|
||||
<string name="html_text_ad_free">W przeciwieństwie do wielu aplikacji do zarządzania hasłami, ta jest wolna od <strong>reklam</strong>, <strong>jest wolnym oprogramowaniem objętym klauzulą copyleft</strong> i nie zbiera danych osobowych na swoich serwerach, bez względu na to, jakiej wersji używasz.</string>
|
||||
<string name="html_text_buy_pro">Kupując wersję pro, będziesz mieć dostęp do <strong>stylu wizualnego</strong> a szczególnie pomożesz <strong> zrealizować projekty społecznościowe.</strong></string>
|
||||
<string name="html_text_feature_generosity">Ten <strong>styl wizualny</strong> jest dostępny dzięki Twojej hojności.</string>
|
||||
<string name="html_text_donation">Aby zachować naszą wolność i być zawsze aktywnym, liczymy na Twój <strong>wkład.</strong></string>
|
||||
<string name="html_text_dev_feature">Ta funkcja jest <strong>rozwojowa</strong> i wymaga twojego <strong>wkładu</strong> aby być wkrótce dostępną.</string>
|
||||
<string name="html_text_dev_feature_buy_pro">Kupując wersję <strong>pro</strong> wersja,</string>
|
||||
<string name="html_text_dev_feature">Ta funkcja jest <strong>rozwojowa</strong> i wymaga Twojego <strong>wkładu</strong>, aby być wkrótce dostępną.</string>
|
||||
<string name="html_text_dev_feature_buy_pro">Kupując wersję <strong>pro</strong>,</string>
|
||||
<string name="html_text_dev_feature_contibute">Przez <strong>przyczynianie się</strong>,</string>
|
||||
<string name="html_text_dev_feature_encourage">zachęcasz programistów do tworzenia <strong>nowych funkcji</strong> i <strong>naprawy błędów</strong> zgodnie z Twoimi uwagami.</string>
|
||||
<string name="html_text_dev_feature_thanks">Wielkie dzięki za twój wkład.</string>
|
||||
<string name="html_text_dev_feature_thanks">Wielkie dzięki za Twój wkład.</string>
|
||||
<string name="html_text_dev_feature_work_hard">Ciężko pracujemy, aby szybko udostępnić tę funkcję.</string>
|
||||
<string name="html_text_dev_feature_upgrade">Pamiętaj, aby aktualizować swoją aplikację, instalując nowe wersje.</string>
|
||||
<string name="download">Pobieranie</string>
|
||||
@@ -367,7 +367,7 @@
|
||||
<string name="database_default_username_title">Domyślna nazwa użytkownika</string>
|
||||
<string name="database_custom_color_title">Niestandardowy kolor bazy danych</string>
|
||||
<string name="compression">Kompresja</string>
|
||||
<string name="compression_none">Żaden</string>
|
||||
<string name="compression_none">Żadna</string>
|
||||
<string name="compression_gzip">Gzip</string>
|
||||
<string name="device_keyboard_setting_title">Ustawienia klawiatury urządzenia</string>
|
||||
<string name="error_invalid_OTP">Nieprawidłowy klucz tajny OTP.</string>
|
||||
@@ -377,7 +377,7 @@
|
||||
<string name="error_otp_period">Okres musi wynosić od %1$d do %2$d sekund.</string>
|
||||
<string name="error_otp_digits">Token musi zawierać cyfry od %1$d do %2$d.</string>
|
||||
<string name="invalid_db_same_uuid">%1$s o tym samym identyfikatorze UUID %2$s już istnieje.</string>
|
||||
<string name="contains_duplicate_uuid_procedure">Rozwiązać problem poprzez stworzenie nowych identyfikatory UUID duplikatów, aby kontynuować\?</string>
|
||||
<string name="contains_duplicate_uuid_procedure">Rozwiązać problem, generując nowe identyfikatory UUID dla duplikatów, aby kontynuować\?</string>
|
||||
<string name="clipboard_explanation_summary">Skopiuj pola wprowadzania danych za pomocą schowka urządzenia</string>
|
||||
<string name="advanced_unlock_explanation_summary">Użyj zaawansowanego odblokowywania w celu łatwiejszego otwierania bazy danych</string>
|
||||
<string name="database_data_compression_summary">Kompresja danych zmniejsza rozmiar bazy danych</string>
|
||||
@@ -408,7 +408,7 @@
|
||||
<string name="hide_expired_entries_title">Ukryj wygasłe wpisy</string>
|
||||
<string name="hide_expired_entries_summary">Wygasłe wpisy nie są wyświetlane</string>
|
||||
<string name="contact">Kontakt</string>
|
||||
<string name="html_about_contribution">Aby <strong>zachować naszą wolność</strong>, <strong>sprawdzać błędy</strong>, <strong>dodać funkcje</strong> i <strong>by być zawsze aktywnym</strong>, liczymy na twój <strong>wkład</strong>.</string>
|
||||
<string name="html_about_contribution">Aby <strong>zachować naszą wolność</strong>, <strong>sprawdzać błędy</strong>, <strong>dodawać funkcje</strong> i <strong> być zawsze aktywnym</strong>, liczymy na Twój <strong>wkład</strong>.</string>
|
||||
<string name="auto_focus_search_title">Szybkie wyszukiwanie</string>
|
||||
<string name="auto_focus_search_summary">Wyszukiwanie po otwarciu bazy danych</string>
|
||||
<string name="remember_database_locations_title">Zapamiętaj lokalizacje baz danych</string>
|
||||
@@ -428,8 +428,6 @@
|
||||
<string name="discard">Odrzuć</string>
|
||||
<string name="discard_changes">Odrzucić zmiany\?</string>
|
||||
<string name="validate">Walidacja</string>
|
||||
<string name="autofill_auto_search_summary">Automatyczne sugerowanie wyników wyszukiwania z domeny internetowej lub identyfikatora aplikacji</string>
|
||||
<string name="autofill_auto_search_title">Automatyczne wyszukiwanie</string>
|
||||
<string name="lock_database_show_button_summary">Wyświetla przycisk blokady w interfejsie użytkownika</string>
|
||||
<string name="lock_database_show_button_title">Pokaż przycisk blokady</string>
|
||||
<string name="autofill_preference_title">Ustawienia autouzupełniania</string>
|
||||
@@ -445,7 +443,7 @@
|
||||
<string name="subdomain_search_summary">Wyszukiwanie domen internetowych z ograniczeniami związanymi z subdomenami</string>
|
||||
<string name="error_string_type">Ten tekst nie pasuje do żądanej pozycji.</string>
|
||||
<string name="content_description_add_item">Dodaj pozycję</string>
|
||||
<string name="autofill_web_domain_blocklist_summary">Lista zablokowanych, która uniemożliwia automatyczne wypełnianie aplikacji</string>
|
||||
<string name="autofill_web_domain_blocklist_summary">Lista zablokowanych, która uniemożliwia automatyczne wypełnianie domen internetowych</string>
|
||||
<string name="autofill_web_domain_blocklist_title">Lista zablokowanych domen internetowych</string>
|
||||
<string name="autofill_application_id_blocklist_title">Lista zablokowanych aplikacji</string>
|
||||
<string name="keyboard_previous_fill_in_summary">Automatycznie przełącz się z powrotem na poprzednią klawiaturę po wykonaniu automatycznej akcji klawiszy</string>
|
||||
@@ -496,22 +494,22 @@
|
||||
<string name="select_entry">Wybierz pozycję</string>
|
||||
<string name="back_to_previous_keyboard">Wróć do poprzedniej klawiatury</string>
|
||||
<string name="custom_fields">Pola niestandardowe</string>
|
||||
<string name="advanced_unlock_delete_all_key_warning">Czy usunąć wszystkie klucze szyfrowania związane z zaawansowanym rozpoznawaniem odblokowania\?</string>
|
||||
<string name="advanced_unlock_delete_all_key_warning">Czy usunąć wszystkie klucze szyfrowania związane z zaawansowanym rozpoznawaniem odblokowywania\?</string>
|
||||
<string name="device_credential_unlock_enable_summary">Umożliwia otwieranie bazy danych za pomocą poświadczeń urządzenia</string>
|
||||
<string name="device_credential_unlock_enable_title">Odblokowanie uwierzytelniające urządzenia</string>
|
||||
<string name="device_credential_unlock_enable_title">Odblokowywanie uwierzytelniające urządzenia</string>
|
||||
<string name="device_credential">Uwierzytelnienie urządzenia</string>
|
||||
<string name="credential_before_click_advanced_unlock_button">Wpisz hasło, a następnie kliknij ten przycisk.</string>
|
||||
<string name="advanced_unlock_not_recognized">Nie można rozpoznać zaawansowanego wydruku odblokowującego</string>
|
||||
<string name="advanced_unlock_invalid_key">Nie można odczytać zaawansowanego klucza odblokowującego. Usuń go i powtórz procedurę rozpoznawania odblokowania.</string>
|
||||
<string name="advanced_unlock_prompt_extract_credential_message">Wyodrębnij poświadczenia bazy danych z zaawansowanymi danymi odblokowującymi</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Ostrzeżenie: Jeśli używasz zaawansowanego rozpoznawania odblokowania, nadal musisz zapamiętać hasło główne.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_message">Jeśli używasz zaawansowanego rozpoznawania odblokowania, nadal musisz zapamiętać główne dane uwierzytelniające.</string>
|
||||
<string name="advanced_unlock_prompt_store_credential_title">Zaawansowane rozpoznawanie odblokowania</string>
|
||||
<string name="menu_keystore_remove_key">Usuń zaawansowany klucz odblokowujący</string>
|
||||
<string name="education_advanced_unlock_summary">Połącz swoje hasło ze zeskanowanymi danymi biometrycznymi lub danymi logowania urządzenia, aby szybko odblokować bazę danych.</string>
|
||||
<string name="education_advanced_unlock_title">Zaawansowane odblokowywanie bazy danych</string>
|
||||
<string name="advanced_unlock_timeout">Limit czasu zaawansowanego odblokowywania</string>
|
||||
<string name="temp_advanced_unlock_timeout_summary">Czas trwania zaawansowanego odblokowywania przed usunięciem jego zawartości</string>
|
||||
<string name="temp_advanced_unlock_timeout_title">Wygaśnięcie zaawansowanego odblokowania</string>
|
||||
<string name="temp_advanced_unlock_timeout_title">Wygaśnięcie zaawansowanego odblokowywania</string>
|
||||
<string name="temp_advanced_unlock_enable_summary">Nie przechowuj żadnych zaszyfrowanych treści, aby korzystać z zaawansowanego odblokowywania</string>
|
||||
<string name="advanced_unlock_tap_delete">Naciśnij, aby usunąć zaawansowane klucze odblokowujące</string>
|
||||
<string name="content">Zawartość</string>
|
||||
@@ -524,7 +522,7 @@
|
||||
<string name="autofill_inline_suggestions_summary">Spróbuj wyświetlić sugestie autouzupełniania bezpośrednio z kompatybilnej klawiatury</string>
|
||||
<string name="temp_advanced_unlock_enable_title">Zaawansowane odblokowywanie tymczasowe</string>
|
||||
<string name="advanced_unlock_prompt_not_initialized">Nie można zainicjować monitu odblokowania zaawansowanego.</string>
|
||||
<string name="open_advanced_unlock_prompt_store_credential">Otwórz monit odblokowania zaawansowanego, aby odblokować bazę danych</string>
|
||||
<string name="open_advanced_unlock_prompt_store_credential">Otwórz monit odblokowania zaawansowanego, aby przechowywać poświadczenia</string>
|
||||
<string name="open_advanced_unlock_prompt_unlock_database">Otwórz monit odblokowania zaawansowanego, aby odblokować bazę danych</string>
|
||||
<string name="warning_database_revoked">Dostęp do pliku odwołany przez menedżera plików, zamknij bazę danych i otwórz ją ponownie z jej lokalizacji.</string>
|
||||
<string name="warning_database_info_changed_options">Scal dane, zastąp modyfikacje zewnętrzne poprzez zapisanie bazy danych lub ponownie załaduj ją z najnowszymi zmianam.</string>
|
||||
@@ -627,4 +625,27 @@
|
||||
<string name="searchable">Przeszukiwalne</string>
|
||||
<string name="menu_save_copy_to">Zapisz kopię w…</string>
|
||||
<string name="expired">Wygasłe</string>
|
||||
<string name="warning_database_already_opened">Baza danych jest już otwarta, należy ją najpierw zamknąć, aby otworzyć nową</string>
|
||||
<string name="advanced_unlock_keystore_warning">Ta funkcja umożliwia przechowywanie zaszyfrowanych danych uwierzytelniających w bezpiecznym magazynie kluczy urządzenia.
|
||||
\n
|
||||
\nW zależności od konkretnej implementacji interfejsu API systemu operacyjnego, może nie być w pełni funkcjonalna.
|
||||
\nSprawdź kompatybilność i bezpieczeństwo magazynu kluczy u producenta urządzenia i twórcy używanego ROM-u.</string>
|
||||
<string name="passphrase">Fraza hasła</string>
|
||||
<string name="colorize_password_summary">Koloruj znaki hasła według typu</string>
|
||||
<string name="keyboard_previous_search_title">Ekran wyszukiwania</string>
|
||||
<string name="entropy">Entropia: %1$s bitowa</string>
|
||||
<string name="entropy_high">Entropia: Wysoka</string>
|
||||
<string name="entropy_calculate">Entropia: Oblicz…</string>
|
||||
<string name="at_least_one_char">Co najmniej po jednym znaku z każdego</string>
|
||||
<string name="exclude_ambiguous_chars">Wyklucz niejednoznaczne znaki</string>
|
||||
<string name="consider_chars_filter">Rozważ znaki</string>
|
||||
<string name="word_separator">Separator</string>
|
||||
<string name="lower_case">małe litery</string>
|
||||
<string name="upper_case">DUŻE LITERY</string>
|
||||
<string name="title_case">Jak Nazwy Własne</string>
|
||||
<string name="character_count">Liczba znaków: %1$d</string>
|
||||
<string name="content_description_passphrase_word_count">Liczba słów frazy hasła</string>
|
||||
<string name="ignore_chars_filter">Ignoruj znaki</string>
|
||||
<string name="colorize_password_title">Koloruj hasła</string>
|
||||
<string name="keyboard_previous_search_summary">Automatycznie przełączaj z powrotem do poprzedniej klawiatury na ekranie wyszukiwania</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user