mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Compare commits
221 Commits
4.2.0
...
c88413f7f7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c88413f7f7 | ||
|
|
7b1fb8a4bf | ||
|
|
3567fa797b | ||
|
|
eb41233e57 | ||
|
|
b394a99e40 | ||
|
|
2bbb40e513 | ||
|
|
09ef69e6ae | ||
|
|
762ac8f77b | ||
|
|
d28087d8d8 | ||
|
|
17d4c363ac | ||
|
|
c754b6a049 | ||
|
|
9c6241afc9 | ||
|
|
f6774b6d51 | ||
|
|
108a61905e | ||
|
|
d251788b1a | ||
|
|
7ed8a44168 | ||
|
|
844b1dfc79 | ||
|
|
d087fcc930 | ||
|
|
5fd25c6150 | ||
|
|
c1cfddddbe | ||
|
|
9146315001 | ||
|
|
609b536898 | ||
|
|
f9051ce787 | ||
|
|
d90d175bd8 | ||
|
|
c17fba8ef7 | ||
|
|
ed095ad0a7 | ||
|
|
82a8776911 | ||
|
|
753e9c4721 | ||
|
|
b64094ed20 | ||
|
|
bc854c63f7 | ||
|
|
3b793a72b8 | ||
|
|
f19afbdb2e | ||
|
|
622e9cefdd | ||
|
|
3ba56677ba | ||
|
|
39b4b4df70 | ||
|
|
4180ca92b0 | ||
|
|
bc9d00a1e1 | ||
|
|
5bdc72aa67 | ||
|
|
2be32e6884 | ||
|
|
612db4a6fc | ||
|
|
e74176f3bc | ||
|
|
af1fba42a0 | ||
|
|
bebf30aec1 | ||
|
|
321bb46df5 | ||
|
|
429f6db93f | ||
|
|
fc5a13160a | ||
|
|
c6eee8d449 | ||
|
|
7d227f372f | ||
|
|
3ac56b974f | ||
|
|
2e85ea401b | ||
|
|
fd080fb952 | ||
|
|
cc8e07366a | ||
|
|
c21bcbdbc2 | ||
|
|
e2ee17dae7 | ||
|
|
e68830fa25 | ||
|
|
9ddd66ce85 | ||
|
|
e3b69789bf | ||
|
|
54f2ed9fab | ||
|
|
2fea019b95 | ||
|
|
9ac7ef2d22 | ||
|
|
6d452fa49c | ||
|
|
d99edb6b4d | ||
|
|
cb679f0d59 | ||
|
|
2e237fba2d | ||
|
|
e68863a154 | ||
|
|
5dd9f75095 | ||
|
|
403021d38b | ||
|
|
fea7b30d6f | ||
|
|
ab5c859db4 | ||
|
|
3fcbc65de0 | ||
|
|
3f1ee6bbea | ||
|
|
37ce2ab781 | ||
|
|
ffaf4a761a | ||
|
|
56b7cc9118 | ||
|
|
987f3f9047 | ||
|
|
3039efc67c | ||
|
|
26daac4637 | ||
|
|
88a93829a9 | ||
|
|
7923a63d36 | ||
|
|
9a5c782d5d | ||
|
|
c39e4ba693 | ||
|
|
7db3d0502f | ||
|
|
d557e8b516 | ||
|
|
d6ae17657b | ||
|
|
3468b0f6f5 | ||
|
|
79777801e8 | ||
|
|
a202f66d48 | ||
|
|
ba58d5d47c | ||
|
|
46685592df | ||
|
|
ba9e2892ef | ||
|
|
a1da3b4fbd | ||
|
|
8bee0ec220 | ||
|
|
aebf6b21de | ||
|
|
0cf9253ea4 | ||
|
|
b63ceb37a4 | ||
|
|
c462dae6f5 | ||
|
|
ddf890b861 | ||
|
|
252eb30b13 | ||
|
|
62ab11cc56 | ||
|
|
e19ad3a8cc | ||
|
|
51fd8a77eb | ||
|
|
5ee0c2eb13 | ||
|
|
6d0ef8265c | ||
|
|
ea69d5acb2 | ||
|
|
1fb9595ec3 | ||
|
|
88e0bd51dc | ||
|
|
67477cc53b | ||
|
|
d2549d61d6 | ||
|
|
d6dc75961b | ||
|
|
f40c83812a | ||
|
|
b29c638d20 | ||
|
|
5bb03c2eef | ||
|
|
a76b1195e5 | ||
|
|
64da26f42c | ||
|
|
ef82552a0f | ||
|
|
d61b27ccd0 | ||
|
|
910ba99056 | ||
|
|
3de2a9acfd | ||
|
|
a48dccf27a | ||
|
|
2a561fb37e | ||
|
|
e27a329ac5 | ||
|
|
8e06a2a7cb | ||
|
|
ace82852af | ||
|
|
73369974b8 | ||
|
|
332eda8a7a | ||
|
|
e5ea1e35aa | ||
|
|
86aae9635a | ||
|
|
db3ccae87d | ||
|
|
4cec26967c | ||
|
|
a0368a4981 | ||
|
|
a1c7fe1e99 | ||
|
|
bf247ddeb7 | ||
|
|
1d2bc0fbfb | ||
|
|
85a12fe4ee | ||
|
|
a443ef996b | ||
|
|
c6995ad403 | ||
|
|
9018807eb8 | ||
|
|
b463106dd5 | ||
|
|
a23d28e1fa | ||
|
|
a0454f42d0 | ||
|
|
1c2ac88f47 | ||
|
|
11eb1bae45 | ||
|
|
089d86165a | ||
|
|
6a7362ad35 | ||
|
|
d2c10e2e4e | ||
|
|
0c20a14e67 | ||
|
|
acccf290de | ||
|
|
6ebe0f78af | ||
|
|
935c09ccd2 | ||
|
|
1eb10ad5bd | ||
|
|
ca4283151e | ||
|
|
8fb98ca4e7 | ||
|
|
be74c9710f | ||
|
|
24fb3c4c30 | ||
|
|
3bdc5fe600 | ||
|
|
c30884d6d0 | ||
|
|
5d26c3bd09 | ||
|
|
02e35cf5b7 | ||
|
|
085aefd2b9 | ||
|
|
1ea5b7a50c | ||
|
|
6cba96dd42 | ||
|
|
46238a76bc | ||
|
|
d5a9b664a1 | ||
|
|
6fdc4504d5 | ||
|
|
8a7c411a35 | ||
|
|
5ff9d5fa2f | ||
|
|
bb0f3c80d3 | ||
|
|
597d9c8274 | ||
|
|
4dd8c06fd2 | ||
|
|
72c66b3cd9 | ||
|
|
c2223afa6f | ||
|
|
d338d1340f | ||
|
|
ed4423666b | ||
|
|
d21fe662ff | ||
|
|
4da1c5bd92 | ||
|
|
18c18605fb | ||
|
|
988cb1a8d0 | ||
|
|
b6e01767e0 | ||
|
|
5414854e9c | ||
|
|
ae7f0732c6 | ||
|
|
d49d33fe3a | ||
|
|
5e7fc2d468 | ||
|
|
0d26e6a870 | ||
|
|
dd92f9ceb6 | ||
|
|
ff9239b9c4 | ||
|
|
319d35e485 | ||
|
|
28e65a4601 | ||
|
|
eb626e5bfe | ||
|
|
e1decf9a23 | ||
|
|
fff0e84b95 | ||
|
|
d73a7004b1 | ||
|
|
f71061e835 | ||
|
|
b2d25cc512 | ||
|
|
4d54b56c1d | ||
|
|
c764c6afff | ||
|
|
87b97a3849 | ||
|
|
5e6db44476 | ||
|
|
8615fa817f | ||
|
|
2f891bacd3 | ||
|
|
0d8a426df4 | ||
|
|
c952eb4415 | ||
|
|
2fd53b9416 | ||
|
|
244ca08890 | ||
|
|
409f290e33 | ||
|
|
7ce6092270 | ||
|
|
a819f2f8a8 | ||
|
|
c92da0a72f | ||
|
|
524963dbd8 | ||
|
|
bfe50fa985 | ||
|
|
3d798e6585 | ||
|
|
068c59ac98 | ||
|
|
34ec94a0c3 | ||
|
|
576a355342 | ||
|
|
aa19f11699 | ||
|
|
2fb4dff46d | ||
|
|
e6cf3f12a5 | ||
|
|
ca94ce86ba | ||
|
|
dea6b25bb4 | ||
|
|
c48f64d331 | ||
|
|
5e3a504c1f | ||
|
|
b9b7d7b2db |
28
CHANGELOG
28
CHANGELOG
@@ -1,3 +1,31 @@
|
|||||||
|
KeePassDX(4.3.0)
|
||||||
|
* Manual change of app language #1884 #1990
|
||||||
|
* Add Passkey User Verification #2283
|
||||||
|
* Fix autofill username detection #2276
|
||||||
|
* Fix Passkey in passwordless mode #2282
|
||||||
|
|
||||||
|
KeePassDX(4.2.4)
|
||||||
|
* Fix remembering database location #2262
|
||||||
|
|
||||||
|
KeePassDX(4.2.3)
|
||||||
|
* Fix multiple Passkey selection #2253
|
||||||
|
* Fix database dialog subtitle #2254
|
||||||
|
* Fix save search info if URL present #2255
|
||||||
|
* Small fixes
|
||||||
|
|
||||||
|
KeePassDX(4.2.2)
|
||||||
|
* Fix database merge algorithm #2223
|
||||||
|
* Fix save search info #2243
|
||||||
|
* Fix Play Service as privileged app for Passkey Cross Device Authentication #2244
|
||||||
|
* Small fixes
|
||||||
|
|
||||||
|
KeePassDX(4.2.1)
|
||||||
|
* Fix Magikeyboard autosearch #2233
|
||||||
|
* Fix database merge #2223
|
||||||
|
* Fix dialog database action #2234
|
||||||
|
* Fix autofill selection #2238 #2235
|
||||||
|
* Small fixes
|
||||||
|
|
||||||
KeePassDX(4.2.0)
|
KeePassDX(4.2.0)
|
||||||
* Passkeys management #1421 #2097 (@cali-95)
|
* Passkeys management #1421 #2097 (@cali-95)
|
||||||
* Confirm usage of passkey #2165 #2124
|
* Confirm usage of passkey #2165 #2124
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -6,19 +6,21 @@
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- Create database files / entries and groups.
|
- **Passkeys** for authentication and **local storage of private keys**.
|
||||||
- Support for **.kdb** and **.kdbx** files (version 1 to 4) with AES - Twofish - ChaCha20 - Argon2 algorithm.
|
- **Biometric recognition** for fast unlocking (fingerprint / face unlock / …).
|
||||||
- **Compatible** with the majority of alternative programs (KeePass, KeePassXC, KeeWeb, …).
|
- **One-Time Password** management (HOTP / TOTP) for two-factor authentication (2FA).
|
||||||
|
- **Autofill** for easy form filling with passwords.
|
||||||
|
- **Magikeyboard** to efficiently fill in any field.
|
||||||
|
- Create **encrypted database files**.
|
||||||
|
- Organisation of credentials by **entry** and in **group** trees.
|
||||||
- Allows opening and **copying URI / URL fields quickly**.
|
- Allows opening and **copying URI / URL fields quickly**.
|
||||||
- **Biometric recognition** for fast unlocking *(fingerprint / face unlock / …)*.
|
- Dynamic **templates** for each type of entry.
|
||||||
- **One-Time Password** management *(HOTP / TOTP)* for Two-factor authentication (2FA).
|
|
||||||
- Material design with **themes**.
|
|
||||||
- **Auto-Fill** and Integration.
|
|
||||||
- Field filling **keyboard**.
|
|
||||||
- Dynamic **templates**
|
|
||||||
- **History** of each entry.
|
- **History** of each entry.
|
||||||
- Precise management of **settings**.
|
- Precise management of **settings**.
|
||||||
- Code written in **native languages** *(Kotlin / Java / JNI / C)*.
|
- Material design with **themes**.
|
||||||
|
- Support for **.kdb** and **.kdbx** files (version 1 to 4) with AES - Twofish - ChaCha20 - Argon2 algorithm.
|
||||||
|
- **Compatible** with the majority of alternative programs (KeePass, KeePassXC, KeeWeb, …).
|
||||||
|
- Code written in **native languages** (Kotlin / Java / JNI / C).
|
||||||
|
|
||||||
KeePassDX is **open source** and **ad-free**.
|
KeePassDX is **open source** and **ad-free**.
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId "com.kunzisoft.keepass"
|
applicationId "com.kunzisoft.keepass"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 35
|
targetSdkVersion 35
|
||||||
versionCode = 145
|
versionCode = 150
|
||||||
versionName = "4.2.0"
|
versionName = "4.3.0"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
||||||
testApplicationId = "com.kunzisoft.keepass.tests"
|
testApplicationId = "com.kunzisoft.keepass.tests"
|
||||||
@@ -110,6 +110,10 @@ android {
|
|||||||
// Bouncy castle bug https://github.com/bcgit/bc-java/issues/1685
|
// Bouncy castle bug https://github.com/bcgit/bc-java/issues/1685
|
||||||
resources.pickFirsts.add('META-INF/versions/9/OSGI-INF/MANIFEST.MF')
|
resources.pickFirsts.add('META-INF/versions/9/OSGI-INF/MANIFEST.MF')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
androidResources {
|
||||||
|
generateLocaleConfig true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def room_version = "2.5.1"
|
def room_version = "2.5.1"
|
||||||
|
|||||||
@@ -48,6 +48,18 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "android",
|
||||||
|
"info": {
|
||||||
|
"package_name": "org.ironfoxoss.ironfox.nightly",
|
||||||
|
"signatures": [
|
||||||
|
{
|
||||||
|
"build": "release",
|
||||||
|
"cert_fingerprint_sha256": "C5:E2:91:B5:A5:71:F9:C8:CD:9A:97:99:C2:C9:4E:02:EC:97:03:94:88:93:F2:CA:75:6D:67:B9:42:04:F9:04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "android",
|
"type": "android",
|
||||||
"info": {
|
"info": {
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ import androidx.core.graphics.BlendModeColorFilterCompat
|
|||||||
import androidx.core.graphics.BlendModeCompat
|
import androidx.core.graphics.BlendModeCompat
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
@@ -53,6 +56,11 @@ import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
|||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
|
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
|
||||||
import com.kunzisoft.keepass.adapters.TagsAdapter
|
import com.kunzisoft.keepass.adapters.TagsAdapter
|
||||||
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationActionType
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.isUserVerificationNeeded
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.requestShowUnprotectField
|
||||||
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
@@ -78,7 +86,10 @@ import com.kunzisoft.keepass.view.changeTitleColor
|
|||||||
import com.kunzisoft.keepass.view.hideByFading
|
import com.kunzisoft.keepass.view.hideByFading
|
||||||
import com.kunzisoft.keepass.view.setTransparentNavigationBar
|
import com.kunzisoft.keepass.view.setTransparentNavigationBar
|
||||||
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
||||||
|
import com.kunzisoft.keepass.view.showError
|
||||||
import com.kunzisoft.keepass.viewmodels.EntryViewModel
|
import com.kunzisoft.keepass.viewmodels.EntryViewModel
|
||||||
|
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.util.EnumSet
|
import java.util.EnumSet
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@@ -100,14 +111,10 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
private var loadingView: ProgressBar? = null
|
private var loadingView: ProgressBar? = null
|
||||||
|
|
||||||
private val mEntryViewModel: EntryViewModel by viewModels()
|
private val mEntryViewModel: EntryViewModel by viewModels()
|
||||||
|
private val mUserVerificationViewModel: UserVerificationViewModel by viewModels()
|
||||||
|
|
||||||
private val mEntryActivityEducation = EntryActivityEducation(this)
|
private val mEntryActivityEducation = EntryActivityEducation(this)
|
||||||
|
|
||||||
private var mMainEntryId: NodeId<UUID>? = null
|
|
||||||
private var mHistoryPosition: Int = -1
|
|
||||||
private var mEntryIsHistory: Boolean = false
|
|
||||||
private var mEntryLoaded = false
|
|
||||||
|
|
||||||
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
|
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
|
||||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||||
private var mAttachmentSelected: Attachment? = null
|
private var mAttachmentSelected: Attachment? = null
|
||||||
@@ -125,6 +132,8 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
private var mBackgroundColor: Int? = null
|
private var mBackgroundColor: Int? = null
|
||||||
private var mForegroundColor: Int? = null
|
private var mForegroundColor: Int? = null
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -208,7 +217,7 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
|
|
||||||
mEntryViewModel.loadEntry(mDatabase, mainEntryId, historyPosition)
|
mEntryViewModel.loadEntry(mDatabase, mainEntryId, historyPosition)
|
||||||
}
|
}
|
||||||
} catch (e: ClassCastException) {
|
} catch (_: ClassCastException) {
|
||||||
Log.e(TAG, "Unable to retrieve the entry key")
|
Log.e(TAG, "Unable to retrieve the entry key")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,13 +245,9 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
|
|
||||||
mEntryViewModel.entryInfoHistory.observe(this) { entryInfoHistory ->
|
mEntryViewModel.entryInfoHistory.observe(this) { entryInfoHistory ->
|
||||||
if (entryInfoHistory != null) {
|
if (entryInfoHistory != null) {
|
||||||
this.mMainEntryId = entryInfoHistory.mainEntryId
|
|
||||||
|
|
||||||
// Manage history position
|
// Manage history position
|
||||||
val historyPosition = entryInfoHistory.historyPosition
|
val historyPosition = entryInfoHistory.historyPosition
|
||||||
this.mHistoryPosition = historyPosition
|
|
||||||
val entryIsHistory = historyPosition > -1
|
val entryIsHistory = historyPosition > -1
|
||||||
this.mEntryIsHistory = entryIsHistory
|
|
||||||
// Assign history dedicated view
|
// Assign history dedicated view
|
||||||
historyView?.visibility = if (entryIsHistory) View.VISIBLE else View.GONE
|
historyView?.visibility = if (entryIsHistory) View.VISIBLE else View.GONE
|
||||||
// TODO History badge
|
// TODO History badge
|
||||||
@@ -277,7 +282,6 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
mForegroundColor = if (showEntryColors) entryInfo.foregroundColor else null
|
mForegroundColor = if (showEntryColors) entryInfo.foregroundColor else null
|
||||||
|
|
||||||
loadingView?.hideByFading()
|
loadingView?.hideByFading()
|
||||||
mEntryLoaded = true
|
|
||||||
} else {
|
} else {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@@ -320,6 +324,73 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
mEntryViewModel.entryState.collect { entryState ->
|
||||||
|
when (entryState) {
|
||||||
|
is EntryViewModel.EntryState.Loading -> {}
|
||||||
|
is EntryViewModel.EntryState.RequestUnprotectField -> {
|
||||||
|
mDatabase?.let { database ->
|
||||||
|
requestShowUnprotectField(
|
||||||
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
|
database = database,
|
||||||
|
protectedFieldView = entryState.protectedFieldView
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mEntryViewModel.actionPerformed()
|
||||||
|
}
|
||||||
|
is EntryViewModel.EntryState.RequestCopyProtectedField -> {
|
||||||
|
mDatabase?.let { database ->
|
||||||
|
checkUserVerification(
|
||||||
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
|
dataToVerify = UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.COPY_PROTECTED_FIELD,
|
||||||
|
database = database,
|
||||||
|
field = entryState.field,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mEntryViewModel.actionPerformed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
mUserVerificationViewModel.userVerificationState.collect { uVState ->
|
||||||
|
when (uVState) {
|
||||||
|
is UserVerificationViewModel.UVState.Loading -> {}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationCanceled -> {
|
||||||
|
coordinatorLayout?.showError(uVState.error)
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationSucceeded -> {
|
||||||
|
val data = uVState.dataToVerify
|
||||||
|
when (data.actionType) {
|
||||||
|
UserVerificationActionType.SHOW_PROTECTED_FIELD -> {
|
||||||
|
// Unprotect field by its view
|
||||||
|
data.protectedFieldView?.unprotect()
|
||||||
|
}
|
||||||
|
UserVerificationActionType.COPY_PROTECTED_FIELD -> {
|
||||||
|
// Copy field value
|
||||||
|
data.field?.let {
|
||||||
|
mEntryViewModel.copyToClipboard(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UserVerificationActionType.EDIT_ENTRY -> {
|
||||||
|
// Edit Entry
|
||||||
|
editEntry(data.database, data.entryId)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun finishActivityIfReloadRequested(): Boolean {
|
override fun finishActivityIfReloadRequested(): Boolean {
|
||||||
@@ -408,13 +479,13 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
super.onCreateOptionsMenu(menu)
|
super.onCreateOptionsMenu(menu)
|
||||||
if (mEntryLoaded) {
|
if (mEntryViewModel.entryLoaded) {
|
||||||
val inflater = menuInflater
|
val inflater = menuInflater
|
||||||
|
|
||||||
inflater.inflate(R.menu.entry, menu)
|
inflater.inflate(R.menu.entry, menu)
|
||||||
inflater.inflate(R.menu.database, menu)
|
inflater.inflate(R.menu.database, menu)
|
||||||
|
|
||||||
if (mEntryIsHistory && !mDatabaseReadOnly) {
|
if (mEntryViewModel.entryIsHistory && !mDatabaseReadOnly) {
|
||||||
inflater.inflate(R.menu.entry_history, menu)
|
inflater.inflate(R.menu.entry_history, menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +498,7 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
||||||
if (mEntryIsHistory || mDatabaseReadOnly) {
|
if (mEntryViewModel.entryIsHistory || mDatabaseReadOnly) {
|
||||||
menu?.findItem(R.id.menu_save_database)?.isVisible = false
|
menu?.findItem(R.id.menu_save_database)?.isVisible = false
|
||||||
menu?.findItem(R.id.menu_merge_database)?.isVisible = false
|
menu?.findItem(R.id.menu_merge_database)?.isVisible = false
|
||||||
menu?.findItem(R.id.menu_edit)?.isVisible = false
|
menu?.findItem(R.id.menu_edit)?.isVisible = false
|
||||||
@@ -472,34 +543,53 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun editEntry(database: ContextualDatabase?, entryId: NodeId<*>?) {
|
||||||
|
database?.let { database ->
|
||||||
|
entryId?.let { entryId ->
|
||||||
|
EntryEditActivity.launch(
|
||||||
|
activity = this@EntryActivity,
|
||||||
|
database = database,
|
||||||
|
registrationType = EntryEditActivity.RegistrationType.UPDATE,
|
||||||
|
nodeId = entryId,
|
||||||
|
activityResultLauncher = mEntryActivityResultLauncher
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.menu_edit -> {
|
R.id.menu_edit -> {
|
||||||
mDatabase?.let { database ->
|
if (mEntryViewModel.entryInfo?.isUserVerificationNeeded() == true) {
|
||||||
mMainEntryId?.let { entryId ->
|
mDatabase?.let { database ->
|
||||||
EntryEditActivity.launch(
|
checkUserVerification(
|
||||||
activity = this,
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
database = database,
|
dataToVerify = UserVerificationData(
|
||||||
registrationType = EntryEditActivity.RegistrationType.UPDATE,
|
actionType = UserVerificationActionType.EDIT_ENTRY,
|
||||||
nodeId = entryId,
|
database = database,
|
||||||
activityResultLauncher = mEntryActivityResultLauncher
|
entryId = mEntryViewModel.mainEntryId
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
editEntry(mDatabase, mEntryViewModel.mainEntryId)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.menu_restore_entry_history -> {
|
R.id.menu_restore_entry_history -> {
|
||||||
mMainEntryId?.let { mainEntryId ->
|
mEntryViewModel.mainEntryId?.let { mainEntryId ->
|
||||||
restoreEntryHistory(
|
restoreEntryHistory(
|
||||||
mainEntryId,
|
mainEntryId,
|
||||||
mHistoryPosition)
|
mEntryViewModel.historyPosition
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.menu_delete_entry_history -> {
|
R.id.menu_delete_entry_history -> {
|
||||||
mMainEntryId?.let { mainEntryId ->
|
mEntryViewModel.mainEntryId?.let { mainEntryId ->
|
||||||
deleteEntryHistory(
|
deleteEntryHistory(
|
||||||
mainEntryId,
|
mainEntryId,
|
||||||
mHistoryPosition)
|
mEntryViewModel.historyPosition
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.menu_save_database -> {
|
R.id.menu_save_database -> {
|
||||||
@@ -519,7 +609,7 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
override fun finish() {
|
override fun finish() {
|
||||||
// Transit data in previous Activity after an update
|
// Transit data in previous Activity after an update
|
||||||
Intent().apply {
|
Intent().apply {
|
||||||
putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mMainEntryId)
|
putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntryViewModel.mainEntryId)
|
||||||
setResult(RESULT_OK, this)
|
setResult(RESULT_OK, this)
|
||||||
}
|
}
|
||||||
super.finish()
|
super.finish()
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.buildSpecia
|
|||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveRegisterInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveRegisterInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationActionType
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.requestShowUnprotectField
|
||||||
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.buildPasskeyResponseAndSetResult
|
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.buildPasskeyResponseAndSetResult
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
@@ -99,9 +100,11 @@ import com.kunzisoft.keepass.view.asError
|
|||||||
import com.kunzisoft.keepass.view.hideByFading
|
import com.kunzisoft.keepass.view.hideByFading
|
||||||
import com.kunzisoft.keepass.view.setTransparentNavigationBar
|
import com.kunzisoft.keepass.view.setTransparentNavigationBar
|
||||||
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
||||||
|
import com.kunzisoft.keepass.view.showError
|
||||||
import com.kunzisoft.keepass.view.updateLockPaddingStart
|
import com.kunzisoft.keepass.view.updateLockPaddingStart
|
||||||
import com.kunzisoft.keepass.viewmodels.ColorPickerViewModel
|
import com.kunzisoft.keepass.viewmodels.ColorPickerViewModel
|
||||||
import com.kunzisoft.keepass.viewmodels.EntryEditViewModel
|
import com.kunzisoft.keepass.viewmodels.EntryEditViewModel
|
||||||
|
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.EnumSet
|
import java.util.EnumSet
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@@ -130,6 +133,7 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
private var mTemplatesSelectorAdapter: TemplatesSelectorAdapter? = null
|
private var mTemplatesSelectorAdapter: TemplatesSelectorAdapter? = null
|
||||||
|
|
||||||
private val mColorPickerViewModel: ColorPickerViewModel by viewModels()
|
private val mColorPickerViewModel: ColorPickerViewModel by viewModels()
|
||||||
|
private val mUserVerificationViewModel: UserVerificationViewModel by viewModels()
|
||||||
|
|
||||||
private var mAllowCustomFields = false
|
private var mAllowCustomFields = false
|
||||||
private var mAllowOTP = false
|
private var mAllowOTP = false
|
||||||
@@ -158,6 +162,8 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_entry_edit)
|
setContentView(R.layout.activity_entry_edit)
|
||||||
@@ -382,23 +388,52 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
mEntryEditViewModel.uiState.collect { uiState ->
|
mEntryEditViewModel.entryEditState.collect { uiState ->
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
EntryEditViewModel.UIState.Loading -> {}
|
is EntryEditViewModel.EntryEditState.Loading -> {}
|
||||||
EntryEditViewModel.UIState.ShowOverwriteMessage -> {
|
is EntryEditViewModel.EntryEditState.ShowOverwriteMessage -> {
|
||||||
if (mEntryEditViewModel.warningOverwriteDataAlreadyApproved.not()) {
|
AlertDialog.Builder(this@EntryEditActivity)
|
||||||
AlertDialog.Builder(this@EntryEditActivity)
|
.setTitle(R.string.warning_overwrite_data_title)
|
||||||
.setTitle(R.string.warning_overwrite_data_title)
|
.setMessage(R.string.warning_overwrite_data_description)
|
||||||
.setMessage(R.string.warning_overwrite_data_description)
|
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
mEntryEditViewModel.backPressedAlreadyApproved = true
|
||||||
mEntryEditViewModel.backPressedAlreadyApproved = true
|
onCancelSpecialMode()
|
||||||
onCancelSpecialMode()
|
}
|
||||||
}
|
.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.create().show()
|
||||||
mEntryEditViewModel.warningOverwriteDataAlreadyApproved = true
|
mEntryEditViewModel.actionPerformed()
|
||||||
}
|
}
|
||||||
.create().show()
|
is EntryEditViewModel.EntryEditState.RequestUnprotectField -> {
|
||||||
|
mDatabase?.let { database ->
|
||||||
|
requestShowUnprotectField(
|
||||||
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
|
database = database,
|
||||||
|
protectedFieldView = uiState.protectedFieldView
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
mEntryEditViewModel.actionPerformed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
mUserVerificationViewModel.userVerificationState.collect { uVState ->
|
||||||
|
when (uVState) {
|
||||||
|
is UserVerificationViewModel.UVState.Loading -> {}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationCanceled -> {
|
||||||
|
coordinatorLayout?.showError(uVState.error)
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationSucceeded -> {
|
||||||
|
when (uVState.dataToVerify.actionType) {
|
||||||
|
UserVerificationActionType.SHOW_PROTECTED_FIELD -> {
|
||||||
|
uVState.dataToVerify.protectedFieldView?.unprotect()
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,7 +482,7 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
searchAction = {
|
searchAction = {
|
||||||
// Nothing when search retrieved
|
// Nothing when search retrieved
|
||||||
},
|
},
|
||||||
selectionAction = { intentSender, typeMode, searchInfo ->
|
selectionAction = { _, typeMode, _ ->
|
||||||
when(typeMode) {
|
when(typeMode) {
|
||||||
TypeMode.DEFAULT -> {}
|
TypeMode.DEFAULT -> {}
|
||||||
TypeMode.MAGIKEYBOARD ->
|
TypeMode.MAGIKEYBOARD ->
|
||||||
@@ -486,14 +521,12 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun entryValidatedForKeyboardSelection(database: ContextualDatabase, entry: Entry) {
|
private fun entryValidatedForKeyboardSelection(database: ContextualDatabase, entry: Entry) {
|
||||||
// Populate Magikeyboard with entry
|
// Build Magikeyboard response with the entry selected
|
||||||
MagikeyboardService.populateKeyboardAndMoveAppToBackground(
|
this.buildSpecialModeResponseAndSetResult(
|
||||||
this,
|
entryInfo = entry.getEntryInfo(database),
|
||||||
entry.getEntryInfo(database)
|
extras = buildEntryResult(entry)
|
||||||
)
|
)
|
||||||
onValidateSpecialMode()
|
onValidateSpecialMode()
|
||||||
// Don't keep activity history for entry edition
|
|
||||||
finishForEntryResult(entry)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun entryValidatedForAutofill(database: ContextualDatabase, entry: Entry) {
|
private fun entryValidatedForAutofill(database: ContextualDatabase, entry: Entry) {
|
||||||
|
|||||||
@@ -55,10 +55,8 @@ import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
|
|||||||
import com.kunzisoft.keepass.hardware.HardwareKey
|
import com.kunzisoft.keepass.hardware.HardwareKey
|
||||||
import com.kunzisoft.keepass.model.RegisterInfo
|
import com.kunzisoft.keepass.model.RegisterInfo
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
|
|
||||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_TASK
|
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_TASK
|
||||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
|
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
|
||||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.DATABASE_URI_KEY
|
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.utils.AppUtil.isContributingUser
|
import com.kunzisoft.keepass.utils.AppUtil.isContributingUser
|
||||||
@@ -94,6 +92,8 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
|
|||||||
|
|
||||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -214,6 +214,12 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
|
|||||||
// Retrieve settings for default database
|
// Retrieve settings for default database
|
||||||
mAdapterDatabaseHistory?.setDefaultDatabase(it)
|
mAdapterDatabaseHistory?.setDefaultDatabase(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove all the remember locations if needed
|
||||||
|
if (PreferencesUtil.rememberDatabaseLocations(applicationContext).not()) {
|
||||||
|
FileDatabaseHistoryAction.getInstance(applicationContext)
|
||||||
|
.deleteAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDatabaseRetrieved(database: ContextualDatabase) {
|
override fun onDatabaseRetrieved(database: ContextualDatabase) {
|
||||||
@@ -226,22 +232,6 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
|
|||||||
result: ActionRunnable.Result
|
result: ActionRunnable.Result
|
||||||
) {
|
) {
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
// Update list
|
|
||||||
when (actionTask) {
|
|
||||||
ACTION_DATABASE_CREATE_TASK,
|
|
||||||
ACTION_DATABASE_LOAD_TASK -> {
|
|
||||||
result.data?.getParcelableCompat<Uri>(DATABASE_URI_KEY)?.let { databaseUri ->
|
|
||||||
val mainCredential =
|
|
||||||
result.data?.getParcelableCompat(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY)
|
|
||||||
?: MainCredential()
|
|
||||||
databaseFilesViewModel.addDatabaseFile(
|
|
||||||
databaseUri,
|
|
||||||
mainCredential.keyFileUri,
|
|
||||||
mainCredential.hardwareKey
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Launch activity
|
// Launch activity
|
||||||
when (actionTask) {
|
when (actionTask) {
|
||||||
ACTION_DATABASE_CREATE_TASK -> {
|
ACTION_DATABASE_CREATE_TASK -> {
|
||||||
@@ -250,13 +240,13 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
|
|||||||
database,
|
database,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
coordinatorLayout.showActionErrorIfNeeded(result)
|
||||||
}
|
}
|
||||||
ACTION_DATABASE_LOAD_TASK -> {
|
ACTION_DATABASE_LOAD_TASK -> {
|
||||||
launchGroupActivityIfLoaded(database)
|
launchGroupActivityIfLoaded(database)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
coordinatorLayout.showActionErrorIfNeeded(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -467,7 +457,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
|
|||||||
* -------------------------
|
* -------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun launchForSearchResult(
|
fun launchForSearch(
|
||||||
context: Context,
|
context: Context,
|
||||||
searchInfo: SearchInfo
|
searchInfo: SearchInfo
|
||||||
) {
|
) {
|
||||||
@@ -488,7 +478,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
|
|||||||
context: Context,
|
context: Context,
|
||||||
typeMode: TypeMode,
|
typeMode: TypeMode,
|
||||||
searchInfo: SearchInfo? = null,
|
searchInfo: SearchInfo? = null,
|
||||||
activityResultLauncher: ActivityResultLauncher<Intent>? = null,
|
activityResultLauncher: ActivityResultLauncher<Intent>?,
|
||||||
) {
|
) {
|
||||||
EntrySelectionHelper.startActivityForSelectionModeResult(
|
EntrySelectionHelper.startActivityForSelectionModeResult(
|
||||||
context = context,
|
context = context,
|
||||||
@@ -512,10 +502,10 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
|
|||||||
) {
|
) {
|
||||||
EntrySelectionHelper.startActivityForRegistrationModeResult(
|
EntrySelectionHelper.startActivityForRegistrationModeResult(
|
||||||
context = context,
|
context = context,
|
||||||
activityResultLauncher = activityResultLauncher,
|
|
||||||
intent = Intent(context, FileDatabaseSelectActivity::class.java),
|
intent = Intent(context, FileDatabaseSelectActivity::class.java),
|
||||||
registerInfo = registerInfo,
|
registerInfo = registerInfo,
|
||||||
typeMode = typeMode
|
typeMode = typeMode,
|
||||||
|
activityResultLauncher = activityResultLauncher
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.activities
|
package com.kunzisoft.keepass.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -41,7 +42,6 @@ import android.widget.TextView
|
|||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
@@ -52,6 +52,9 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.datepicker.MaterialDatePicker
|
import com.google.android.material.datepicker.MaterialDatePicker
|
||||||
import com.google.android.material.timepicker.MaterialTimePicker
|
import com.google.android.material.timepicker.MaterialTimePicker
|
||||||
@@ -72,10 +75,13 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.removeModes
|
|||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationActionType
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.isUserVerificationNeeded
|
||||||
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
||||||
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.buildPasskeyResponseAndSetResult
|
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.buildPasskeyResponseAndSetResult
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.MainCredential
|
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
import com.kunzisoft.keepass.database.element.Entry
|
import com.kunzisoft.keepass.database.element.Entry
|
||||||
import com.kunzisoft.keepass.database.element.Group
|
import com.kunzisoft.keepass.database.element.Group
|
||||||
@@ -117,10 +123,14 @@ import com.kunzisoft.keepass.view.applyWindowInsets
|
|||||||
import com.kunzisoft.keepass.view.hideByFading
|
import com.kunzisoft.keepass.view.hideByFading
|
||||||
import com.kunzisoft.keepass.view.setTransparentNavigationBar
|
import com.kunzisoft.keepass.view.setTransparentNavigationBar
|
||||||
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
||||||
|
import com.kunzisoft.keepass.view.showError
|
||||||
import com.kunzisoft.keepass.view.toastError
|
import com.kunzisoft.keepass.view.toastError
|
||||||
import com.kunzisoft.keepass.view.updateLockPaddingStart
|
import com.kunzisoft.keepass.view.updateLockPaddingStart
|
||||||
import com.kunzisoft.keepass.viewmodels.GroupEditViewModel
|
import com.kunzisoft.keepass.viewmodels.GroupEditViewModel
|
||||||
import com.kunzisoft.keepass.viewmodels.GroupViewModel
|
import com.kunzisoft.keepass.viewmodels.GroupViewModel
|
||||||
|
import com.kunzisoft.keepass.viewmodels.MainCredentialViewModel
|
||||||
|
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.joda.time.LocalDateTime
|
import org.joda.time.LocalDateTime
|
||||||
import java.util.EnumSet
|
import java.util.EnumSet
|
||||||
|
|
||||||
@@ -130,8 +140,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
GroupFragment.NodesActionMenuListener,
|
GroupFragment.NodesActionMenuListener,
|
||||||
GroupFragment.OnScrollListener,
|
GroupFragment.OnScrollListener,
|
||||||
GroupFragment.GroupRefreshedListener,
|
GroupFragment.GroupRefreshedListener,
|
||||||
SortDialogFragment.SortSelectionListener,
|
SortDialogFragment.SortSelectionListener {
|
||||||
MainCredentialDialogFragment.AskMainCredentialDialogListener {
|
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
private var header: ViewGroup? = null
|
private var header: ViewGroup? = null
|
||||||
@@ -156,6 +165,8 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
|
|
||||||
private val mGroupViewModel: GroupViewModel by viewModels()
|
private val mGroupViewModel: GroupViewModel by viewModels()
|
||||||
private val mGroupEditViewModel: GroupEditViewModel by viewModels()
|
private val mGroupEditViewModel: GroupEditViewModel by viewModels()
|
||||||
|
private val mMainCredentialViewModel: MainCredentialViewModel by viewModels()
|
||||||
|
private val mUserVerificationViewModel: UserVerificationViewModel by viewModels()
|
||||||
|
|
||||||
private val mGroupActivityEducation = GroupActivityEducation(this)
|
private val mGroupActivityEducation = GroupActivityEducation(this)
|
||||||
|
|
||||||
@@ -274,6 +285,8 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
mGroupEditViewModel.selectIcon(icon)
|
mGroupEditViewModel.selectIcon(icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -354,14 +367,22 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
SettingsActivity.launch(this@GroupActivity, true)
|
SettingsActivity.launch(this@GroupActivity, true)
|
||||||
}
|
}
|
||||||
R.id.menu_merge_from -> {
|
R.id.menu_merge_from -> {
|
||||||
mExternalFileHelper?.openDocument()
|
checkUserVerification(
|
||||||
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
|
dataToVerify = UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.MERGE_FROM_DATABASE,
|
||||||
|
database = mDatabase
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
R.id.menu_save_copy_to -> {
|
R.id.menu_save_copy_to -> {
|
||||||
mExternalFileHelper?.createDocument(
|
checkUserVerification(
|
||||||
getString(R.string.database_file_name_default) +
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
"_" +
|
dataToVerify = UserVerificationData(
|
||||||
LocalDateTime.now().toString() +
|
actionType = UserVerificationActionType.SAVE_DATABASE_COPY_TO,
|
||||||
mDatabase?.defaultFileExtension)
|
database = mDatabase
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
R.id.menu_lock_all -> {
|
R.id.menu_lock_all -> {
|
||||||
lockAndExit()
|
lockAndExit()
|
||||||
@@ -546,6 +567,58 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
mMainCredentialViewModel.uiState.collect { credentialState ->
|
||||||
|
when (credentialState) {
|
||||||
|
is MainCredentialViewModel.UIState.Loading -> {}
|
||||||
|
is MainCredentialViewModel.UIState.OnMainCredentialEntered -> {
|
||||||
|
mergeDatabaseFrom(credentialState.databaseUri, credentialState.mainCredential)
|
||||||
|
mMainCredentialViewModel.onActionReceived()
|
||||||
|
}
|
||||||
|
is MainCredentialViewModel.UIState.OnMainCredentialCanceled -> {
|
||||||
|
mMainCredentialViewModel.onActionReceived()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
mUserVerificationViewModel.userVerificationState.collect { uVState ->
|
||||||
|
when (uVState) {
|
||||||
|
is UserVerificationViewModel.UVState.Loading -> {}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationCanceled -> {
|
||||||
|
coordinatorLayout?.showError(uVState.error)
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationSucceeded -> {
|
||||||
|
val data = uVState.dataToVerify
|
||||||
|
when (data.actionType) {
|
||||||
|
UserVerificationActionType.EDIT_ENTRY -> {
|
||||||
|
editEntry(uVState.dataToVerify.database, uVState.dataToVerify.entryId)
|
||||||
|
}
|
||||||
|
UserVerificationActionType.MERGE_FROM_DATABASE -> {
|
||||||
|
mExternalFileHelper?.openDocument()
|
||||||
|
}
|
||||||
|
UserVerificationActionType.SAVE_DATABASE_COPY_TO -> {
|
||||||
|
mExternalFileHelper?.createDocument(
|
||||||
|
getString(R.string.database_file_name_default) +
|
||||||
|
"_" +
|
||||||
|
LocalDateTime.now().toString() +
|
||||||
|
uVState.dataToVerify.database?.defaultFileExtension
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun viewToInvalidateTimeout(): View? {
|
override fun viewToInvalidateTimeout(): View? {
|
||||||
@@ -666,14 +739,6 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
result: ActionRunnable.Result
|
result: ActionRunnable.Result
|
||||||
) {
|
) {
|
||||||
super.onDatabaseActionFinished(database, actionTask, result)
|
super.onDatabaseActionFinished(database, actionTask, result)
|
||||||
|
|
||||||
var entry: Entry? = null
|
|
||||||
try {
|
|
||||||
entry = result.data?.getNewEntry(database)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Unable to retrieve entry action for selection", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
when (actionTask) {
|
when (actionTask) {
|
||||||
ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
|
ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
@@ -685,7 +750,13 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
searchAction = {
|
searchAction = {
|
||||||
// Search not used
|
// Search not used
|
||||||
},
|
},
|
||||||
selectionAction = { intentSenderMode, typeMode, searchInfo ->
|
selectionAction = { _, typeMode, _ ->
|
||||||
|
var entry: Entry? = null
|
||||||
|
try {
|
||||||
|
entry = result.data?.getNewEntry(database)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to retrieve entry action for selection", e)
|
||||||
|
}
|
||||||
when (typeMode) {
|
when (typeMode) {
|
||||||
TypeMode.DEFAULT -> {}
|
TypeMode.DEFAULT -> {}
|
||||||
TypeMode.MAGIKEYBOARD -> entry?.let {
|
TypeMode.MAGIKEYBOARD -> entry?.let {
|
||||||
@@ -699,19 +770,17 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
registrationAction = { intentSenderMode, typeMode, searchInfo ->
|
registrationAction = { _, _, _ ->
|
||||||
// Save not used
|
// Save not used
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
coordinatorError?.showActionErrorIfNeeded(result)
|
||||||
|
// Reload the group
|
||||||
|
loadGroup()
|
||||||
|
finishNodeAction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coordinatorError?.showActionErrorIfNeeded(result)
|
|
||||||
|
|
||||||
// Reload the group
|
|
||||||
loadGroup()
|
|
||||||
finishNodeAction()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun manageIntent(intent: Intent?) {
|
private fun manageIntent(intent: Intent?) {
|
||||||
@@ -839,7 +908,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
// Open child group
|
// Open child group
|
||||||
loadMainGroup(GroupState(group.nodeId, 0))
|
loadMainGroup(GroupState(group.nodeId, 0))
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
Log.e(TAG, "Node can't be cast in Group")
|
Log.e(TAG, "Node can't be cast in Group", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
Type.ENTRY -> try {
|
Type.ENTRY -> try {
|
||||||
@@ -858,21 +927,23 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
searchAction = {
|
searchAction = {
|
||||||
// Nothing here, a search is simply performed
|
// Nothing here, a search is simply performed
|
||||||
},
|
},
|
||||||
selectionAction = { intentSenderMode, typeMode, searchInfo ->
|
selectionAction = { _, typeMode, searchInfo ->
|
||||||
when (typeMode) {
|
when (typeMode) {
|
||||||
TypeMode.DEFAULT -> {}
|
TypeMode.DEFAULT -> {}
|
||||||
TypeMode.MAGIKEYBOARD -> {
|
TypeMode.MAGIKEYBOARD -> {
|
||||||
if (!database.isReadOnly
|
if (!database.isReadOnly
|
||||||
&& searchInfo != null
|
&& searchInfo != null
|
||||||
&& PreferencesUtil.isKeyboardSaveSearchInfoEnable(this@GroupActivity)
|
&& PreferencesUtil.isKeyboardSaveSearchInfoEnable(this@GroupActivity)
|
||||||
|
&& entryVersioned.containsSearchInfo(database, searchInfo).not()
|
||||||
) {
|
) {
|
||||||
updateEntryWithRegisterInfo(
|
updateEntryWithRegisterInfo(
|
||||||
database,
|
database,
|
||||||
entryVersioned,
|
entryVersioned,
|
||||||
searchInfo.toRegisterInfo()
|
searchInfo.toRegisterInfo()
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
entrySelectedForKeyboardSelection(database, entryVersioned)
|
||||||
}
|
}
|
||||||
entrySelectedForKeyboardSelection(database, entryVersioned)
|
|
||||||
}
|
}
|
||||||
TypeMode.PASSKEY -> {
|
TypeMode.PASSKEY -> {
|
||||||
entrySelectedForPasskeySelection(database, entryVersioned)
|
entrySelectedForPasskeySelection(database, entryVersioned)
|
||||||
@@ -881,14 +952,16 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
if (!database.isReadOnly
|
if (!database.isReadOnly
|
||||||
&& searchInfo != null
|
&& searchInfo != null
|
||||||
&& PreferencesUtil.isAutofillSaveSearchInfoEnable(this@GroupActivity)
|
&& PreferencesUtil.isAutofillSaveSearchInfoEnable(this@GroupActivity)
|
||||||
|
&& entryVersioned.containsSearchInfo(database, searchInfo).not()
|
||||||
) {
|
) {
|
||||||
updateEntryWithRegisterInfo(
|
updateEntryWithRegisterInfo(
|
||||||
database,
|
database,
|
||||||
entryVersioned,
|
entryVersioned,
|
||||||
searchInfo.toRegisterInfo()
|
searchInfo.toRegisterInfo()
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
entrySelectedForAutofillSelection(database, entryVersioned)
|
||||||
}
|
}
|
||||||
entrySelectedForAutofillSelection(database, entryVersioned)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadGroup()
|
loadGroup()
|
||||||
@@ -908,18 +981,15 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
finish()
|
finish()
|
||||||
})
|
})
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
Log.e(TAG, "Node can't be cast in Entry")
|
Log.e(TAG, "Node can't be cast in Entry", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun entrySelectedForKeyboardSelection(database: ContextualDatabase, entry: Entry) {
|
private fun entrySelectedForKeyboardSelection(database: ContextualDatabase, entry: Entry) {
|
||||||
removeSearch()
|
removeSearch()
|
||||||
// Populate Magikeyboard with entry
|
// Build response with the entry selected
|
||||||
MagikeyboardService.populateKeyboardAndMoveAppToBackground(
|
this.buildSpecialModeResponseAndSetResult(entry.getEntryInfo(database))
|
||||||
this,
|
|
||||||
entry.getEntryInfo(database)
|
|
||||||
)
|
|
||||||
onValidateSpecialMode()
|
onValidateSpecialMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -980,6 +1050,31 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
updateEntry(entry, newEntry)
|
updateEntry(entry, newEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Entry.containsSearchInfo(
|
||||||
|
database: ContextualDatabase,
|
||||||
|
searchInfo: SearchInfo
|
||||||
|
): Boolean {
|
||||||
|
return getEntryInfo(
|
||||||
|
database,
|
||||||
|
raw = true,
|
||||||
|
removeTemplateConfiguration = false
|
||||||
|
).containsSearchInfo(searchInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun editEntry(database: ContextualDatabase?, entryId: NodeId<*>?) {
|
||||||
|
database?.let {
|
||||||
|
entryId?.let {
|
||||||
|
EntryEditActivity.launch(
|
||||||
|
activity = this@GroupActivity,
|
||||||
|
database = database,
|
||||||
|
registrationType = EntryEditActivity.RegistrationType.UPDATE,
|
||||||
|
nodeId = entryId,
|
||||||
|
activityResultLauncher = mEntryActivityResultLauncher
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun finishNodeAction() {
|
private fun finishNodeAction() {
|
||||||
actionNodeMode?.finish()
|
actionNodeMode?.finish()
|
||||||
}
|
}
|
||||||
@@ -1029,13 +1124,18 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
launchDialogForGroupUpdate(node as Group)
|
launchDialogForGroupUpdate(node as Group)
|
||||||
}
|
}
|
||||||
Type.ENTRY -> {
|
Type.ENTRY -> {
|
||||||
EntryEditActivity.launch(
|
if ((node as Entry).getEntryInfo(database).isUserVerificationNeeded()) {
|
||||||
activity = this@GroupActivity,
|
checkUserVerification(
|
||||||
database = database,
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
registrationType = EntryEditActivity.RegistrationType.UPDATE,
|
dataToVerify = UserVerificationData(
|
||||||
nodeId = (node as Entry).nodeId,
|
actionType = UserVerificationActionType.EDIT_ENTRY,
|
||||||
activityResultLauncher = mEntryActivityResultLauncher
|
database = database,
|
||||||
)
|
entryId = node.nodeId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
editEntry(database, node.nodeId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -1119,20 +1219,6 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAskMainCredentialDialogPositiveClick(
|
|
||||||
databaseUri: Uri?,
|
|
||||||
mainCredential: MainCredential
|
|
||||||
) {
|
|
||||||
databaseUri?.let {
|
|
||||||
mergeDatabaseFrom(it, mainCredential)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAskMainCredentialDialogNegativeClick(
|
|
||||||
databaseUri: Uri?,
|
|
||||||
mainCredential: MainCredential
|
|
||||||
) { }
|
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
@@ -1521,7 +1607,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
* Search Launch
|
* Search Launch
|
||||||
* -------------------------
|
* -------------------------
|
||||||
*/
|
*/
|
||||||
fun launchForSearchResult(
|
fun launchForSearch(
|
||||||
context: Context,
|
context: Context,
|
||||||
database: ContextualDatabase,
|
database: ContextualDatabase,
|
||||||
searchInfo: SearchInfo,
|
searchInfo: SearchInfo,
|
||||||
@@ -1536,13 +1622,18 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -------------------------
|
||||||
|
* Selection Launch
|
||||||
|
* -------------------------
|
||||||
|
*/
|
||||||
fun launchForSelection(
|
fun launchForSelection(
|
||||||
context: Context,
|
context: Context,
|
||||||
database: ContextualDatabase,
|
database: ContextualDatabase,
|
||||||
typeMode: TypeMode,
|
typeMode: TypeMode,
|
||||||
searchInfo: SearchInfo? = null,
|
searchInfo: SearchInfo? = null,
|
||||||
autoSearch: Boolean = false,
|
autoSearch: Boolean = false,
|
||||||
activityResultLauncher: ActivityResultLauncher<Intent>? = null,
|
activityResultLauncher: ActivityResultLauncher<Intent>?,
|
||||||
) {
|
) {
|
||||||
if (database.loaded) {
|
if (database.loaded) {
|
||||||
checkTimeAndBuildIntent(context, null) { intent ->
|
checkTimeAndBuildIntent(context, null) { intent ->
|
||||||
@@ -1590,7 +1681,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
* -------------------------
|
* -------------------------
|
||||||
*/
|
*/
|
||||||
fun launch(
|
fun launch(
|
||||||
activity: AppCompatActivity,
|
activity: Activity,
|
||||||
database: ContextualDatabase,
|
database: ContextualDatabase,
|
||||||
onValidateSpecialMode: () -> Unit,
|
onValidateSpecialMode: () -> Unit,
|
||||||
onCancelSpecialMode: () -> Unit,
|
onCancelSpecialMode: () -> Unit,
|
||||||
@@ -1610,7 +1701,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
searchAction = { searchInfo ->
|
searchAction = { searchInfo ->
|
||||||
// Search action
|
// Search action
|
||||||
if (database.loaded) {
|
if (database.loaded) {
|
||||||
launchForSearchResult(activity,
|
launchForSearch(activity,
|
||||||
database,
|
database,
|
||||||
searchInfo,
|
searchInfo,
|
||||||
true)
|
true)
|
||||||
@@ -1625,18 +1716,14 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
context = activity,
|
context = activity,
|
||||||
database = database,
|
database = database,
|
||||||
searchInfo = searchInfo,
|
searchInfo = searchInfo,
|
||||||
onItemsFound = { openedDatabase, items ->
|
onItemsFound = { _, items ->
|
||||||
when (typeMode) {
|
when (typeMode) {
|
||||||
TypeMode.DEFAULT -> {}
|
TypeMode.DEFAULT -> {}
|
||||||
TypeMode.MAGIKEYBOARD -> {
|
TypeMode.MAGIKEYBOARD -> {
|
||||||
MagikeyboardService.performSelection(
|
MagikeyboardService.performSelection(
|
||||||
items = items,
|
items = items,
|
||||||
actionPopulateKeyboard = { entryInfo ->
|
actionPopulateKeyboard = { _ ->
|
||||||
// Keyboard populated
|
activity.buildSpecialModeResponseAndSetResult(items)
|
||||||
MagikeyboardService.populateKeyboardAndMoveAppToBackground(
|
|
||||||
activity,
|
|
||||||
entryInfo
|
|
||||||
)
|
|
||||||
onValidateSpecialMode()
|
onValidateSpecialMode()
|
||||||
},
|
},
|
||||||
actionEntrySelection = { autoSearch ->
|
actionEntrySelection = { autoSearch ->
|
||||||
@@ -1645,6 +1732,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
database = database,
|
database = database,
|
||||||
typeMode = TypeMode.MAGIKEYBOARD,
|
typeMode = TypeMode.MAGIKEYBOARD,
|
||||||
searchInfo = searchInfo,
|
searchInfo = searchInfo,
|
||||||
|
activityResultLauncher = activityResultLauncher,
|
||||||
autoSearch = autoSearch
|
autoSearch = autoSearch
|
||||||
)
|
)
|
||||||
onLaunchActivitySpecialMode()
|
onLaunchActivitySpecialMode()
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ class IconPickerActivity : DatabaseLockActivity() {
|
|||||||
|
|
||||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ class ImageViewerActivity : DatabaseLockActivity() {
|
|||||||
private lateinit var imageView: ImageView
|
private lateinit var imageView: ImageView
|
||||||
private lateinit var progressView: View
|
private lateinit var progressView: View
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = false
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ class KeyGeneratorActivity : DatabaseLockActivity() {
|
|||||||
private lateinit var validationButton: View
|
private lateinit var validationButton: View
|
||||||
private var lockView: View? = null
|
private var lockView: View? = null
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = true
|
||||||
|
|
||||||
private val keyGeneratorViewModel: KeyGeneratorViewModel by viewModels()
|
private val keyGeneratorViewModel: KeyGeneratorViewModel by viewModels()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import android.widget.TextView
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.biometric.BiometricManager
|
import androidx.biometric.BiometricManager
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
@@ -125,6 +124,8 @@ class MainCredentialActivity : DatabaseModeActivity() {
|
|||||||
private var mReadOnly: Boolean = false
|
private var mReadOnly: Boolean = false
|
||||||
private var mForceReadOnly: Boolean = false
|
private var mForceReadOnly: Boolean = false
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -803,13 +804,13 @@ class MainCredentialActivity : DatabaseModeActivity() {
|
|||||||
|
|
||||||
@Throws(FileNotFoundException::class)
|
@Throws(FileNotFoundException::class)
|
||||||
fun launchForSelection(
|
fun launchForSelection(
|
||||||
activity: AppCompatActivity,
|
activity: Activity,
|
||||||
databaseFile: Uri,
|
databaseFile: Uri,
|
||||||
keyFile: Uri?,
|
keyFile: Uri?,
|
||||||
hardwareKey: HardwareKey?,
|
hardwareKey: HardwareKey?,
|
||||||
typeMode: TypeMode,
|
typeMode: TypeMode,
|
||||||
searchInfo: SearchInfo?,
|
searchInfo: SearchInfo?,
|
||||||
activityResultLauncher: ActivityResultLauncher<Intent>? = null,
|
activityResultLauncher: ActivityResultLauncher<Intent>?
|
||||||
) {
|
) {
|
||||||
buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
|
buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
|
||||||
EntrySelectionHelper.startActivityForSelectionModeResult(
|
EntrySelectionHelper.startActivityForSelectionModeResult(
|
||||||
@@ -831,20 +832,20 @@ class MainCredentialActivity : DatabaseModeActivity() {
|
|||||||
@Throws(FileNotFoundException::class)
|
@Throws(FileNotFoundException::class)
|
||||||
fun launchForRegistration(
|
fun launchForRegistration(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
activityResultLauncher: ActivityResultLauncher<Intent>?,
|
|
||||||
databaseFile: Uri,
|
databaseFile: Uri,
|
||||||
keyFile: Uri?,
|
keyFile: Uri?,
|
||||||
hardwareKey: HardwareKey?,
|
hardwareKey: HardwareKey?,
|
||||||
typeMode: TypeMode,
|
typeMode: TypeMode,
|
||||||
registerInfo: RegisterInfo?
|
registerInfo: RegisterInfo?,
|
||||||
|
activityResultLauncher: ActivityResultLauncher<Intent>?
|
||||||
) {
|
) {
|
||||||
buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
|
buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
|
||||||
EntrySelectionHelper.startActivityForRegistrationModeResult(
|
EntrySelectionHelper.startActivityForRegistrationModeResult(
|
||||||
context = activity,
|
context = activity,
|
||||||
activityResultLauncher = activityResultLauncher,
|
|
||||||
intent = intent,
|
intent = intent,
|
||||||
typeMode = typeMode,
|
typeMode = typeMode,
|
||||||
registerInfo = registerInfo
|
registerInfo = registerInfo,
|
||||||
|
activityResultLauncher = activityResultLauncher,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 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.os.Bundle
|
||||||
|
import android.text.InputFilter
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import com.kunzisoft.keepass.R
|
||||||
|
import com.kunzisoft.keepass.database.element.MasterCredential
|
||||||
|
import com.kunzisoft.keepass.utils.UriUtil.openUrl
|
||||||
|
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
|
||||||
|
|
||||||
|
|
||||||
|
class CheckDatabaseCredentialDialogFragment : DatabaseDialogFragment() {
|
||||||
|
|
||||||
|
private val userVerificationViewModel: UserVerificationViewModel by activityViewModels()
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
activity?.let { activity ->
|
||||||
|
val builder = AlertDialog.Builder(activity)
|
||||||
|
val inflater = activity.layoutInflater
|
||||||
|
val rootView = inflater.inflate(R.layout.fragment_check_database_credential, null)
|
||||||
|
val editText = rootView.findViewById<TextView>(R.id.setup_check_password_edit_text)
|
||||||
|
editText.filters = arrayOf<InputFilter>(InputFilter.LengthFilter(
|
||||||
|
MasterCredential.CHECK_KEY_PASSWORD_LENGTH)
|
||||||
|
)
|
||||||
|
builder.setView(rootView)
|
||||||
|
.setPositiveButton(R.string.check) { _, _ ->
|
||||||
|
userVerificationViewModel.checkMainCredential(
|
||||||
|
editText.text.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
|
userVerificationViewModel.onUserVerificationFailed()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
rootView.findViewById<View>(R.id.user_verification_information)?.setOnClickListener {
|
||||||
|
activity.openUrl(R.string.user_verification_explanation_url)
|
||||||
|
}
|
||||||
|
return builder.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onCreateDialog(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun getInstance(): CheckDatabaseCredentialDialogFragment {
|
||||||
|
val fragment = CheckDatabaseCredentialDialogFragment()
|
||||||
|
val args = Bundle()
|
||||||
|
fragment.arguments = args
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,45 +20,27 @@
|
|||||||
package com.kunzisoft.keepass.activities.dialogs
|
package com.kunzisoft.keepass.activities.dialogs
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||||
import com.kunzisoft.keepass.database.MainCredential
|
import com.kunzisoft.keepass.database.MainCredential
|
||||||
|
import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException
|
||||||
import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile
|
import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile
|
||||||
import com.kunzisoft.keepass.utils.getParcelableCompat
|
import com.kunzisoft.keepass.utils.getParcelableCompat
|
||||||
import com.kunzisoft.keepass.view.MainCredentialView
|
import com.kunzisoft.keepass.view.MainCredentialView
|
||||||
|
import com.kunzisoft.keepass.viewmodels.MainCredentialViewModel
|
||||||
|
|
||||||
class MainCredentialDialogFragment : DatabaseDialogFragment() {
|
class MainCredentialDialogFragment : DatabaseDialogFragment() {
|
||||||
|
|
||||||
private var mainCredentialView: MainCredentialView? = null
|
private var mainCredentialView: MainCredentialView? = null
|
||||||
|
|
||||||
private var mListener: AskMainCredentialDialogListener? = null
|
|
||||||
|
|
||||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||||
|
|
||||||
interface AskMainCredentialDialogListener {
|
private val mMainCredentialViewModel: MainCredentialViewModel by activityViewModels()
|
||||||
fun onAskMainCredentialDialogPositiveClick(databaseUri: Uri?, mainCredential: MainCredential)
|
|
||||||
fun onAskMainCredentialDialogNegativeClick(databaseUri: Uri?, mainCredential: MainCredential)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAttach(activity: Context) {
|
|
||||||
super.onAttach(activity)
|
|
||||||
try {
|
|
||||||
mListener = activity as AskMainCredentialDialogListener
|
|
||||||
} catch (e: ClassCastException) {
|
|
||||||
throw ClassCastException(activity.toString()
|
|
||||||
+ " must implement " + AskMainCredentialDialogListener::class.java.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetach() {
|
|
||||||
mListener = null
|
|
||||||
super.onDetach()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
activity?.let { activity ->
|
activity?.let { activity ->
|
||||||
@@ -76,30 +58,35 @@ class MainCredentialDialogFragment : DatabaseDialogFragment() {
|
|||||||
databaseUri?.let {
|
databaseUri?.let {
|
||||||
root.findViewById<TextView>(R.id.title_database)?.text =
|
root.findViewById<TextView>(R.id.title_database)?.text =
|
||||||
it.getDocumentFile(requireContext())?.name
|
it.getDocumentFile(requireContext())?.name
|
||||||
}
|
|
||||||
builder.setView(root)
|
builder.setView(root)
|
||||||
// Add action buttons
|
// Add action buttons
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
mListener?.onAskMainCredentialDialogPositiveClick(
|
mMainCredentialViewModel.validateMainCredential(
|
||||||
databaseUri,
|
databaseUri = databaseUri,
|
||||||
retrieveMainCredential()
|
mainCredential = retrieveMainCredential()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
mListener?.onAskMainCredentialDialogNegativeClick(
|
mMainCredentialViewModel.cancelMainCredential(
|
||||||
databaseUri,
|
databaseUri = databaseUri
|
||||||
retrieveMainCredential()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mExternalFileHelper = ExternalFileHelper(this)
|
||||||
mExternalFileHelper = ExternalFileHelper(this)
|
mExternalFileHelper?.buildOpenDocument { uri ->
|
||||||
mExternalFileHelper?.buildOpenDocument { uri ->
|
if (uri != null) {
|
||||||
if (uri != null) {
|
mainCredentialView?.populateKeyFileView(uri)
|
||||||
mainCredentialView?.populateKeyFileView(uri)
|
}
|
||||||
}
|
}
|
||||||
|
mainCredentialView?.setOpenKeyfileClickListener(mExternalFileHelper)
|
||||||
|
} ?: run {
|
||||||
|
mMainCredentialViewModel.cancelMainCredential(
|
||||||
|
databaseUri = null,
|
||||||
|
error = FileNotFoundDatabaseException()
|
||||||
|
)
|
||||||
|
dismissAllowingStateLoss()
|
||||||
}
|
}
|
||||||
mainCredentialView?.setOpenKeyfileClickListener(mExternalFileHelper)
|
|
||||||
|
|
||||||
return builder.create()
|
return builder.create()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,9 @@ class EntryEditFragment: DatabaseFragment() {
|
|||||||
setOnForegroundColorClickListener {
|
setOnForegroundColorClickListener {
|
||||||
mEntryEditViewModel.requestForegroundColorSelection(templateView.getForegroundColor())
|
mEntryEditViewModel.requestForegroundColorSelection(templateView.getForegroundColor())
|
||||||
}
|
}
|
||||||
|
setOnUnprotectClickListener { _, textEditFieldView ->
|
||||||
|
mEntryEditViewModel.requestUnprotectField(textEditFieldView)
|
||||||
|
}
|
||||||
setOnCustomEditionActionClickListener { field ->
|
setOnCustomEditionActionClickListener { field ->
|
||||||
mEntryEditViewModel.requestCustomFieldEdition(field)
|
mEntryEditViewModel.requestCustomFieldEdition(field)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,10 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
import com.kunzisoft.keepass.database.element.template.TemplateField
|
|
||||||
import com.kunzisoft.keepass.database.helper.getLocalizedName
|
|
||||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||||
import com.kunzisoft.keepass.model.EntryInfo
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
import com.kunzisoft.keepass.model.StreamDirection
|
import com.kunzisoft.keepass.model.StreamDirection
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
|
||||||
import com.kunzisoft.keepass.utils.TimeUtil.getDateTimeString
|
import com.kunzisoft.keepass.utils.TimeUtil.getDateTimeString
|
||||||
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
|
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
|
||||||
import com.kunzisoft.keepass.view.TemplateView
|
import com.kunzisoft.keepass.view.TemplateView
|
||||||
@@ -50,8 +47,6 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
private lateinit var uuidContainerView: View
|
private lateinit var uuidContainerView: View
|
||||||
private lateinit var uuidReferenceView: TextView
|
private lateinit var uuidReferenceView: TextView
|
||||||
|
|
||||||
private var mClipboardHelper: ClipboardHelper? = null
|
|
||||||
|
|
||||||
private val mEntryViewModel: EntryViewModel by activityViewModels()
|
private val mEntryViewModel: EntryViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater,
|
override fun onCreateView(inflater: LayoutInflater,
|
||||||
@@ -66,10 +61,6 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
savedInstanceState: Bundle?) {
|
savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
context?.let { context ->
|
|
||||||
mClipboardHelper = ClipboardHelper(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootView = view
|
rootView = view
|
||||||
// Hide only the first time
|
// Hide only the first time
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
@@ -152,16 +143,14 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
private fun assignEntryInfo(entryInfo: EntryInfo?) {
|
private fun assignEntryInfo(entryInfo: EntryInfo?) {
|
||||||
// Set copy buttons
|
// Set copy buttons
|
||||||
templateView.apply {
|
templateView.apply {
|
||||||
|
setOnUnprotectClickListener { protectedFieldView ->
|
||||||
|
mEntryViewModel.requestUnprotectField(protectedFieldView)
|
||||||
|
}
|
||||||
setOnAskCopySafeClickListener {
|
setOnAskCopySafeClickListener {
|
||||||
showClipboardDialog()
|
showClipboardDialog()
|
||||||
}
|
}
|
||||||
|
setOnCopyActionClickListener { field, protectedFieldView ->
|
||||||
setOnCopyActionClickListener { field ->
|
mEntryViewModel.requestCopyField(field, protectedFieldView)
|
||||||
mClipboardHelper?.timeoutCopyToClipboard(
|
|
||||||
TemplateField.getLocalizedName(context, field.name),
|
|
||||||
field.protectedValue.stringValue,
|
|
||||||
field.protectedValue.isProtected
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,14 +231,14 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
fun firstEntryFieldCopyView(): View? {
|
fun firstEntryFieldCopyView(): View? {
|
||||||
return try {
|
return try {
|
||||||
templateView.getActionImageView()
|
templateView.getActionImageView()
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launchEntryCopyEducationAction() {
|
fun launchEntryCopyEducationAction() {
|
||||||
val appNameString = getString(R.string.app_name)
|
val appNameString = getString(R.string.app_name)
|
||||||
mClipboardHelper?.timeoutCopyToClipboard(appNameString, appNameString)
|
mEntryViewModel.copyToClipboard(appNameString)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package com.kunzisoft.keepass.activities.legacy
|
package com.kunzisoft.keepass.activities.legacy
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
@@ -16,13 +20,14 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Companion.DATABASE_CHANGED_DIALOG_TAG
|
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Companion.DATABASE_CHANGED_DIALOG_TAG
|
||||||
import com.kunzisoft.keepass.activities.stylish.StylishActivity
|
import com.kunzisoft.keepass.activities.stylish.StylishActivity
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.DatabaseTaskProvider.Companion.startDatabaseService
|
import com.kunzisoft.keepass.database.DatabaseTaskProvider.Companion.startDatabaseService
|
||||||
import com.kunzisoft.keepass.database.ProgressMessage
|
|
||||||
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
|
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment
|
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment
|
||||||
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS_TASK_DIALOG_TAG
|
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS_TASK_DIALOG_TAG
|
||||||
|
import com.kunzisoft.keepass.tasks.ProgressTaskViewModel
|
||||||
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -32,6 +37,7 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
|
|||||||
protected val mDatabase: ContextualDatabase?
|
protected val mDatabase: ContextualDatabase?
|
||||||
get() = mDatabaseViewModel.database
|
get() = mDatabaseViewModel.database
|
||||||
|
|
||||||
|
private val progressTaskViewModel: ProgressTaskViewModel by viewModels()
|
||||||
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
|
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
|
||||||
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
|
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
|
||||||
|
|
||||||
@@ -53,58 +59,104 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Useful to only waiting for the activity result and prevent any parallel action
|
||||||
|
*/
|
||||||
|
var credentialResultLaunched = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility activity result launcher,
|
||||||
|
* Used recursively, close each activity with return data
|
||||||
|
*/
|
||||||
|
protected var mCredentialActivityResultLauncher: CredentialActivityResultLauncher =
|
||||||
|
CredentialActivityResultLauncher(
|
||||||
|
registerForActivityResult(
|
||||||
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
) {
|
||||||
|
setActivityResult(
|
||||||
|
lockDatabase = false,
|
||||||
|
resultCode = it.resultCode,
|
||||||
|
data = it.data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom ActivityResultLauncher to manage the database action
|
||||||
|
*/
|
||||||
|
protected inner class CredentialActivityResultLauncher(
|
||||||
|
val builder: ActivityResultLauncher<Intent>
|
||||||
|
) : ActivityResultLauncher<Intent>() {
|
||||||
|
|
||||||
|
override fun launch(
|
||||||
|
input: Intent?,
|
||||||
|
options: ActivityOptionsCompat?
|
||||||
|
) {
|
||||||
|
credentialResultLaunched = true
|
||||||
|
builder.launch(input, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unregister() {
|
||||||
|
builder.unregister()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getContract(): ActivityResultContract<Intent?, *> {
|
||||||
|
return builder.getContract()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
if (savedInstanceState != null
|
||||||
|
&& savedInstanceState.containsKey(CREDENTIAL_RESULT_LAUNCHER_KEY)
|
||||||
|
) {
|
||||||
|
credentialResultLaunched = savedInstanceState.getBoolean(CREDENTIAL_RESULT_LAUNCHER_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
mDatabaseViewModel.actionState.collect { uiState ->
|
mDatabaseViewModel.actionState.collect { uiState ->
|
||||||
when (uiState) {
|
if (credentialResultLaunched.not()) {
|
||||||
is DatabaseViewModel.ActionState.Loading -> {}
|
when (uiState) {
|
||||||
is DatabaseViewModel.ActionState.OnDatabaseReloaded -> {
|
is DatabaseViewModel.ActionState.Wait -> {}
|
||||||
if (finishActivityIfReloadRequested()) {
|
is DatabaseViewModel.ActionState.OnDatabaseReloaded -> {
|
||||||
finish()
|
if (finishActivityIfReloadRequested()) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
is DatabaseViewModel.ActionState.OnDatabaseInfoChanged -> {
|
||||||
|
if (manageDatabaseInfo()) {
|
||||||
is DatabaseViewModel.ActionState.OnDatabaseInfoChanged -> {
|
showDatabaseChangedDialog(
|
||||||
if (manageDatabaseInfo()) {
|
uiState.previousDatabaseInfo,
|
||||||
showDatabaseChangedDialog(
|
uiState.newDatabaseInfo,
|
||||||
uiState.previousDatabaseInfo,
|
uiState.readOnlyDatabase
|
||||||
uiState.newDatabaseInfo,
|
)
|
||||||
uiState.readOnlyDatabase
|
}
|
||||||
|
}
|
||||||
|
is DatabaseViewModel.ActionState.OnDatabaseActionRequested -> {
|
||||||
|
startDatabasePermissionService(
|
||||||
|
uiState.bundle,
|
||||||
|
uiState.actionTask
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
is DatabaseViewModel.ActionState.OnDatabaseActionStarted -> {
|
||||||
|
progressTaskViewModel.show(uiState.progressMessage)
|
||||||
is DatabaseViewModel.ActionState.OnDatabaseActionRequested -> {
|
}
|
||||||
startDatabasePermissionService(
|
is DatabaseViewModel.ActionState.OnDatabaseActionUpdated -> {
|
||||||
uiState.bundle,
|
progressTaskViewModel.show(uiState.progressMessage)
|
||||||
uiState.actionTask
|
}
|
||||||
)
|
is DatabaseViewModel.ActionState.OnDatabaseActionStopped -> {
|
||||||
}
|
progressTaskViewModel.hide()
|
||||||
|
}
|
||||||
is DatabaseViewModel.ActionState.OnDatabaseActionStarted -> {
|
is DatabaseViewModel.ActionState.OnDatabaseActionFinished -> {
|
||||||
if (showDatabaseDialog())
|
onDatabaseActionFinished(
|
||||||
startDialog(uiState.progressMessage)
|
uiState.database,
|
||||||
}
|
uiState.actionTask,
|
||||||
|
uiState.result
|
||||||
is DatabaseViewModel.ActionState.OnDatabaseActionUpdated -> {
|
)
|
||||||
if (showDatabaseDialog())
|
progressTaskViewModel.hide()
|
||||||
updateDialog(uiState.progressMessage)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
is DatabaseViewModel.ActionState.OnDatabaseActionStopped -> {
|
|
||||||
// Remove the progress task
|
|
||||||
stopDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
is DatabaseViewModel.ActionState.OnDatabaseActionFinished -> {
|
|
||||||
onDatabaseActionFinished(
|
|
||||||
uiState.database,
|
|
||||||
uiState.actionTask,
|
|
||||||
uiState.result
|
|
||||||
)
|
|
||||||
stopDialog()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,15 +164,34 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
|
|||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
mDatabaseViewModel.databaseState.collect { database ->
|
progressTaskViewModel.progressTaskState.collect { state ->
|
||||||
// Nullable function
|
when (state) {
|
||||||
onUnknownDatabaseRetrieved(database)
|
is ProgressTaskViewModel.ProgressTaskState.Show ->
|
||||||
database?.let {
|
startDialog()
|
||||||
onDatabaseRetrieved(database)
|
is ProgressTaskViewModel.ProgressTaskState.Hide ->
|
||||||
|
stopDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
mDatabaseViewModel.databaseState.collect { database ->
|
||||||
|
if (credentialResultLaunched.not()) {
|
||||||
|
// Nullable function
|
||||||
|
onUnknownDatabaseRetrieved(database)
|
||||||
|
database?.let {
|
||||||
|
onDatabaseRetrieved(database)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
outState.putBoolean(CREDENTIAL_RESULT_LAUNCHER_KEY, credentialResultLaunched)
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -203,29 +274,21 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startDialog(progressMessage: ProgressMessage) {
|
private fun startDialog() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
if (progressTaskDialogFragment == null) {
|
if (showDatabaseDialog()) {
|
||||||
progressTaskDialogFragment = supportFragmentManager
|
if (progressTaskDialogFragment == null) {
|
||||||
.findFragmentByTag(PROGRESS_TASK_DIALOG_TAG) as ProgressTaskDialogFragment?
|
progressTaskDialogFragment = supportFragmentManager
|
||||||
|
.findFragmentByTag(PROGRESS_TASK_DIALOG_TAG) as ProgressTaskDialogFragment?
|
||||||
|
}
|
||||||
|
if (progressTaskDialogFragment == null) {
|
||||||
|
progressTaskDialogFragment = ProgressTaskDialogFragment()
|
||||||
|
progressTaskDialogFragment?.show(
|
||||||
|
supportFragmentManager,
|
||||||
|
PROGRESS_TASK_DIALOG_TAG
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (progressTaskDialogFragment == null) {
|
|
||||||
progressTaskDialogFragment = ProgressTaskDialogFragment()
|
|
||||||
progressTaskDialogFragment?.show(
|
|
||||||
supportFragmentManager,
|
|
||||||
PROGRESS_TASK_DIALOG_TAG
|
|
||||||
)
|
|
||||||
}
|
|
||||||
updateDialog(progressMessage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateDialog(progressMessage: ProgressMessage) {
|
|
||||||
progressTaskDialogFragment?.apply {
|
|
||||||
updateTitle(progressMessage.titleId)
|
|
||||||
updateMessage(progressMessage.messageId)
|
|
||||||
updateWarning(progressMessage.warningId)
|
|
||||||
setCancellable(progressMessage.cancelable)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,4 +300,8 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
|
|||||||
protected open fun showDatabaseDialog(): Boolean {
|
protected open fun showDatabaseDialog(): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CREDENTIAL_RESULT_LAUNCHER_KEY = "com.kunzisoft.keepass.CREDENTIAL_RESULT_LAUNCHER_KEY"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -370,9 +370,11 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
|
|||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.setPositiveButton(R.string.lock) { _, _ ->
|
.setPositiveButton(R.string.lock) { _, _ ->
|
||||||
sendBroadcast(Intent(LOCK_ACTION))
|
sendBroadcast(Intent(LOCK_ACTION))
|
||||||
|
finish()
|
||||||
}.create().show()
|
}.create().show()
|
||||||
} else {
|
} else {
|
||||||
sendBroadcast(Intent(LOCK_ACTION))
|
sendBroadcast(Intent(LOCK_ACTION))
|
||||||
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package com.kunzisoft.keepass.activities.legacy
|
package com.kunzisoft.keepass.activities.legacy
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.isIntentSenderMode
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.isIntentSenderMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.removeInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.removeInfo
|
||||||
@@ -15,7 +12,6 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveReg
|
|||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSpecialMode
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSpecialMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveTypeMode
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveTypeMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
|
||||||
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
||||||
import com.kunzisoft.keepass.model.RegisterInfo
|
import com.kunzisoft.keepass.model.RegisterInfo
|
||||||
@@ -34,21 +30,6 @@ abstract class DatabaseModeActivity : DatabaseActivity() {
|
|||||||
|
|
||||||
private var mToolbarSpecial: ToolbarSpecial? = null
|
private var mToolbarSpecial: ToolbarSpecial? = null
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility activity result launcher,
|
|
||||||
* Used recursively, close each activity with return data
|
|
||||||
*/
|
|
||||||
protected open var mCredentialActivityResultLauncher: ActivityResultLauncher<Intent>? =
|
|
||||||
registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) {
|
|
||||||
setActivityResult(
|
|
||||||
lockDatabase = false,
|
|
||||||
resultCode = it.resultCode,
|
|
||||||
data = it.data
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun onDatabaseBackPressed() {
|
open fun onDatabaseBackPressed() {
|
||||||
if (mSpecialMode != SpecialMode.DEFAULT)
|
if (mSpecialMode != SpecialMode.DEFAULT)
|
||||||
onCancelSpecialMode()
|
onCancelSpecialMode()
|
||||||
|
|||||||
@@ -34,15 +34,20 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
|
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||||
import com.kunzisoft.keepass.model.EntryInfo
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
import com.kunzisoft.keepass.model.RegisterInfo
|
import com.kunzisoft.keepass.model.RegisterInfo
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
import com.kunzisoft.keepass.utils.LOCK_ACTION
|
import com.kunzisoft.keepass.utils.LOCK_ACTION
|
||||||
|
import com.kunzisoft.keepass.utils.getEnum
|
||||||
import com.kunzisoft.keepass.utils.getEnumExtra
|
import com.kunzisoft.keepass.utils.getEnumExtra
|
||||||
|
import com.kunzisoft.keepass.utils.getParcelableCompat
|
||||||
import com.kunzisoft.keepass.utils.getParcelableExtraCompat
|
import com.kunzisoft.keepass.utils.getParcelableExtraCompat
|
||||||
import com.kunzisoft.keepass.utils.getParcelableList
|
import com.kunzisoft.keepass.utils.getParcelableList
|
||||||
|
import com.kunzisoft.keepass.utils.putEnum
|
||||||
import com.kunzisoft.keepass.utils.putEnumExtra
|
import com.kunzisoft.keepass.utils.putEnumExtra
|
||||||
import com.kunzisoft.keepass.utils.putParcelableList
|
import com.kunzisoft.keepass.utils.putParcelableList
|
||||||
|
import java.io.IOException
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
object EntrySelectionHelper {
|
object EntrySelectionHelper {
|
||||||
@@ -157,10 +162,21 @@ object EntrySelectionHelper {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Bundle.addSearchInfo(searchInfo: SearchInfo?): Bundle {
|
||||||
|
searchInfo?.let {
|
||||||
|
putParcelable(KEY_SEARCH_INFO, it)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
fun Intent.retrieveSearchInfo(): SearchInfo? {
|
fun Intent.retrieveSearchInfo(): SearchInfo? {
|
||||||
return getParcelableExtraCompat(KEY_SEARCH_INFO)
|
return getParcelableExtraCompat(KEY_SEARCH_INFO)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Bundle.getSearchInfo(): SearchInfo? {
|
||||||
|
return getParcelableCompat(KEY_SEARCH_INFO)
|
||||||
|
}
|
||||||
|
|
||||||
fun Intent.addRegisterInfo(registerInfo: RegisterInfo?): Intent {
|
fun Intent.addRegisterInfo(registerInfo: RegisterInfo?): Intent {
|
||||||
registerInfo?.let {
|
registerInfo?.let {
|
||||||
putExtra(KEY_REGISTER_INFO, it)
|
putExtra(KEY_REGISTER_INFO, it)
|
||||||
@@ -168,10 +184,21 @@ object EntrySelectionHelper {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Bundle.addRegisterInfo(registerInfo: RegisterInfo?): Bundle {
|
||||||
|
registerInfo?.let {
|
||||||
|
putParcelable(KEY_REGISTER_INFO, it)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
fun Intent.retrieveRegisterInfo(): RegisterInfo? {
|
fun Intent.retrieveRegisterInfo(): RegisterInfo? {
|
||||||
return getParcelableExtraCompat(KEY_REGISTER_INFO)
|
return getParcelableExtraCompat(KEY_REGISTER_INFO)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Bundle.getRegisterInfo(): RegisterInfo? {
|
||||||
|
return getParcelableCompat(KEY_REGISTER_INFO)
|
||||||
|
}
|
||||||
|
|
||||||
fun Intent.removeInfo() {
|
fun Intent.removeInfo() {
|
||||||
removeExtra(KEY_SEARCH_INFO)
|
removeExtra(KEY_SEARCH_INFO)
|
||||||
removeExtra(KEY_REGISTER_INFO)
|
removeExtra(KEY_REGISTER_INFO)
|
||||||
@@ -182,8 +209,17 @@ object EntrySelectionHelper {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Bundle.addSpecialMode(specialMode: SpecialMode): Bundle {
|
||||||
|
this.putEnum(KEY_SPECIAL_MODE, specialMode)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
fun Intent.retrieveSpecialMode(): SpecialMode {
|
fun Intent.retrieveSpecialMode(): SpecialMode {
|
||||||
return getEnumExtra<SpecialMode>(KEY_SPECIAL_MODE) ?: SpecialMode.DEFAULT
|
return this.getEnumExtra<SpecialMode>(KEY_SPECIAL_MODE) ?: SpecialMode.DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Bundle.getSpecialMode(): SpecialMode {
|
||||||
|
return this.getEnum<SpecialMode>(KEY_SPECIAL_MODE) ?: SpecialMode.DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Intent.addTypeMode(typeMode: TypeMode): Intent {
|
fun Intent.addTypeMode(typeMode: TypeMode): Intent {
|
||||||
@@ -233,12 +269,26 @@ object EntrySelectionHelper {
|
|||||||
removeExtra(EXTRA_NODE_ID)
|
removeExtra(EXTRA_NODE_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve nodes ids from intent and get the corresponding entry info list in [database]
|
||||||
|
*/
|
||||||
|
fun Intent.retrieveAndRemoveEntries(database: ContextualDatabase): List<EntryInfo> {
|
||||||
|
val nodesIds = retrieveNodesIds()
|
||||||
|
?: throw IOException("NodesIds is null")
|
||||||
|
removeNodesIds()
|
||||||
|
return nodesIds.mapNotNull { nodeId ->
|
||||||
|
database
|
||||||
|
.getEntryById(NodeIdUUID(nodeId))
|
||||||
|
?.getEntryInfo(database)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intent sender uses special retains data in callback
|
* Intent sender uses special retains data in callback
|
||||||
*/
|
*/
|
||||||
fun isIntentSenderMode(specialMode: SpecialMode, typeMode: TypeMode): Boolean {
|
fun isIntentSenderMode(specialMode: SpecialMode, typeMode: TypeMode): Boolean {
|
||||||
return (specialMode == SpecialMode.SELECTION
|
return (specialMode == SpecialMode.SELECTION
|
||||||
&& (typeMode == TypeMode.AUTOFILL || typeMode == TypeMode.PASSKEY))
|
&& (typeMode == TypeMode.MAGIKEYBOARD || typeMode == TypeMode.AUTOFILL || typeMode == TypeMode.PASSKEY))
|
||||||
|| (specialMode == SpecialMode.REGISTRATION
|
|| (specialMode == SpecialMode.REGISTRATION
|
||||||
&& (typeMode == TypeMode.AUTOFILL || typeMode == TypeMode.PASSKEY))
|
&& (typeMode == TypeMode.AUTOFILL || typeMode == TypeMode.PASSKEY))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.kunzisoft.keepass.credentialprovider
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
|
import com.kunzisoft.keepass.database.element.Field
|
||||||
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
|
import com.kunzisoft.keepass.view.ProtectedFieldView
|
||||||
|
|
||||||
|
data class UserVerificationData(
|
||||||
|
val actionType: UserVerificationActionType,
|
||||||
|
val database: ContextualDatabase? = null,
|
||||||
|
val entryId: NodeId<*>? = null,
|
||||||
|
val field: Field? = null,
|
||||||
|
val protectedFieldView: ProtectedFieldView? = null,
|
||||||
|
val preferenceKey: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class UserVerificationActionType {
|
||||||
|
LAUNCH_PASSKEY_CEREMONY,
|
||||||
|
SHOW_PROTECTED_FIELD,
|
||||||
|
COPY_PROTECTED_FIELD,
|
||||||
|
EDIT_ENTRY,
|
||||||
|
EDIT_DATABASE_SETTING,
|
||||||
|
MERGE_FROM_DATABASE,
|
||||||
|
SAVE_DATABASE_COPY_TO
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
package com.kunzisoft.keepass.credentialprovider
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.biometric.BiometricManager
|
||||||
|
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
|
||||||
|
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||||
|
import androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS
|
||||||
|
import androidx.biometric.BiometricPrompt
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import com.kunzisoft.keepass.R
|
||||||
|
import com.kunzisoft.keepass.activities.dialogs.CheckDatabaseCredentialDialogFragment
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.UserVerificationRequirement
|
||||||
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
|
import com.kunzisoft.keepass.settings.PreferencesUtil.isUserVerificationDeviceCredential
|
||||||
|
import com.kunzisoft.keepass.utils.getEnumExtra
|
||||||
|
import com.kunzisoft.keepass.utils.putEnumExtra
|
||||||
|
import com.kunzisoft.keepass.view.ProtectedFieldView
|
||||||
|
import com.kunzisoft.keepass.view.toastError
|
||||||
|
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
|
||||||
|
|
||||||
|
class UserVerificationHelper {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val EXTRA_USER_VERIFICATION = "com.kunzisoft.keepass.extra.userVerification"
|
||||||
|
private const val EXTRA_USER_VERIFIED_WITH_AUTH = "com.kunzisoft.keepass.extra.userVerifiedWithAuth"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed authenticators for the User Verification
|
||||||
|
*/
|
||||||
|
const val ALLOWED_AUTHENTICATORS = BIOMETRIC_WEAK or DEVICE_CREDENTIAL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the device supports the biometric prompt for User Verification
|
||||||
|
*/
|
||||||
|
fun Context.isAuthenticatorsAllowed(): Boolean {
|
||||||
|
return BiometricManager.from(this)
|
||||||
|
.canAuthenticate(ALLOWED_AUTHENTICATORS) == BIOMETRIC_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the User Verification to the intent
|
||||||
|
*/
|
||||||
|
fun Intent.addUserVerification(
|
||||||
|
userVerification: UserVerificationRequirement,
|
||||||
|
userVerifiedWithAuth: Boolean
|
||||||
|
) {
|
||||||
|
putEnumExtra(EXTRA_USER_VERIFICATION, userVerification)
|
||||||
|
putExtra(EXTRA_USER_VERIFIED_WITH_AUTH, userVerifiedWithAuth)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define if the User is verified with authentification from the intent
|
||||||
|
*/
|
||||||
|
fun Intent.getUserVerifiedWithAuth(): Boolean {
|
||||||
|
return getBooleanExtra(EXTRA_USER_VERIFIED_WITH_AUTH, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the User Verification from the intent
|
||||||
|
*/
|
||||||
|
fun Intent.removeUserVerification() {
|
||||||
|
removeExtra(EXTRA_USER_VERIFICATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the User verified with auth from the intent
|
||||||
|
*/
|
||||||
|
fun Intent.removeUserVerifiedWithAuth() {
|
||||||
|
removeExtra(EXTRA_USER_VERIFIED_WITH_AUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the User Verification from the intent
|
||||||
|
*/
|
||||||
|
fun Intent.isUserVerificationNeeded(userVerificationPreferred: Boolean): Boolean {
|
||||||
|
val userVerification: UserVerificationRequirement =
|
||||||
|
getEnumExtra<UserVerificationRequirement>(EXTRA_USER_VERIFICATION)
|
||||||
|
?: UserVerificationRequirement.PREFERRED
|
||||||
|
return (userVerification == UserVerificationRequirement.REQUIRED
|
||||||
|
|| (userVerificationPreferred
|
||||||
|
&& userVerification == UserVerificationRequirement.PREFERRED)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the User needs to be verified for this entry
|
||||||
|
*/
|
||||||
|
fun EntryInfo.isUserVerificationNeeded(): Boolean {
|
||||||
|
// Apply to any entry with protected content
|
||||||
|
// Not only this.passkey != null
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Fragment.checkUserVerification(
|
||||||
|
userVerificationViewModel: UserVerificationViewModel,
|
||||||
|
dataToVerify: UserVerificationData
|
||||||
|
) {
|
||||||
|
activity?.checkUserVerification(userVerificationViewModel, dataToVerify)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FragmentActivity.requestShowUnprotectField(
|
||||||
|
userVerificationViewModel: UserVerificationViewModel,
|
||||||
|
database: ContextualDatabase,
|
||||||
|
protectedFieldView: ProtectedFieldView
|
||||||
|
) {
|
||||||
|
if (protectedFieldView.isCurrentlyProtected()) {
|
||||||
|
checkUserVerification(
|
||||||
|
userVerificationViewModel = userVerificationViewModel,
|
||||||
|
dataToVerify = UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.SHOW_PROTECTED_FIELD,
|
||||||
|
database = database,
|
||||||
|
protectedFieldView = protectedFieldView
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
protectedFieldView.protect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a dialog to verify the user
|
||||||
|
*/
|
||||||
|
fun FragmentActivity.checkUserVerification(
|
||||||
|
userVerificationViewModel: UserVerificationViewModel,
|
||||||
|
dataToVerify: UserVerificationData
|
||||||
|
) {
|
||||||
|
if (isAuthenticatorsAllowed() && isUserVerificationDeviceCredential(this)) {
|
||||||
|
showUserVerificationDeviceCredential(userVerificationViewModel, dataToVerify)
|
||||||
|
} else if (dataToVerify.database != null) {
|
||||||
|
showUserVerificationDatabaseCredential(userVerificationViewModel, dataToVerify)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a dialog for entering the device credential to be checked
|
||||||
|
*/
|
||||||
|
fun FragmentActivity.showUserVerificationDeviceCredential(
|
||||||
|
userVerificationViewModel: UserVerificationViewModel,
|
||||||
|
dataToVerify: UserVerificationData
|
||||||
|
) {
|
||||||
|
BiometricPrompt(
|
||||||
|
this, ContextCompat.getMainExecutor(this),
|
||||||
|
object : BiometricPrompt.AuthenticationCallback() {
|
||||||
|
override fun onAuthenticationError(
|
||||||
|
errorCode: Int,
|
||||||
|
errString: CharSequence
|
||||||
|
) {
|
||||||
|
super.onAuthenticationError(errorCode, errString)
|
||||||
|
when (errorCode) {
|
||||||
|
BiometricPrompt.ERROR_CANCELED,
|
||||||
|
BiometricPrompt.ERROR_NEGATIVE_BUTTON,
|
||||||
|
BiometricPrompt.ERROR_USER_CANCELED -> {
|
||||||
|
// No operation
|
||||||
|
Log.i("UserVerification", "$errString")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
toastError(SecurityException("Authentication error: $errString"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userVerificationViewModel.onUserVerificationFailed(dataToVerify)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationSucceeded(
|
||||||
|
result: BiometricPrompt.AuthenticationResult
|
||||||
|
) {
|
||||||
|
super.onAuthenticationSucceeded(result)
|
||||||
|
userVerificationViewModel.onUserVerificationSucceeded(dataToVerify)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationFailed() {
|
||||||
|
super.onAuthenticationFailed()
|
||||||
|
toastError(SecurityException(getString(R.string.device_unlock_not_recognized)))
|
||||||
|
userVerificationViewModel.onUserVerificationFailed(dataToVerify)
|
||||||
|
}
|
||||||
|
}).authenticate(
|
||||||
|
BiometricPrompt.PromptInfo.Builder()
|
||||||
|
.setTitle(getString(R.string.user_verification_required_title))
|
||||||
|
.setSubtitle(getString(R.string.user_verification_required_description))
|
||||||
|
.setAllowedAuthenticators(ALLOWED_AUTHENTICATORS)
|
||||||
|
.setConfirmationRequired(false)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a dialog for entering the database credential to be checked
|
||||||
|
*/
|
||||||
|
fun FragmentActivity.showUserVerificationDatabaseCredential(
|
||||||
|
userVerificationViewModel: UserVerificationViewModel,
|
||||||
|
dataToVerify: UserVerificationData
|
||||||
|
) {
|
||||||
|
userVerificationViewModel.dataToVerify = dataToVerify
|
||||||
|
val fragmentTag = "checkDatabaseCredentialDialog"
|
||||||
|
var fragment: CheckDatabaseCredentialDialogFragment? =
|
||||||
|
supportFragmentManager.findFragmentByTag(fragmentTag)
|
||||||
|
as? CheckDatabaseCredentialDialogFragment?
|
||||||
|
if (fragment == null) {
|
||||||
|
fragment = CheckDatabaseCredentialDialogFragment.getInstance()
|
||||||
|
fragment.show(this.supportFragmentManager, fragmentTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,14 +38,17 @@ import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
|||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addRegisterInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addRegisterInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSearchInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSearchInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSpecialMode
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSpecialMode
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.getRegisterInfo
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.getSearchInfo
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.getSpecialMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
||||||
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillComponent
|
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillComponent
|
||||||
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.addAutofillComponent
|
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.addAutofillComponent
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.retrieveAutofillComponent
|
||||||
import com.kunzisoft.keepass.credentialprovider.viewmodel.AutofillLauncherViewModel
|
import com.kunzisoft.keepass.credentialprovider.viewmodel.AutofillLauncherViewModel
|
||||||
import com.kunzisoft.keepass.credentialprovider.viewmodel.CredentialLauncherViewModel
|
import com.kunzisoft.keepass.credentialprovider.viewmodel.CredentialLauncherViewModel
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseException
|
|
||||||
import com.kunzisoft.keepass.model.RegisterInfo
|
import com.kunzisoft.keepass.model.RegisterInfo
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
import com.kunzisoft.keepass.utils.AppUtil.randomRequestCode
|
import com.kunzisoft.keepass.utils.AppUtil.randomRequestCode
|
||||||
@@ -67,15 +70,21 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
autofillLauncherViewModel.manageRegistrationResult(it)
|
autofillLauncherViewModel.manageRegistrationResult(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyCustomStyle(): Boolean {
|
override fun applyCustomStyle(): Boolean = false
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun finishActivityIfReloadRequested(): Boolean {
|
override fun finishActivityIfReloadRequested(): Boolean = true
|
||||||
return true
|
|
||||||
}
|
override fun manageDatabaseInfo(): Boolean = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
// To apply the bypass https://github.com/Kunzisoft/KeePassDX/issues/2238
|
||||||
|
// before managing intent in super class
|
||||||
|
intent.retrieveSelectionBundle()?.apply {
|
||||||
|
intent.addSpecialMode(getSpecialMode())
|
||||||
|
intent.addSearchInfo(getSearchInfo())
|
||||||
|
intent.addRegisterInfo(getRegisterInfo())
|
||||||
|
intent.addAutofillComponent(retrieveAutofillComponent())
|
||||||
|
}
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
autofillLauncherViewModel.initialize()
|
autofillLauncherViewModel.initialize()
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
@@ -87,10 +96,6 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
showBlockRestartMessage()
|
showBlockRestartMessage()
|
||||||
autofillLauncherViewModel.cancelResult()
|
autofillLauncherViewModel.cancelResult()
|
||||||
}
|
}
|
||||||
is AutofillLauncherViewModel.UIState.ShowReadOnlyMessage -> {
|
|
||||||
showReadOnlySaveMessage()
|
|
||||||
autofillLauncherViewModel.cancelResult()
|
|
||||||
}
|
|
||||||
is AutofillLauncherViewModel.UIState.ShowAutofillSuggestionMessage -> {
|
is AutofillLauncherViewModel.UIState.ShowAutofillSuggestionMessage -> {
|
||||||
showAutofillSuggestionMessage()
|
showAutofillSuggestionMessage()
|
||||||
}
|
}
|
||||||
@@ -101,8 +106,8 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
// Retrieve the UI
|
// Retrieve the UI
|
||||||
autofillLauncherViewModel.credentialUiState.collect { uiState ->
|
autofillLauncherViewModel.credentialUiState.collect { uiState ->
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is CredentialLauncherViewModel.UIState.Loading -> {}
|
is CredentialLauncherViewModel.CredentialState.Loading -> {}
|
||||||
is CredentialLauncherViewModel.UIState.LaunchGroupActivityForSelection -> {
|
is CredentialLauncherViewModel.CredentialState.LaunchGroupActivityForSelection -> {
|
||||||
GroupActivity.launchForSelection(
|
GroupActivity.launchForSelection(
|
||||||
context = this@AutofillLauncherActivity,
|
context = this@AutofillLauncherActivity,
|
||||||
database = uiState.database,
|
database = uiState.database,
|
||||||
@@ -111,7 +116,7 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
activityResultLauncher = mAutofillSelectionActivityResultLauncher,
|
activityResultLauncher = mAutofillSelectionActivityResultLauncher,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.LaunchGroupActivityForRegistration -> {
|
is CredentialLauncherViewModel.CredentialState.LaunchGroupActivityForRegistration -> {
|
||||||
GroupActivity.launchForRegistration(
|
GroupActivity.launchForRegistration(
|
||||||
context = this@AutofillLauncherActivity,
|
context = this@AutofillLauncherActivity,
|
||||||
database = uiState.database,
|
database = uiState.database,
|
||||||
@@ -120,7 +125,7 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
activityResultLauncher = mAutofillRegistrationActivityResultLauncher
|
activityResultLauncher = mAutofillRegistrationActivityResultLauncher
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.LaunchFileDatabaseSelectActivityForSelection -> {
|
is CredentialLauncherViewModel.CredentialState.LaunchFileDatabaseSelectActivityForSelection -> {
|
||||||
FileDatabaseSelectActivity.launchForSelection(
|
FileDatabaseSelectActivity.launchForSelection(
|
||||||
context = this@AutofillLauncherActivity,
|
context = this@AutofillLauncherActivity,
|
||||||
searchInfo = uiState.searchInfo,
|
searchInfo = uiState.searchInfo,
|
||||||
@@ -128,7 +133,7 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
activityResultLauncher = mAutofillSelectionActivityResultLauncher
|
activityResultLauncher = mAutofillSelectionActivityResultLauncher
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.LaunchFileDatabaseSelectActivityForRegistration -> {
|
is CredentialLauncherViewModel.CredentialState.LaunchFileDatabaseSelectActivityForRegistration -> {
|
||||||
FileDatabaseSelectActivity.launchForRegistration(
|
FileDatabaseSelectActivity.launchForRegistration(
|
||||||
context = this@AutofillLauncherActivity,
|
context = this@AutofillLauncherActivity,
|
||||||
registerInfo = uiState.registerInfo,
|
registerInfo = uiState.registerInfo,
|
||||||
@@ -136,14 +141,14 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
activityResultLauncher = mAutofillRegistrationActivityResultLauncher,
|
activityResultLauncher = mAutofillRegistrationActivityResultLauncher,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.SetActivityResult -> {
|
is CredentialLauncherViewModel.CredentialState.SetActivityResult -> {
|
||||||
setActivityResult(
|
setActivityResult(
|
||||||
lockDatabase = uiState.lockDatabase,
|
lockDatabase = uiState.lockDatabase,
|
||||||
resultCode = uiState.resultCode,
|
resultCode = uiState.resultCode,
|
||||||
data = uiState.data
|
data = uiState.data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.ShowError -> {
|
is CredentialLauncherViewModel.CredentialState.ShowError -> {
|
||||||
toastError(uiState.error)
|
toastError(uiState.error)
|
||||||
autofillLauncherViewModel.cancelResult()
|
autofillLauncherViewModel.cancelResult()
|
||||||
}
|
}
|
||||||
@@ -174,29 +179,34 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showReadOnlySaveMessage() {
|
|
||||||
toastError(RegisterInReadOnlyDatabaseException())
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
private const val KEY_PENDING_INTENT_BUNDLE = "com.kunzisoft.keepass.extra.BUNDLE"
|
||||||
private val TAG = AutofillLauncherActivity::class.java.name
|
private val TAG = AutofillLauncherActivity::class.java.name
|
||||||
|
|
||||||
|
fun Intent.retrieveSelectionBundle(): Bundle? {
|
||||||
|
return this.getBundleExtra(KEY_PENDING_INTENT_BUNDLE)
|
||||||
|
}
|
||||||
|
|
||||||
fun getPendingIntentForSelection(
|
fun getPendingIntentForSelection(
|
||||||
context: Context,
|
context: Context,
|
||||||
searchInfo: SearchInfo? = null,
|
searchInfo: SearchInfo? = null,
|
||||||
autofillComponent: AutofillComponent
|
autofillComponent: AutofillComponent
|
||||||
): PendingIntent? {
|
): PendingIntent? {
|
||||||
try {
|
try {
|
||||||
|
// Doesn't work with direct extra Parcelable in Android 11 (don't know why?)
|
||||||
|
// https://github.com/Kunzisoft/KeePassDX/issues/2238
|
||||||
|
// Wrap into a bundle to bypass the problem
|
||||||
|
val tempBundle = Bundle().apply {
|
||||||
|
addSpecialMode(SpecialMode.SELECTION)
|
||||||
|
addSearchInfo(searchInfo)
|
||||||
|
addAutofillComponent(autofillComponent)
|
||||||
|
}
|
||||||
return PendingIntent.getActivity(
|
return PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
randomRequestCode(),
|
randomRequestCode(),
|
||||||
// Doesn't work with direct extra Parcelable (don't know why?)
|
|
||||||
// Wrap into a bundle to bypass the problem
|
|
||||||
Intent(context, AutofillLauncherActivity::class.java).apply {
|
Intent(context, AutofillLauncherActivity::class.java).apply {
|
||||||
addSpecialMode(SpecialMode.SELECTION)
|
putExtra(KEY_PENDING_INTENT_BUNDLE, tempBundle)
|
||||||
addSearchInfo(searchInfo)
|
|
||||||
addAutofillComponent(autofillComponent)
|
|
||||||
},
|
},
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
|
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
|
||||||
@@ -215,12 +225,16 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
registerInfo: RegisterInfo
|
registerInfo: RegisterInfo
|
||||||
): PendingIntent? {
|
): PendingIntent? {
|
||||||
try {
|
try {
|
||||||
|
// Bypass intent issue
|
||||||
|
val tempBundle = Bundle().apply {
|
||||||
|
addSpecialMode(SpecialMode.REGISTRATION)
|
||||||
|
addRegisterInfo(registerInfo)
|
||||||
|
}
|
||||||
return PendingIntent.getActivity(
|
return PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
randomRequestCode(),
|
randomRequestCode(),
|
||||||
Intent(context, AutofillLauncherActivity::class.java).apply {
|
Intent(context, AutofillLauncherActivity::class.java).apply {
|
||||||
addSpecialMode(SpecialMode.REGISTRATION)
|
putExtra(KEY_PENDING_INTENT_BUNDLE, tempBundle)
|
||||||
addRegisterInfo(registerInfo)
|
|
||||||
},
|
},
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
|
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
|
||||||
|
|||||||
@@ -22,20 +22,22 @@ package com.kunzisoft.keepass.credentialprovider.activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.net.toUri
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
||||||
import com.kunzisoft.keepass.activities.GroupActivity
|
import com.kunzisoft.keepass.activities.GroupActivity
|
||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
||||||
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSearchInfo
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
||||||
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.viewmodel.CredentialLauncherViewModel
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.viewmodel.EntrySelectionViewModel
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseException
|
|
||||||
import com.kunzisoft.keepass.database.helper.SearchHelper
|
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields
|
|
||||||
import com.kunzisoft.keepass.utils.KeyboardUtil.isKeyboardActivatedInSettings
|
|
||||||
import com.kunzisoft.keepass.utils.getParcelableCompat
|
|
||||||
import com.kunzisoft.keepass.view.toastError
|
import com.kunzisoft.keepass.view.toastError
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity to search or select entry in database,
|
* Activity to search or select entry in database,
|
||||||
@@ -43,201 +45,133 @@ import com.kunzisoft.keepass.view.toastError
|
|||||||
*/
|
*/
|
||||||
class EntrySelectionLauncherActivity : DatabaseModeActivity() {
|
class EntrySelectionLauncherActivity : DatabaseModeActivity() {
|
||||||
|
|
||||||
override fun applyCustomStyle(): Boolean {
|
private val entrySelectionViewModel: EntrySelectionViewModel by viewModels()
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun finishActivityIfReloadRequested(): Boolean {
|
private var mEntrySelectionActivityResultLauncher: ActivityResultLauncher<Intent>? =
|
||||||
return false
|
this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
entrySelectionViewModel.manageSelectionResult(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyCustomStyle() = false
|
||||||
|
|
||||||
|
override fun finishActivityIfReloadRequested() = false
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = false
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
entrySelectionViewModel.initialize()
|
||||||
|
lifecycleScope.launch {
|
||||||
|
// Initialize the parameters
|
||||||
|
entrySelectionViewModel.uiState.collect { uiState ->
|
||||||
|
when (uiState) {
|
||||||
|
is EntrySelectionViewModel.UIState.Loading -> {}
|
||||||
|
is EntrySelectionViewModel.UIState.PopulateKeyboard -> {
|
||||||
|
MagikeyboardService.addEntryAndLaunchNotificationIfAllowed(
|
||||||
|
context = this@EntrySelectionLauncherActivity,
|
||||||
|
entry = uiState.entryInfo,
|
||||||
|
toast = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is EntrySelectionViewModel.UIState.LaunchFileDatabaseSelectForSearch -> {
|
||||||
|
FileDatabaseSelectActivity.launchForSearch(
|
||||||
|
context = this@EntrySelectionLauncherActivity,
|
||||||
|
searchInfo = uiState.searchInfo
|
||||||
|
)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
is EntrySelectionViewModel.UIState.LaunchGroupActivityForSearch -> {
|
||||||
|
GroupActivity.launchForSearch(
|
||||||
|
context = this@EntrySelectionLauncherActivity,
|
||||||
|
database = uiState.database,
|
||||||
|
searchInfo = uiState.searchInfo
|
||||||
|
)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
// Retrieve the UI
|
||||||
|
entrySelectionViewModel.credentialUiState.collect { uiState ->
|
||||||
|
when (uiState) {
|
||||||
|
is CredentialLauncherViewModel.CredentialState.Loading -> {}
|
||||||
|
is CredentialLauncherViewModel.CredentialState.LaunchGroupActivityForSelection -> {
|
||||||
|
GroupActivity.launchForSelection(
|
||||||
|
context = this@EntrySelectionLauncherActivity,
|
||||||
|
database = uiState.database,
|
||||||
|
searchInfo = uiState.searchInfo,
|
||||||
|
typeMode = uiState.typeMode,
|
||||||
|
activityResultLauncher = mEntrySelectionActivityResultLauncher
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is CredentialLauncherViewModel.CredentialState.LaunchGroupActivityForRegistration -> {
|
||||||
|
GroupActivity.launchForRegistration(
|
||||||
|
context = this@EntrySelectionLauncherActivity,
|
||||||
|
database = uiState.database,
|
||||||
|
registerInfo = uiState.registerInfo,
|
||||||
|
typeMode = uiState.typeMode,
|
||||||
|
activityResultLauncher = null // Null to not get any callback
|
||||||
|
)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
is CredentialLauncherViewModel.CredentialState.LaunchFileDatabaseSelectActivityForSelection -> {
|
||||||
|
FileDatabaseSelectActivity.launchForSelection(
|
||||||
|
context = this@EntrySelectionLauncherActivity,
|
||||||
|
searchInfo = uiState.searchInfo,
|
||||||
|
typeMode = uiState.typeMode,
|
||||||
|
activityResultLauncher = mEntrySelectionActivityResultLauncher
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is CredentialLauncherViewModel.CredentialState.LaunchFileDatabaseSelectActivityForRegistration -> {
|
||||||
|
FileDatabaseSelectActivity.launchForRegistration(
|
||||||
|
context = this@EntrySelectionLauncherActivity,
|
||||||
|
registerInfo = uiState.registerInfo,
|
||||||
|
typeMode = uiState.typeMode,
|
||||||
|
activityResultLauncher = null // Null to not get any callback
|
||||||
|
)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
is CredentialLauncherViewModel.CredentialState.SetActivityResult -> {
|
||||||
|
setActivityResult(
|
||||||
|
lockDatabase = uiState.lockDatabase,
|
||||||
|
resultCode = uiState.resultCode,
|
||||||
|
data = uiState.data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is CredentialLauncherViewModel.CredentialState.ShowError -> {
|
||||||
|
toastError(uiState.error)
|
||||||
|
entrySelectionViewModel.cancelResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnknownDatabaseRetrieved(database: ContextualDatabase?) {
|
override fun onUnknownDatabaseRetrieved(database: ContextualDatabase?) {
|
||||||
super.onUnknownDatabaseRetrieved(database)
|
super.onUnknownDatabaseRetrieved(database)
|
||||||
|
entrySelectionViewModel.launchActionIfNeeded(intent, mSpecialMode, database)
|
||||||
val keySelectionBundle = intent.getBundleExtra(KEY_SELECTION_BUNDLE)
|
|
||||||
if (keySelectionBundle != null) {
|
|
||||||
// To manage package name
|
|
||||||
var searchInfo = SearchInfo()
|
|
||||||
keySelectionBundle.getParcelableCompat<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 = extra.toUri().host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launchSelection(database, sharedWebDomain, otpString)
|
|
||||||
}
|
|
||||||
Intent.ACTION_VIEW -> {
|
|
||||||
// Retrieve OTP
|
|
||||||
intent.dataString?.let { extra ->
|
|
||||||
if (OtpEntryFields.isOTPUri(extra))
|
|
||||||
otpString = extra
|
|
||||||
}
|
|
||||||
launchSelection(database, null, otpString)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
if (database != null) {
|
|
||||||
GroupActivity.launch(this, database)
|
|
||||||
} else {
|
|
||||||
FileDatabaseSelectActivity.launch(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finish()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchSelection(database: ContextualDatabase?,
|
override fun onDestroy() {
|
||||||
sharedWebDomain: String?,
|
super.onDestroy()
|
||||||
otpString: String?) {
|
|
||||||
// Build domain search param
|
|
||||||
val searchInfo = SearchInfo().apply {
|
|
||||||
this.webDomain = sharedWebDomain
|
|
||||||
this.otpString = otpString
|
|
||||||
}
|
|
||||||
launch(database, searchInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun launch(database: ContextualDatabase?,
|
|
||||||
searchInfo: SearchInfo) {
|
|
||||||
|
|
||||||
// Setting to integrate Magikeyboard
|
|
||||||
val searchShareForMagikeyboard = isKeyboardActivatedInSettings()
|
|
||||||
|
|
||||||
// If database is open
|
|
||||||
val readOnly = database?.isReadOnly != false
|
|
||||||
SearchHelper.checkAutoSearchInfo(
|
|
||||||
context = this,
|
|
||||||
database = database,
|
|
||||||
searchInfo = searchInfo,
|
|
||||||
onItemsFound = { openedDatabase, items ->
|
|
||||||
// Items found
|
|
||||||
if (searchInfo.otpString != null) {
|
|
||||||
if (!readOnly) {
|
|
||||||
GroupActivity.launchForRegistration(
|
|
||||||
context = this,
|
|
||||||
activityResultLauncher = null,
|
|
||||||
database = openedDatabase,
|
|
||||||
registerInfo = searchInfo.toRegisterInfo(),
|
|
||||||
typeMode = TypeMode.DEFAULT
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
toastError(RegisterInReadOnlyDatabaseException())
|
|
||||||
}
|
|
||||||
} else if (searchShareForMagikeyboard) {
|
|
||||||
MagikeyboardService.performSelection(
|
|
||||||
items,
|
|
||||||
{ entryInfo ->
|
|
||||||
// Automatically populate keyboard
|
|
||||||
MagikeyboardService.populateKeyboardAndMoveAppToBackground(
|
|
||||||
this,
|
|
||||||
entryInfo
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{ autoSearch ->
|
|
||||||
GroupActivity.launchForSelection(
|
|
||||||
context = this,
|
|
||||||
database = openedDatabase,
|
|
||||||
typeMode = TypeMode.MAGIKEYBOARD,
|
|
||||||
searchInfo = searchInfo,
|
|
||||||
autoSearch = autoSearch
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
GroupActivity.launchForSearchResult(
|
|
||||||
this,
|
|
||||||
openedDatabase,
|
|
||||||
searchInfo,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onItemNotFound = { openedDatabase ->
|
|
||||||
// Show the database UI to select the entry
|
|
||||||
if (searchInfo.otpString != null) {
|
|
||||||
if (!readOnly) {
|
|
||||||
GroupActivity.launchForRegistration(
|
|
||||||
context = this,
|
|
||||||
activityResultLauncher = null,
|
|
||||||
database = openedDatabase,
|
|
||||||
registerInfo = searchInfo.toRegisterInfo(),
|
|
||||||
typeMode = TypeMode.DEFAULT
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
toastError(RegisterInReadOnlyDatabaseException())
|
|
||||||
}
|
|
||||||
} else if (searchShareForMagikeyboard) {
|
|
||||||
GroupActivity.launchForSelection(
|
|
||||||
context = this,
|
|
||||||
database = openedDatabase,
|
|
||||||
typeMode = TypeMode.MAGIKEYBOARD,
|
|
||||||
searchInfo = searchInfo,
|
|
||||||
autoSearch = false
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
GroupActivity.launchForSearchResult(
|
|
||||||
this,
|
|
||||||
openedDatabase,
|
|
||||||
searchInfo,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDatabaseClosed = {
|
|
||||||
// If database not open
|
|
||||||
if (searchInfo.otpString != null) {
|
|
||||||
FileDatabaseSelectActivity.launchForRegistration(
|
|
||||||
context = this,
|
|
||||||
activityResultLauncher = null,
|
|
||||||
registerInfo = searchInfo.toRegisterInfo(),
|
|
||||||
typeMode = TypeMode.DEFAULT
|
|
||||||
)
|
|
||||||
} else if (searchShareForMagikeyboard) {
|
|
||||||
FileDatabaseSelectActivity.launchForSelection(
|
|
||||||
context = this,
|
|
||||||
typeMode = TypeMode.MAGIKEYBOARD,
|
|
||||||
searchInfo = searchInfo
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
FileDatabaseSelectActivity.launchForSearchResult(
|
|
||||||
this,
|
|
||||||
searchInfo
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val KEY_SELECTION_BUNDLE = "KEY_SELECTION_BUNDLE"
|
fun launch(
|
||||||
private const val KEY_SEARCH_INFO = "KEY_SEARCH_INFO"
|
context: Context,
|
||||||
|
searchInfo: SearchInfo? = null
|
||||||
fun launch(context: Context,
|
) {
|
||||||
searchInfo: SearchInfo? = null) {
|
context.startActivity(Intent(
|
||||||
val intent = Intent(context, EntrySelectionLauncherActivity::class.java).apply {
|
context,
|
||||||
putExtra(KEY_SELECTION_BUNDLE, Bundle().apply {
|
EntrySelectionLauncherActivity::class.java
|
||||||
putParcelable(KEY_SEARCH_INFO, searchInfo)
|
).apply {
|
||||||
})
|
addSearchInfo(searchInfo)
|
||||||
}
|
// New task needed because don't launch from an Activity context
|
||||||
// New task needed because don't launch from an Activity context
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
Intent.FLAG_ACTIVITY_CLEAR_TASK
|
})
|
||||||
context.startActivity(intent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,14 +76,14 @@ class HardwareKeyActivity: DatabaseModeActivity(){
|
|||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
mHardwareKeyLauncherViewModel.credentialUiState.collect { uiState ->
|
mHardwareKeyLauncherViewModel.credentialUiState.collect { uiState ->
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is CredentialLauncherViewModel.UIState.SetActivityResult -> {
|
is CredentialLauncherViewModel.CredentialState.SetActivityResult -> {
|
||||||
setActivityResult(
|
setActivityResult(
|
||||||
lockDatabase = uiState.lockDatabase,
|
lockDatabase = uiState.lockDatabase,
|
||||||
resultCode = uiState.resultCode,
|
resultCode = uiState.resultCode,
|
||||||
data = uiState.data
|
data = uiState.data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.ShowError -> {
|
is CredentialLauncherViewModel.CredentialState.ShowError -> {
|
||||||
toastError(uiState.error)
|
toastError(uiState.error)
|
||||||
mHardwareKeyLauncherViewModel.cancelResult()
|
mHardwareKeyLauncherViewModel.cancelResult()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
||||||
import com.kunzisoft.keepass.activities.GroupActivity
|
import com.kunzisoft.keepass.activities.GroupActivity
|
||||||
@@ -43,7 +45,14 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addTypeMode
|
|||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
||||||
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationActionType
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.addUserVerification
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.getUserVerifiedWithAuth
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.isUserVerificationNeeded
|
||||||
import com.kunzisoft.keepass.credentialprovider.passkey.data.AndroidPrivilegedApp
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.AndroidPrivilegedApp
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.UserVerificationRequirement
|
||||||
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.addAppOrigin
|
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.addAppOrigin
|
||||||
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.addAuthCode
|
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.addAuthCode
|
||||||
import com.kunzisoft.keepass.credentialprovider.viewmodel.CredentialLauncherViewModel
|
import com.kunzisoft.keepass.credentialprovider.viewmodel.CredentialLauncherViewModel
|
||||||
@@ -52,9 +61,11 @@ import com.kunzisoft.keepass.database.ContextualDatabase
|
|||||||
import com.kunzisoft.keepass.model.AppOrigin
|
import com.kunzisoft.keepass.model.AppOrigin
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
||||||
|
import com.kunzisoft.keepass.settings.PreferencesUtil.isUserVerificationPreferred
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.utils.AppUtil.randomRequestCode
|
import com.kunzisoft.keepass.utils.AppUtil.randomRequestCode
|
||||||
import com.kunzisoft.keepass.view.toastError
|
import com.kunzisoft.keepass.view.toastError
|
||||||
|
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@@ -62,6 +73,7 @@ import java.util.UUID
|
|||||||
class PasskeyLauncherActivity : DatabaseLockActivity() {
|
class PasskeyLauncherActivity : DatabaseLockActivity() {
|
||||||
|
|
||||||
private val passkeyLauncherViewModel: PasskeyLauncherViewModel by viewModels()
|
private val passkeyLauncherViewModel: PasskeyLauncherViewModel by viewModels()
|
||||||
|
private val userVerificationViewModel: UserVerificationViewModel by viewModels()
|
||||||
|
|
||||||
private var mPasskeySelectionActivityResultLauncher: ActivityResultLauncher<Intent>? =
|
private var mPasskeySelectionActivityResultLauncher: ActivityResultLauncher<Intent>? =
|
||||||
this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
@@ -83,9 +95,10 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
// Initialize the parameters
|
// Initialize the parameters
|
||||||
passkeyLauncherViewModel.initialize()
|
passkeyLauncherViewModel.initialize(userVerified = intent.getUserVerifiedWithAuth())
|
||||||
// Retrieve the UI
|
// Retrieve the UI
|
||||||
passkeyLauncherViewModel.uiState.collect { uiState ->
|
passkeyLauncherViewModel.uiState.collect { uiState ->
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
@@ -112,19 +125,19 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
passkeyLauncherViewModel.credentialUiState.collect { uiState ->
|
passkeyLauncherViewModel.credentialUiState.collect { uiState ->
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is CredentialLauncherViewModel.UIState.Loading -> {}
|
is CredentialLauncherViewModel.CredentialState.Loading -> {}
|
||||||
is CredentialLauncherViewModel.UIState.SetActivityResult -> {
|
is CredentialLauncherViewModel.CredentialState.SetActivityResult -> {
|
||||||
setActivityResult(
|
setActivityResult(
|
||||||
lockDatabase = uiState.lockDatabase,
|
lockDatabase = uiState.lockDatabase,
|
||||||
resultCode = uiState.resultCode,
|
resultCode = uiState.resultCode,
|
||||||
data = uiState.data
|
data = uiState.data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.ShowError -> {
|
is CredentialLauncherViewModel.CredentialState.ShowError -> {
|
||||||
toastError(uiState.error)
|
toastError(uiState.error)
|
||||||
passkeyLauncherViewModel.cancelResult()
|
passkeyLauncherViewModel.cancelResult()
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.LaunchGroupActivityForSelection -> {
|
is CredentialLauncherViewModel.CredentialState.LaunchGroupActivityForSelection -> {
|
||||||
GroupActivity.launchForSelection(
|
GroupActivity.launchForSelection(
|
||||||
context = this@PasskeyLauncherActivity,
|
context = this@PasskeyLauncherActivity,
|
||||||
database = uiState.database,
|
database = uiState.database,
|
||||||
@@ -133,7 +146,7 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
activityResultLauncher = mPasskeySelectionActivityResultLauncher
|
activityResultLauncher = mPasskeySelectionActivityResultLauncher
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.LaunchGroupActivityForRegistration -> {
|
is CredentialLauncherViewModel.CredentialState.LaunchGroupActivityForRegistration -> {
|
||||||
GroupActivity.launchForRegistration(
|
GroupActivity.launchForRegistration(
|
||||||
context = this@PasskeyLauncherActivity,
|
context = this@PasskeyLauncherActivity,
|
||||||
database = uiState.database,
|
database = uiState.database,
|
||||||
@@ -142,7 +155,7 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
activityResultLauncher = mPasskeyRegistrationActivityResultLauncher
|
activityResultLauncher = mPasskeyRegistrationActivityResultLauncher
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.LaunchFileDatabaseSelectActivityForSelection -> {
|
is CredentialLauncherViewModel.CredentialState.LaunchFileDatabaseSelectActivityForSelection -> {
|
||||||
FileDatabaseSelectActivity.launchForSelection(
|
FileDatabaseSelectActivity.launchForSelection(
|
||||||
context = this@PasskeyLauncherActivity,
|
context = this@PasskeyLauncherActivity,
|
||||||
typeMode = uiState.typeMode,
|
typeMode = uiState.typeMode,
|
||||||
@@ -150,7 +163,7 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
activityResultLauncher = mPasskeySelectionActivityResultLauncher
|
activityResultLauncher = mPasskeySelectionActivityResultLauncher
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CredentialLauncherViewModel.UIState.LaunchFileDatabaseSelectActivityForRegistration -> {
|
is CredentialLauncherViewModel.CredentialState.LaunchFileDatabaseSelectActivityForRegistration -> {
|
||||||
FileDatabaseSelectActivity.launchForRegistration(
|
FileDatabaseSelectActivity.launchForRegistration(
|
||||||
context = this@PasskeyLauncherActivity,
|
context = this@PasskeyLauncherActivity,
|
||||||
typeMode = uiState.typeMode,
|
typeMode = uiState.typeMode,
|
||||||
@@ -161,11 +174,58 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
userVerificationViewModel.userVerificationState.collect { uiState ->
|
||||||
|
when (uiState) {
|
||||||
|
is UserVerificationViewModel.UVState.Loading -> {}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationSucceeded -> {
|
||||||
|
val data = uiState.dataToVerify
|
||||||
|
when (data.actionType) {
|
||||||
|
UserVerificationActionType.LAUNCH_PASSKEY_CEREMONY -> {
|
||||||
|
passkeyLauncherViewModel.launchActionIfNeeded(
|
||||||
|
userVerified = true,
|
||||||
|
intent = intent,
|
||||||
|
specialMode = mSpecialMode,
|
||||||
|
database = uiState.dataToVerify.database
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
userVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationCanceled -> {
|
||||||
|
toastError(uiState.error)
|
||||||
|
passkeyLauncherViewModel.cancelResult()
|
||||||
|
userVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnknownDatabaseRetrieved(database: ContextualDatabase?) {
|
override fun onUnknownDatabaseRetrieved(database: ContextualDatabase?) {
|
||||||
super.onUnknownDatabaseRetrieved(database)
|
super.onUnknownDatabaseRetrieved(database)
|
||||||
passkeyLauncherViewModel.launchActionIfNeeded(intent, mSpecialMode, database)
|
// To manage https://github.com/Kunzisoft/KeePassDX/issues/2283
|
||||||
|
val userVerificationNeeded = intent.isUserVerificationNeeded(
|
||||||
|
userVerificationPreferred = isUserVerificationPreferred(this)
|
||||||
|
) && intent.getUserVerifiedWithAuth().not()
|
||||||
|
if (userVerificationNeeded) {
|
||||||
|
checkUserVerification(
|
||||||
|
userVerificationViewModel = userVerificationViewModel,
|
||||||
|
dataToVerify = UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.LAUNCH_PASSKEY_CEREMONY,
|
||||||
|
database = database
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
passkeyLauncherViewModel.launchActionIfNeeded(
|
||||||
|
intent = intent,
|
||||||
|
specialMode = mSpecialMode,
|
||||||
|
database = database
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDatabaseActionFinished(
|
override fun onDatabaseActionFinished(
|
||||||
@@ -278,7 +338,9 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
specialMode: SpecialMode,
|
specialMode: SpecialMode,
|
||||||
searchInfo: SearchInfo? = null,
|
searchInfo: SearchInfo? = null,
|
||||||
appOrigin: AppOrigin? = null,
|
appOrigin: AppOrigin? = null,
|
||||||
nodeId: UUID? = null
|
nodeId: UUID? = null,
|
||||||
|
userVerification: UserVerificationRequirement = UserVerificationRequirement.PREFERRED,
|
||||||
|
userVerifiedWithAuth: Boolean = true
|
||||||
): PendingIntent? {
|
): PendingIntent? {
|
||||||
return PendingIntent.getActivity(
|
return PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
@@ -290,6 +352,7 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
addAppOrigin(appOrigin)
|
addAppOrigin(appOrigin)
|
||||||
addNodeId(nodeId)
|
addNodeId(nodeId)
|
||||||
addAuthCode(nodeId)
|
addAuthCode(nodeId)
|
||||||
|
addUserVerification(userVerification, userVerifiedWithAuth)
|
||||||
},
|
},
|
||||||
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import android.content.Intent
|
|||||||
import android.graphics.BlendMode
|
import android.graphics.BlendMode
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
import android.service.autofill.Dataset
|
import android.service.autofill.Dataset
|
||||||
import android.service.autofill.Field
|
import android.service.autofill.Field
|
||||||
import android.service.autofill.FillResponse
|
import android.service.autofill.FillResponse
|
||||||
@@ -53,6 +54,7 @@ import com.kunzisoft.keepass.model.SearchInfo
|
|||||||
import com.kunzisoft.keepass.settings.AutofillSettingsActivity
|
import com.kunzisoft.keepass.settings.AutofillSettingsActivity
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.utils.AppUtil.randomRequestCode
|
import com.kunzisoft.keepass.utils.AppUtil.randomRequestCode
|
||||||
|
import com.kunzisoft.keepass.utils.getParcelableCompat
|
||||||
import com.kunzisoft.keepass.utils.getParcelableExtraCompat
|
import com.kunzisoft.keepass.utils.getParcelableExtraCompat
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@@ -64,20 +66,50 @@ object AutofillHelper {
|
|||||||
private const val EXTRA_BASE_STRUCTURE = "com.kunzisoft.keepass.autofill.BASE_STRUCTURE"
|
private const val EXTRA_BASE_STRUCTURE = "com.kunzisoft.keepass.autofill.BASE_STRUCTURE"
|
||||||
private const val EXTRA_INLINE_SUGGESTIONS_REQUEST = "com.kunzisoft.keepass.autofill.INLINE_SUGGESTIONS_REQUEST"
|
private const val EXTRA_INLINE_SUGGESTIONS_REQUEST = "com.kunzisoft.keepass.autofill.INLINE_SUGGESTIONS_REQUEST"
|
||||||
|
|
||||||
fun Intent.addAutofillComponent(autofillComponent: AutofillComponent) {
|
fun Intent.addAutofillComponent(autofillComponent: AutofillComponent?): Intent {
|
||||||
this.putExtra(EXTRA_BASE_STRUCTURE, autofillComponent.assistStructure)
|
autofillComponent?.let {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
this.putExtra(EXTRA_BASE_STRUCTURE, autofillComponent.assistStructure)
|
||||||
autofillComponent.compatInlineSuggestionsRequest?.let {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
this.putExtra(EXTRA_INLINE_SUGGESTIONS_REQUEST, it)
|
autofillComponent.compatInlineSuggestionsRequest?.let {
|
||||||
|
this.putExtra(EXTRA_INLINE_SUGGESTIONS_REQUEST, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Intent.retrieveAutofillComponent(): AutofillComponent? {
|
fun Intent.retrieveAutofillComponent(): AutofillComponent? {
|
||||||
getParcelableExtraCompat<AssistStructure>(EXTRA_BASE_STRUCTURE)?.let { assistStructure ->
|
this.getParcelableExtraCompat<AssistStructure>(EXTRA_BASE_STRUCTURE)?.let { assistStructure ->
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
AutofillComponent(assistStructure,
|
AutofillComponent(
|
||||||
getParcelableExtraCompat(EXTRA_INLINE_SUGGESTIONS_REQUEST))
|
assistStructure,
|
||||||
|
this.getParcelableExtraCompat(EXTRA_INLINE_SUGGESTIONS_REQUEST))
|
||||||
|
} else {
|
||||||
|
AutofillComponent(assistStructure, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Bundle.addAutofillComponent(autofillComponent: AutofillComponent?): Bundle {
|
||||||
|
autofillComponent?.let {
|
||||||
|
this.putParcelable(EXTRA_BASE_STRUCTURE, autofillComponent.assistStructure)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
autofillComponent.compatInlineSuggestionsRequest?.let {
|
||||||
|
this.putParcelable(EXTRA_INLINE_SUGGESTIONS_REQUEST, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Bundle.retrieveAutofillComponent(): AutofillComponent? {
|
||||||
|
this.getParcelableCompat<AssistStructure>(EXTRA_BASE_STRUCTURE)?.let { assistStructure ->
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
AutofillComponent(
|
||||||
|
assistStructure,
|
||||||
|
this.getParcelableCompat(EXTRA_INLINE_SUGGESTIONS_REQUEST)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
AutofillComponent(assistStructure, null)
|
AutofillComponent(assistStructure, null)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ class StructureParser(private val structure: AssistStructure) {
|
|||||||
it.contains(View.AUTOFILL_HINT_PASSWORD, true) -> {
|
it.contains(View.AUTOFILL_HINT_PASSWORD, true) -> {
|
||||||
// Password Id changed if it's the second times we are here,
|
// Password Id changed if it's the second times we are here,
|
||||||
// So the last username candidate is most appropriate
|
// So the last username candidate is most appropriate
|
||||||
if (result?.passwordId != null) {
|
if (result?.passwordId != null && usernameIdCandidate != null) {
|
||||||
result?.usernameId = usernameIdCandidate
|
result?.usernameId = usernameIdCandidate
|
||||||
result?.usernameValue = usernameValueCandidate
|
result?.usernameValue = usernameValueCandidate
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -462,9 +462,11 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
|||||||
KeyboardEntryNotificationService.launchNotificationIfAllowed(context, entry, toast)
|
KeyboardEntryNotificationService.launchNotificationIfAllowed(context, entry, toast)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun performSelection(items: List<EntryInfo>,
|
fun performSelection(
|
||||||
actionPopulateKeyboard: (entryInfo: EntryInfo) -> Unit,
|
items: List<EntryInfo>,
|
||||||
actionEntrySelection: (autoSearch: Boolean) -> Unit) {
|
actionPopulateKeyboard: (entryInfo: EntryInfo) -> Unit,
|
||||||
|
actionEntrySelection: (autoSearch: Boolean) -> Unit
|
||||||
|
) {
|
||||||
EntrySelectionHelper.performSelection(
|
EntrySelectionHelper.performSelection(
|
||||||
items = items,
|
items = items,
|
||||||
actionPopulateCredentialProvider = { itemFound ->
|
actionPopulateCredentialProvider = { itemFound ->
|
||||||
@@ -478,15 +480,5 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
|||||||
actionEntrySelection = actionEntrySelection
|
actionEntrySelection = actionEntrySelection
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun populateKeyboardAndMoveAppToBackground(activity: Activity,
|
|
||||||
entry: EntryInfo,
|
|
||||||
toast: Boolean = true) {
|
|
||||||
// Populate Magikeyboard with entry
|
|
||||||
addEntryAndLaunchNotificationIfAllowed(activity, entry, toast)
|
|
||||||
// Consume the selection mode
|
|
||||||
activity.intent.removeModes()
|
|
||||||
activity.moveTaskToBack(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,12 +42,14 @@ import androidx.credentials.provider.CredentialEntry
|
|||||||
import androidx.credentials.provider.CredentialProviderService
|
import androidx.credentials.provider.CredentialProviderService
|
||||||
import androidx.credentials.provider.ProviderClearCredentialStateRequest
|
import androidx.credentials.provider.ProviderClearCredentialStateRequest
|
||||||
import androidx.credentials.provider.PublicKeyCredentialEntry
|
import androidx.credentials.provider.PublicKeyCredentialEntry
|
||||||
|
import com.kunzisoft.encrypt.Base64Helper.Companion.b64Encode
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.buildIcon
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.buildIcon
|
||||||
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.activity.PasskeyLauncherActivity
|
import com.kunzisoft.keepass.credentialprovider.activity.PasskeyLauncherActivity
|
||||||
import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialCreationOptions
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialCreationOptions
|
||||||
import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialRequestOptions
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialRequestOptions
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.UserVerificationRequirement
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.DatabaseTaskProvider
|
import com.kunzisoft.keepass.database.DatabaseTaskProvider
|
||||||
import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseException
|
import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseException
|
||||||
@@ -90,9 +92,13 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildPasskeySearchInfo(relyingParty: String): SearchInfo {
|
private fun buildPasskeySearchInfo(
|
||||||
|
relyingParty: String,
|
||||||
|
credentialIds: List<String> = listOf()
|
||||||
|
): SearchInfo {
|
||||||
return SearchInfo().apply {
|
return SearchInfo().apply {
|
||||||
this.relyingParty = relyingParty
|
this.relyingParty = relyingParty
|
||||||
|
this.credentialIds = credentialIds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +114,7 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(javaClass.simpleName, "onBeginGetCredentialRequest error", e)
|
Log.e(javaClass.simpleName, "onBeginGetCredentialRequest error", e)
|
||||||
|
toastError(e)
|
||||||
callback.onError(GetCredentialUnknownException())
|
callback.onError(GetCredentialUnknownException())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,12 +143,16 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
option: BeginGetPublicKeyCredentialOption,
|
option: BeginGetPublicKeyCredentialOption,
|
||||||
callback: (List<CredentialEntry>) -> Unit
|
callback: (List<CredentialEntry>) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val passkeyEntries: MutableList<CredentialEntry> = mutableListOf()
|
val passkeyEntries: MutableList<CredentialEntry> = mutableListOf()
|
||||||
|
|
||||||
val relyingPartyId = PublicKeyCredentialRequestOptions(option.requestJson).rpId
|
val publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions(option.requestJson)
|
||||||
val searchInfo = buildPasskeySearchInfo(relyingPartyId)
|
val relyingPartyId = publicKeyCredentialRequestOptions.rpId
|
||||||
Log.d(TAG, "Build passkey search for relying party $relyingPartyId")
|
val credentialIdList = publicKeyCredentialRequestOptions.allowCredentials
|
||||||
|
.map { b64Encode(it.id) }
|
||||||
|
val searchInfo = buildPasskeySearchInfo(relyingPartyId, credentialIdList)
|
||||||
|
val userVerification = publicKeyCredentialRequestOptions.userVerification
|
||||||
|
Log.d(TAG, "Build passkey search for UV $userVerification, " +
|
||||||
|
"RP $relyingPartyId and Credential IDs $credentialIdList")
|
||||||
SearchHelper.checkAutoSearchInfo(
|
SearchHelper.checkAutoSearchInfo(
|
||||||
context = this,
|
context = this,
|
||||||
database = mDatabase,
|
database = mDatabase,
|
||||||
@@ -153,14 +164,19 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
context = applicationContext,
|
context = applicationContext,
|
||||||
specialMode = SpecialMode.SELECTION,
|
specialMode = SpecialMode.SELECTION,
|
||||||
nodeId = passkeyEntry.id,
|
nodeId = passkeyEntry.id,
|
||||||
appOrigin = passkeyEntry.appOrigin
|
appOrigin = passkeyEntry.appOrigin,
|
||||||
|
userVerification = userVerification,
|
||||||
|
userVerifiedWithAuth = false
|
||||||
)?.let { usagePendingIntent ->
|
)?.let { usagePendingIntent ->
|
||||||
val passkey = passkeyEntry.passkey
|
val passkey = passkeyEntry.passkey
|
||||||
passkeyEntries.add(
|
passkeyEntries.add(
|
||||||
PublicKeyCredentialEntry(
|
PublicKeyCredentialEntry(
|
||||||
context = applicationContext,
|
context = applicationContext,
|
||||||
username = passkey?.username ?: "Unknown",
|
username = passkey?.username ?: "Unknown",
|
||||||
icon = passkeyEntry.buildIcon(this@PasskeyProviderService, database)?.apply {
|
icon = passkeyEntry.buildIcon(
|
||||||
|
this@PasskeyProviderService,
|
||||||
|
database
|
||||||
|
)?.apply {
|
||||||
setTintBlendMode(BlendMode.DST)
|
setTintBlendMode(BlendMode.DST)
|
||||||
} ?: defaultIcon,
|
} ?: defaultIcon,
|
||||||
pendingIntent = usagePendingIntent,
|
pendingIntent = usagePendingIntent,
|
||||||
@@ -175,26 +191,38 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
},
|
},
|
||||||
onItemNotFound = { _ ->
|
onItemNotFound = { _ ->
|
||||||
Log.w(TAG, "No passkey found in the database with this relying party : $relyingPartyId")
|
Log.w(TAG, "No passkey found in the database with this relying party : $relyingPartyId")
|
||||||
Log.d(TAG, "Add pending intent for passkey selection in opened database")
|
if (credentialIdList.isEmpty()) {
|
||||||
PasskeyLauncherActivity.getPendingIntent(
|
Log.d(TAG, "Add pending intent for passkey selection in opened database")
|
||||||
context = applicationContext,
|
PasskeyLauncherActivity.getPendingIntent(
|
||||||
specialMode = SpecialMode.SELECTION,
|
context = applicationContext,
|
||||||
searchInfo = searchInfo
|
specialMode = SpecialMode.SELECTION,
|
||||||
)?.let { pendingIntent ->
|
searchInfo = searchInfo,
|
||||||
passkeyEntries.add(
|
userVerification = userVerification,
|
||||||
PublicKeyCredentialEntry(
|
userVerifiedWithAuth = false
|
||||||
context = applicationContext,
|
)?.let { pendingIntent ->
|
||||||
username = getString(R.string.passkey_database_username),
|
passkeyEntries.add(
|
||||||
displayName = getString(R.string.passkey_selection_description),
|
PublicKeyCredentialEntry(
|
||||||
icon = defaultIcon,
|
context = applicationContext,
|
||||||
pendingIntent = pendingIntent,
|
username = getString(R.string.passkey_database_username),
|
||||||
beginGetPublicKeyCredentialOption = option,
|
displayName = getString(R.string.passkey_selection_description),
|
||||||
lastUsedTime = Instant.now(),
|
icon = defaultIcon,
|
||||||
isAutoSelectAllowed = isAutoSelectAllowed
|
pendingIntent = pendingIntent,
|
||||||
|
beginGetPublicKeyCredentialOption = option,
|
||||||
|
lastUsedTime = Instant.now(),
|
||||||
|
isAutoSelectAllowed = isAutoSelectAllowed
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
callback(passkeyEntries)
|
||||||
|
} else {
|
||||||
|
throw IOException(
|
||||||
|
getString(
|
||||||
|
R.string.error_passkey_credential_id,
|
||||||
|
relyingPartyId,
|
||||||
|
credentialIdList
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
callback(passkeyEntries)
|
|
||||||
},
|
},
|
||||||
onDatabaseClosed = {
|
onDatabaseClosed = {
|
||||||
Log.d(TAG, "Add pending intent for passkey selection in closed database")
|
Log.d(TAG, "Add pending intent for passkey selection in closed database")
|
||||||
@@ -202,7 +230,8 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
PasskeyLauncherActivity.getPendingIntent(
|
PasskeyLauncherActivity.getPendingIntent(
|
||||||
context = applicationContext,
|
context = applicationContext,
|
||||||
specialMode = SpecialMode.SELECTION,
|
specialMode = SpecialMode.SELECTION,
|
||||||
searchInfo = searchInfo
|
searchInfo = searchInfo,
|
||||||
|
userVerifiedWithAuth = true
|
||||||
)?.let { pendingIntent ->
|
)?.let { pendingIntent ->
|
||||||
passkeyEntries.add(
|
passkeyEntries.add(
|
||||||
PublicKeyCredentialEntry(
|
PublicKeyCredentialEntry(
|
||||||
@@ -257,14 +286,17 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
|
|
||||||
private fun MutableList<CreateEntry>.addPendingIntentCreationNewEntry(
|
private fun MutableList<CreateEntry>.addPendingIntentCreationNewEntry(
|
||||||
accountName: String,
|
accountName: String,
|
||||||
searchInfo: SearchInfo?
|
searchInfo: SearchInfo?,
|
||||||
|
userVerification: UserVerificationRequirement
|
||||||
) {
|
) {
|
||||||
Log.d(TAG, "Add pending intent for registration in opened database to create new item")
|
Log.d(TAG, "Add pending intent for registration in opened database to create new item")
|
||||||
// TODO add a setting to directly store in a specific group
|
// TODO add a setting to directly store in a specific group
|
||||||
PasskeyLauncherActivity.getPendingIntent(
|
PasskeyLauncherActivity.getPendingIntent(
|
||||||
context = applicationContext,
|
context = applicationContext,
|
||||||
specialMode = SpecialMode.REGISTRATION,
|
specialMode = SpecialMode.REGISTRATION,
|
||||||
searchInfo = searchInfo
|
searchInfo = searchInfo,
|
||||||
|
userVerification = userVerification,
|
||||||
|
userVerifiedWithAuth = false
|
||||||
)?.let { pendingIntent ->
|
)?.let { pendingIntent ->
|
||||||
this.add(
|
this.add(
|
||||||
CreateEntry(
|
CreateEntry(
|
||||||
@@ -287,11 +319,13 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
getString(R.string.passkey_database_username)
|
getString(R.string.passkey_database_username)
|
||||||
else databaseName
|
else databaseName
|
||||||
val createEntries: MutableList<CreateEntry> = mutableListOf()
|
val createEntries: MutableList<CreateEntry> = mutableListOf()
|
||||||
val relyingPartyId = PublicKeyCredentialCreationOptions(
|
val publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions(
|
||||||
requestJson = request.requestJson,
|
requestJson = request.requestJson,
|
||||||
clientDataHash = request.clientDataHash
|
clientDataHash = request.clientDataHash
|
||||||
).relyingPartyEntity.id
|
)
|
||||||
|
val relyingPartyId = publicKeyCredentialCreationOptions.relyingPartyEntity.id
|
||||||
val searchInfo = buildPasskeySearchInfo(relyingPartyId)
|
val searchInfo = buildPasskeySearchInfo(relyingPartyId)
|
||||||
|
val userVerification = publicKeyCredentialCreationOptions.authenticatorSelection.userVerification
|
||||||
Log.d(TAG, "Build passkey search for relying party $relyingPartyId")
|
Log.d(TAG, "Build passkey search for relying party $relyingPartyId")
|
||||||
SearchHelper.checkAutoSearchInfo(
|
SearchHelper.checkAutoSearchInfo(
|
||||||
context = this,
|
context = this,
|
||||||
@@ -302,7 +336,11 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
throw RegisterInReadOnlyDatabaseException()
|
throw RegisterInReadOnlyDatabaseException()
|
||||||
} else {
|
} else {
|
||||||
// To create a new entry
|
// To create a new entry
|
||||||
createEntries.addPendingIntentCreationNewEntry(accountName, searchInfo)
|
createEntries.addPendingIntentCreationNewEntry(
|
||||||
|
accountName = accountName,
|
||||||
|
searchInfo = searchInfo,
|
||||||
|
userVerification = userVerification
|
||||||
|
)
|
||||||
/* TODO Overwrite
|
/* TODO Overwrite
|
||||||
// To select an existing entry and permit an overwrite
|
// To select an existing entry and permit an overwrite
|
||||||
Log.w(TAG, "Passkey already registered")
|
Log.w(TAG, "Passkey already registered")
|
||||||
@@ -333,7 +371,11 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
if (database.isReadOnly) {
|
if (database.isReadOnly) {
|
||||||
throw RegisterInReadOnlyDatabaseException()
|
throw RegisterInReadOnlyDatabaseException()
|
||||||
} else {
|
} else {
|
||||||
createEntries.addPendingIntentCreationNewEntry(accountName, searchInfo)
|
createEntries.addPendingIntentCreationNewEntry(
|
||||||
|
accountName = accountName,
|
||||||
|
searchInfo = searchInfo,
|
||||||
|
userVerification = userVerification
|
||||||
|
)
|
||||||
}
|
}
|
||||||
callback(createEntries)
|
callback(createEntries)
|
||||||
},
|
},
|
||||||
@@ -342,7 +384,8 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
Log.d(TAG, "Add pending intent for passkey registration in closed database")
|
Log.d(TAG, "Add pending intent for passkey registration in closed database")
|
||||||
PasskeyLauncherActivity.getPendingIntent(
|
PasskeyLauncherActivity.getPendingIntent(
|
||||||
context = applicationContext,
|
context = applicationContext,
|
||||||
specialMode = SpecialMode.REGISTRATION
|
specialMode = SpecialMode.REGISTRATION,
|
||||||
|
userVerifiedWithAuth = true
|
||||||
)?.let { pendingIntent ->
|
)?.let { pendingIntent ->
|
||||||
createEntries.add(
|
createEntries.add(
|
||||||
CreateEntry(
|
CreateEntry(
|
||||||
|
|||||||
@@ -16,7 +16,25 @@
|
|||||||
|
|
||||||
package com.kunzisoft.keepass.credentialprovider.passkey.data
|
package com.kunzisoft.keepass.credentialprovider.passkey.data
|
||||||
|
|
||||||
data class PublicKeyCredentialRpEntity(val name: String, val id: String)
|
import com.kunzisoft.encrypt.Base64Helper
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
data class PublicKeyCredentialRpEntity(
|
||||||
|
val name: String,
|
||||||
|
val id: String
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun JSONObject.getPublicKeyCredentialRpEntity(
|
||||||
|
parameterName: String
|
||||||
|
): PublicKeyCredentialRpEntity {
|
||||||
|
val rpJson = this.getJSONObject(parameterName)
|
||||||
|
return PublicKeyCredentialRpEntity(
|
||||||
|
rpJson.getString("name"),
|
||||||
|
rpJson.getString("id")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class PublicKeyCredentialUserEntity(
|
data class PublicKeyCredentialUserEntity(
|
||||||
val name: String,
|
val name: String,
|
||||||
@@ -42,9 +60,41 @@ data class PublicKeyCredentialUserEntity(
|
|||||||
result = 31 * result + displayName.hashCode()
|
result = 31 * result + displayName.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun JSONObject.getPublicKeyCredentialUserEntity(
|
||||||
|
parameterName: String
|
||||||
|
): PublicKeyCredentialUserEntity {
|
||||||
|
val rpUser = this.getJSONObject(parameterName)
|
||||||
|
return PublicKeyCredentialUserEntity(
|
||||||
|
rpUser.getString("name"),
|
||||||
|
Base64Helper.b64Decode(rpUser.getString("id")),
|
||||||
|
rpUser.getString("displayName")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PublicKeyCredentialParameters(val type: String, val alg: Long)
|
data class PublicKeyCredentialParameters(
|
||||||
|
val type: String,
|
||||||
|
val alg: Long
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun JSONObject.getPublicKeyCredentialParametersList(
|
||||||
|
parameterName: String
|
||||||
|
): List<PublicKeyCredentialParameters> {
|
||||||
|
val pubKeyCredParamsJson = this.getJSONArray(parameterName)
|
||||||
|
val pubKeyCredParamsTmp: MutableList<PublicKeyCredentialParameters> = mutableListOf()
|
||||||
|
for (i in 0 until pubKeyCredParamsJson.length()) {
|
||||||
|
val e = pubKeyCredParamsJson.getJSONObject(i)
|
||||||
|
pubKeyCredParamsTmp.add(
|
||||||
|
PublicKeyCredentialParameters(e.getString("type"), e.getLong("alg"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return pubKeyCredParamsTmp.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class PublicKeyCredentialDescriptor(
|
data class PublicKeyCredentialDescriptor(
|
||||||
val type: String,
|
val type: String,
|
||||||
@@ -70,11 +120,104 @@ data class PublicKeyCredentialDescriptor(
|
|||||||
result = 31 * result + transports.hashCode()
|
result = 31 * result + transports.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun JSONObject.getPublicKeyCredentialDescriptorList(
|
||||||
|
parameterName: String
|
||||||
|
): List<PublicKeyCredentialDescriptor> {
|
||||||
|
val credentialsJson = this.getJSONArray(parameterName)
|
||||||
|
val credentialsTmp: MutableList<PublicKeyCredentialDescriptor> = mutableListOf()
|
||||||
|
for (i in 0 until credentialsJson.length()) {
|
||||||
|
val credentialJson = credentialsJson.getJSONObject(i)
|
||||||
|
|
||||||
|
val transports: MutableList<String> = mutableListOf()
|
||||||
|
val transportsJson = credentialJson.getJSONArray("transports")
|
||||||
|
for (j in 0 until transportsJson.length()) {
|
||||||
|
transports.add(transportsJson.getString(j))
|
||||||
|
}
|
||||||
|
credentialsTmp.add(
|
||||||
|
PublicKeyCredentialDescriptor(
|
||||||
|
type = credentialJson.getString("type"),
|
||||||
|
id = Base64Helper.b64Decode(credentialJson.getString("id")),
|
||||||
|
transports = transports
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return credentialsTmp.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorselectioncriteria
|
||||||
data class AuthenticatorSelectionCriteria(
|
data class AuthenticatorSelectionCriteria(
|
||||||
val authenticatorAttachment: String,
|
val authenticatorAttachment: String? = null,
|
||||||
val residentKey: String,
|
val residentKey: ResidentKeyRequirement? = null,
|
||||||
val requireResidentKey: Boolean = false,
|
val requireResidentKey: Boolean?,
|
||||||
val userVerification: String = "preferred"
|
val userVerification: UserVerificationRequirement = UserVerificationRequirement.PREFERRED
|
||||||
)
|
) {
|
||||||
|
companion object {
|
||||||
|
fun JSONObject.getAuthenticatorSelectionCriteria(
|
||||||
|
parameterName: String
|
||||||
|
): AuthenticatorSelectionCriteria {
|
||||||
|
val authenticatorSelection = this.optJSONObject(parameterName)
|
||||||
|
?: return AuthenticatorSelectionCriteria(requireResidentKey = null)
|
||||||
|
val authenticatorAttachment = if (!authenticatorSelection.isNull("authenticatorAttachment"))
|
||||||
|
authenticatorSelection.getString("authenticatorAttachment") else null
|
||||||
|
var residentKey = if (!authenticatorSelection.isNull("residentKey"))
|
||||||
|
ResidentKeyRequirement.fromString(authenticatorSelection.getString("residentKey"))
|
||||||
|
else null
|
||||||
|
val requireResidentKey = authenticatorSelection.optBoolean("requireResidentKey", false)
|
||||||
|
val userVerification = UserVerificationRequirement
|
||||||
|
.fromString(authenticatorSelection.optString("userVerification", "preferred"))
|
||||||
|
?: UserVerificationRequirement.PREFERRED
|
||||||
|
// https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement
|
||||||
|
if (residentKey == null) {
|
||||||
|
residentKey = if (requireResidentKey) {
|
||||||
|
ResidentKeyRequirement.REQUIRED
|
||||||
|
} else {
|
||||||
|
ResidentKeyRequirement.DISCOURAGED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AuthenticatorSelectionCriteria(
|
||||||
|
authenticatorAttachment = authenticatorAttachment,
|
||||||
|
residentKey = residentKey,
|
||||||
|
requireResidentKey = requireResidentKey,
|
||||||
|
userVerification = userVerification
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement
|
||||||
|
enum class ResidentKeyRequirement(val value: String) {
|
||||||
|
DISCOURAGED("discouraged"),
|
||||||
|
PREFERRED("preferred"),
|
||||||
|
REQUIRED("required");
|
||||||
|
override fun toString(): String {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
companion object {
|
||||||
|
fun fromString(value: String): ResidentKeyRequirement? {
|
||||||
|
return ResidentKeyRequirement.entries.firstOrNull {
|
||||||
|
it.value.equals(other = value, ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement
|
||||||
|
enum class UserVerificationRequirement(val value: String) {
|
||||||
|
REQUIRED("required"),
|
||||||
|
PREFERRED("preferred"),
|
||||||
|
DISCOURAGED("discouraged");
|
||||||
|
override fun toString(): String {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
companion object {
|
||||||
|
fun fromString(value: String): UserVerificationRequirement? {
|
||||||
|
return UserVerificationRequirement.entries.firstOrNull {
|
||||||
|
it.value.equals(other = value, ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,52 +20,42 @@
|
|||||||
package com.kunzisoft.keepass.credentialprovider.passkey.data
|
package com.kunzisoft.keepass.credentialprovider.passkey.data
|
||||||
|
|
||||||
import com.kunzisoft.encrypt.Base64Helper
|
import com.kunzisoft.encrypt.Base64Helper
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.AuthenticatorSelectionCriteria.Companion.getAuthenticatorSelectionCriteria
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialDescriptor.Companion.getPublicKeyCredentialDescriptorList
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialParameters.Companion.getPublicKeyCredentialParametersList
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialRpEntity.Companion.getPublicKeyCredentialRpEntity
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialUserEntity.Companion.getPublicKeyCredentialUserEntity
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class PublicKeyCredentialCreationOptions(
|
class PublicKeyCredentialCreationOptions(
|
||||||
requestJson: String,
|
requestJson: String,
|
||||||
var clientDataHash: ByteArray?
|
var clientDataHash: ByteArray?
|
||||||
) {
|
) {
|
||||||
val json: JSONObject = JSONObject(requestJson)
|
private val json: JSONObject = JSONObject(requestJson)
|
||||||
|
|
||||||
val relyingPartyEntity: PublicKeyCredentialRpEntity
|
val relyingPartyEntity: PublicKeyCredentialRpEntity =
|
||||||
val userEntity: PublicKeyCredentialUserEntity
|
json.getPublicKeyCredentialRpEntity("rp")
|
||||||
val challenge: ByteArray
|
|
||||||
val pubKeyCredParams: List<PublicKeyCredentialParameters>
|
|
||||||
|
|
||||||
var timeout: Long
|
val userEntity: PublicKeyCredentialUserEntity =
|
||||||
var excludeCredentials: List<PublicKeyCredentialDescriptor>
|
json.getPublicKeyCredentialUserEntity("user")
|
||||||
var authenticatorSelection: AuthenticatorSelectionCriteria
|
|
||||||
var attestation: String
|
|
||||||
|
|
||||||
init {
|
val challenge: ByteArray =
|
||||||
val rpJson = json.getJSONObject("rp")
|
Base64Helper.b64Decode(json.getString("challenge"))
|
||||||
relyingPartyEntity = PublicKeyCredentialRpEntity(rpJson.getString("name"), rpJson.getString("id"))
|
|
||||||
val rpUser = json.getJSONObject("user")
|
|
||||||
val userId = Base64Helper.b64Decode(rpUser.getString("id"))
|
|
||||||
userEntity =
|
|
||||||
PublicKeyCredentialUserEntity(
|
|
||||||
rpUser.getString("name"),
|
|
||||||
userId,
|
|
||||||
rpUser.getString("displayName")
|
|
||||||
)
|
|
||||||
challenge = Base64Helper.b64Decode(json.getString("challenge"))
|
|
||||||
val pubKeyCredParamsJson = json.getJSONArray("pubKeyCredParams")
|
|
||||||
val pubKeyCredParamsTmp: MutableList<PublicKeyCredentialParameters> = mutableListOf()
|
|
||||||
for (i in 0 until pubKeyCredParamsJson.length()) {
|
|
||||||
val e = pubKeyCredParamsJson.getJSONObject(i)
|
|
||||||
pubKeyCredParamsTmp.add(
|
|
||||||
PublicKeyCredentialParameters(e.getString("type"), e.getLong("alg"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pubKeyCredParams = pubKeyCredParamsTmp.toList()
|
|
||||||
|
|
||||||
timeout = json.optLong("timeout", 0)
|
val pubKeyCredParams: List<PublicKeyCredentialParameters> =
|
||||||
// TODO: Fix excludeCredentials and authenticatorSelection
|
json.getPublicKeyCredentialParametersList("pubKeyCredParams")
|
||||||
excludeCredentials = emptyList()
|
|
||||||
authenticatorSelection = AuthenticatorSelectionCriteria("platform", "required")
|
var timeout: Long =
|
||||||
attestation = json.optString("attestation", "none")
|
json.optLong("timeout", 0)
|
||||||
}
|
|
||||||
|
var excludeCredentials: List<PublicKeyCredentialDescriptor> =
|
||||||
|
json.getPublicKeyCredentialDescriptorList("excludeCredentials")
|
||||||
|
|
||||||
|
var authenticatorSelection: AuthenticatorSelectionCriteria =
|
||||||
|
json.getAuthenticatorSelectionCriteria("authenticatorSelection")
|
||||||
|
|
||||||
|
var attestation: String =
|
||||||
|
json.optString("attestation", "none")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = PublicKeyCredentialCreationOptions::class.simpleName
|
private val TAG = PublicKeyCredentialCreationOptions::class.simpleName
|
||||||
|
|||||||
@@ -20,12 +20,33 @@
|
|||||||
package com.kunzisoft.keepass.credentialprovider.passkey.data
|
package com.kunzisoft.keepass.credentialprovider.passkey.data
|
||||||
|
|
||||||
import com.kunzisoft.encrypt.Base64Helper
|
import com.kunzisoft.encrypt.Base64Helper
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.PublicKeyCredentialDescriptor.Companion.getPublicKeyCredentialDescriptorList
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement
|
||||||
class PublicKeyCredentialRequestOptions(requestJson: String) {
|
class PublicKeyCredentialRequestOptions(requestJson: String) {
|
||||||
val json: JSONObject = JSONObject(requestJson)
|
private val json: JSONObject = JSONObject(requestJson)
|
||||||
val challenge: ByteArray = Base64Helper.b64Decode(json.getString("challenge"))
|
|
||||||
val timeout: Long = json.optLong("timeout", 0)
|
val challenge: ByteArray =
|
||||||
val rpId: String = json.optString("rpId", "")
|
Base64Helper.b64Decode(json.getString("challenge"))
|
||||||
val userVerification: String = json.optString("userVerification", "preferred")
|
|
||||||
|
val timeout: Long =
|
||||||
|
json.optLong("timeout", 0)
|
||||||
|
|
||||||
|
val rpId: String =
|
||||||
|
json.optString("rpId", "")
|
||||||
|
|
||||||
|
val allowCredentials: List<PublicKeyCredentialDescriptor> =
|
||||||
|
json.getPublicKeyCredentialDescriptorList("allowCredentials")
|
||||||
|
|
||||||
|
val userVerification: UserVerificationRequirement =
|
||||||
|
UserVerificationRequirement.fromString(
|
||||||
|
json.optString("userVerification", "preferred"))
|
||||||
|
?: UserVerificationRequirement.PREFERRED
|
||||||
|
|
||||||
|
// TODO Hints
|
||||||
|
val hints: List<String> = listOf()
|
||||||
|
|
||||||
|
// TODO Extensions
|
||||||
|
// val extensions: AuthenticationExtensionsClientInputs
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.credentialprovider.passkey.util
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.res.Resources
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.security.keystore.KeyGenParameterSpec
|
import android.security.keystore.KeyGenParameterSpec
|
||||||
@@ -200,6 +201,28 @@ object PasskeyHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the Passkey error response
|
||||||
|
*/
|
||||||
|
fun Activity.buildPasskeyErrorAndSetResult(
|
||||||
|
resources: Resources,
|
||||||
|
relyingPartyId: String?,
|
||||||
|
credentialIds: List<String>
|
||||||
|
) {
|
||||||
|
val error = resources.getString(
|
||||||
|
R.string.error_passkey_credential_id,
|
||||||
|
relyingPartyId,
|
||||||
|
credentialIds
|
||||||
|
)
|
||||||
|
Log.e(javaClass.name, error)
|
||||||
|
Toast.makeText(
|
||||||
|
this,
|
||||||
|
error,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
setResult(Activity.RESULT_CANCELED)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the timestamp and authentication code transmitted via PendingIntent
|
* Check the timestamp and authentication code transmitted via PendingIntent
|
||||||
*/
|
*/
|
||||||
@@ -471,6 +494,7 @@ object PasskeyHelper {
|
|||||||
*/
|
*/
|
||||||
fun buildCreatePublicKeyCredentialResponse(
|
fun buildCreatePublicKeyCredentialResponse(
|
||||||
publicKeyCredentialCreationParameters: PublicKeyCredentialCreationParameters,
|
publicKeyCredentialCreationParameters: PublicKeyCredentialCreationParameters,
|
||||||
|
userVerified: Boolean,
|
||||||
backupEligibility: Boolean,
|
backupEligibility: Boolean,
|
||||||
backupState: Boolean
|
backupState: Boolean
|
||||||
): CreatePublicKeyCredentialResponse {
|
): CreatePublicKeyCredentialResponse {
|
||||||
@@ -488,7 +512,7 @@ object PasskeyHelper {
|
|||||||
keyTypeId = keyTypeId
|
keyTypeId = keyTypeId
|
||||||
) ?: mapOf<Int, Any>()),
|
) ?: mapOf<Int, Any>()),
|
||||||
userPresent = true,
|
userPresent = true,
|
||||||
userVerified = true,
|
userVerified = userVerified,
|
||||||
backupEligibility = backupEligibility,
|
backupEligibility = backupEligibility,
|
||||||
backupState = backupState,
|
backupState = backupState,
|
||||||
publicKeyTypeId = keyTypeId,
|
publicKeyTypeId = keyTypeId,
|
||||||
@@ -560,6 +584,7 @@ object PasskeyHelper {
|
|||||||
requestOptions: PublicKeyCredentialRequestOptions,
|
requestOptions: PublicKeyCredentialRequestOptions,
|
||||||
clientDataResponse: ClientDataResponse,
|
clientDataResponse: ClientDataResponse,
|
||||||
passkey: Passkey,
|
passkey: Passkey,
|
||||||
|
userVerified: Boolean,
|
||||||
defaultBackupEligibility: Boolean,
|
defaultBackupEligibility: Boolean,
|
||||||
defaultBackupState: Boolean
|
defaultBackupState: Boolean
|
||||||
): PublicKeyCredential {
|
): PublicKeyCredential {
|
||||||
@@ -568,7 +593,7 @@ object PasskeyHelper {
|
|||||||
response = AuthenticatorAssertionResponse(
|
response = AuthenticatorAssertionResponse(
|
||||||
requestOptions = requestOptions,
|
requestOptions = requestOptions,
|
||||||
userPresent = true,
|
userPresent = true,
|
||||||
userVerified = true,
|
userVerified = userVerified,
|
||||||
backupEligibility = passkey.backupEligibility ?: defaultBackupEligibility,
|
backupEligibility = passkey.backupEligibility ?: defaultBackupEligibility,
|
||||||
backupState = passkey.backupState ?: defaultBackupState,
|
backupState = passkey.backupState ?: defaultBackupState,
|
||||||
userHandle = passkey.userHandle,
|
userHandle = passkey.userHandle,
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import android.util.Log
|
|||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.removeNodesIds
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveAndRemoveEntries
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveNodesIds
|
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveRegisterInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveRegisterInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSpecialMode
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSpecialMode
|
||||||
@@ -21,7 +20,7 @@ import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper
|
|||||||
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.retrieveAutofillComponent
|
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.retrieveAutofillComponent
|
||||||
import com.kunzisoft.keepass.credentialprovider.autofill.KeeAutofillService
|
import com.kunzisoft.keepass.credentialprovider.autofill.KeeAutofillService
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseException
|
||||||
import com.kunzisoft.keepass.database.helper.SearchHelper
|
import com.kunzisoft.keepass.database.helper.SearchHelper
|
||||||
import com.kunzisoft.keepass.model.RegisterInfo
|
import com.kunzisoft.keepass.model.RegisterInfo
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
@@ -119,7 +118,7 @@ class AutofillLauncherViewModel(application: Application): CredentialLauncherVie
|
|||||||
onItemNotFound = { openedDatabase ->
|
onItemNotFound = { openedDatabase ->
|
||||||
// Show the database UI to select the entry
|
// Show the database UI to select the entry
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchGroupActivityForSelection(
|
CredentialState.LaunchGroupActivityForSelection(
|
||||||
database = openedDatabase,
|
database = openedDatabase,
|
||||||
searchInfo = searchInfo,
|
searchInfo = searchInfo,
|
||||||
typeMode = TypeMode.AUTOFILL
|
typeMode = TypeMode.AUTOFILL
|
||||||
@@ -128,7 +127,7 @@ class AutofillLauncherViewModel(application: Application): CredentialLauncherVie
|
|||||||
onDatabaseClosed = {
|
onDatabaseClosed = {
|
||||||
// If database not open
|
// If database not open
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchFileDatabaseSelectActivityForSelection(
|
CredentialState.LaunchFileDatabaseSelectActivityForSelection(
|
||||||
searchInfo = searchInfo,
|
searchInfo = searchInfo,
|
||||||
typeMode = TypeMode.AUTOFILL
|
typeMode = TypeMode.AUTOFILL
|
||||||
)
|
)
|
||||||
@@ -156,17 +155,10 @@ class AutofillLauncherViewModel(application: Application): CredentialLauncherVie
|
|||||||
Log.d(TAG, "Autofill selection result")
|
Log.d(TAG, "Autofill selection result")
|
||||||
if (intent == null)
|
if (intent == null)
|
||||||
throw IOException("Intent is null")
|
throw IOException("Intent is null")
|
||||||
val nodesIds = intent.retrieveNodesIds()
|
val entries = intent.retrieveAndRemoveEntries(database)
|
||||||
?: throw IOException("NodesIds is null")
|
|
||||||
intent.removeNodesIds()
|
|
||||||
val autofillComponent = mAutofillComponent
|
val autofillComponent = mAutofillComponent
|
||||||
if (autofillComponent == null)
|
if (autofillComponent == null)
|
||||||
throw IOException("Autofill component is null")
|
throw IOException("Autofill component is null")
|
||||||
val entries = nodesIds.mapNotNull { nodeId ->
|
|
||||||
database
|
|
||||||
.getEntryById(NodeIdUUID(nodeId))
|
|
||||||
?.getEntryInfo(database)
|
|
||||||
}
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
AutofillHelper.buildResponse(
|
AutofillHelper.buildResponse(
|
||||||
context = getApplication(),
|
context = getApplication(),
|
||||||
@@ -211,32 +203,36 @@ class AutofillLauncherViewModel(application: Application): CredentialLauncherVie
|
|||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
// Show the database UI to select the entry
|
// Show the database UI to select the entry
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchGroupActivityForRegistration(
|
CredentialState.LaunchGroupActivityForRegistration(
|
||||||
database = openedDatabase,
|
database = openedDatabase,
|
||||||
registerInfo = registerInfo,
|
registerInfo = registerInfo,
|
||||||
typeMode = TypeMode.AUTOFILL
|
typeMode = TypeMode.AUTOFILL
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
mUiState.value = UIState.ShowReadOnlyMessage
|
mCredentialUiState.value = CredentialState.ShowError(
|
||||||
|
RegisterInReadOnlyDatabaseException()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onItemNotFound = { openedDatabase ->
|
onItemNotFound = { openedDatabase ->
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
// Show the database UI to select the entry
|
// Show the database UI to select the entry
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchGroupActivityForRegistration(
|
CredentialState.LaunchGroupActivityForRegistration(
|
||||||
database = openedDatabase,
|
database = openedDatabase,
|
||||||
registerInfo = registerInfo,
|
registerInfo = registerInfo,
|
||||||
typeMode = TypeMode.AUTOFILL
|
typeMode = TypeMode.AUTOFILL
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
mUiState.value = UIState.ShowReadOnlyMessage
|
mCredentialUiState.value = CredentialState.ShowError(
|
||||||
|
RegisterInReadOnlyDatabaseException()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDatabaseClosed = {
|
onDatabaseClosed = {
|
||||||
// If database not open
|
// If database not open
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchFileDatabaseSelectActivityForRegistration(
|
CredentialState.LaunchFileDatabaseSelectActivityForRegistration(
|
||||||
registerInfo = registerInfo,
|
registerInfo = registerInfo,
|
||||||
typeMode = TypeMode.AUTOFILL
|
typeMode = TypeMode.AUTOFILL
|
||||||
)
|
)
|
||||||
@@ -274,7 +270,6 @@ class AutofillLauncherViewModel(application: Application): CredentialLauncherVie
|
|||||||
sealed class UIState {
|
sealed class UIState {
|
||||||
object Loading: UIState()
|
object Loading: UIState()
|
||||||
object ShowBlockRestartMessage: UIState()
|
object ShowBlockRestartMessage: UIState()
|
||||||
object ShowReadOnlyMessage: UIState()
|
|
||||||
object ShowAutofillSuggestionMessage: UIState()
|
object ShowAutofillSuggestionMessage: UIState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,13 +24,12 @@ abstract class CredentialLauncherViewModel(application: Application): AndroidVie
|
|||||||
|
|
||||||
protected var isResultLauncherRegistered: Boolean = false
|
protected var isResultLauncherRegistered: Boolean = false
|
||||||
private var mSelectionResult: ActivityResult? = null
|
private var mSelectionResult: ActivityResult? = null
|
||||||
|
protected val mCredentialUiState = MutableStateFlow<CredentialState>(CredentialState.Loading)
|
||||||
protected val mCredentialUiState = MutableStateFlow<UIState>(UIState.Loading)
|
val credentialUiState: StateFlow<CredentialState> = mCredentialUiState
|
||||||
val credentialUiState: StateFlow<UIState> = mCredentialUiState
|
|
||||||
|
|
||||||
fun showError(error: Throwable) {
|
fun showError(error: Throwable) {
|
||||||
Log.e(TAG, "Error on credential provider launch", error)
|
Log.e(TAG, "Error on credential provider launch", error)
|
||||||
mCredentialUiState.value = UIState.ShowError(error)
|
mCredentialUiState.value = CredentialState.ShowError(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun onResult() {
|
open fun onResult() {
|
||||||
@@ -41,7 +40,7 @@ abstract class CredentialLauncherViewModel(application: Application): AndroidVie
|
|||||||
fun setResult(intent: Intent, lockDatabase: Boolean = false) {
|
fun setResult(intent: Intent, lockDatabase: Boolean = false) {
|
||||||
// Remove the launcher register
|
// Remove the launcher register
|
||||||
onResult()
|
onResult()
|
||||||
mCredentialUiState.value = UIState.SetActivityResult(
|
mCredentialUiState.value = CredentialState.SetActivityResult(
|
||||||
lockDatabase = lockDatabase,
|
lockDatabase = lockDatabase,
|
||||||
resultCode = RESULT_OK,
|
resultCode = RESULT_OK,
|
||||||
data = intent
|
data = intent
|
||||||
@@ -50,13 +49,13 @@ abstract class CredentialLauncherViewModel(application: Application): AndroidVie
|
|||||||
|
|
||||||
fun cancelResult(lockDatabase: Boolean = false) {
|
fun cancelResult(lockDatabase: Boolean = false) {
|
||||||
onResult()
|
onResult()
|
||||||
mCredentialUiState.value = UIState.SetActivityResult(
|
mCredentialUiState.value = CredentialState.SetActivityResult(
|
||||||
lockDatabase = lockDatabase,
|
lockDatabase = lockDatabase,
|
||||||
resultCode = RESULT_CANCELED
|
resultCode = RESULT_CANCELED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onDatabaseRetrieved(database: ContextualDatabase) {
|
fun onDatabaseRetrieved(database: ContextualDatabase) {
|
||||||
mDatabase = database
|
mDatabase = database
|
||||||
mSelectionResult?.let { selectionResult ->
|
mSelectionResult?.let { selectionResult ->
|
||||||
manageSelectionResult(database, selectionResult)
|
manageSelectionResult(database, selectionResult)
|
||||||
@@ -115,34 +114,34 @@ abstract class CredentialLauncherViewModel(application: Application): AndroidVie
|
|||||||
database: ContextualDatabase?
|
database: ContextualDatabase?
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed class UIState {
|
sealed class CredentialState {
|
||||||
object Loading : UIState()
|
object Loading : CredentialState()
|
||||||
data class LaunchGroupActivityForSelection(
|
data class LaunchGroupActivityForSelection(
|
||||||
val database: ContextualDatabase,
|
val database: ContextualDatabase,
|
||||||
val searchInfo: SearchInfo?,
|
val searchInfo: SearchInfo?,
|
||||||
val typeMode: TypeMode
|
val typeMode: TypeMode
|
||||||
): UIState()
|
): CredentialState()
|
||||||
data class LaunchGroupActivityForRegistration(
|
data class LaunchGroupActivityForRegistration(
|
||||||
val database: ContextualDatabase,
|
val database: ContextualDatabase,
|
||||||
val registerInfo: RegisterInfo?,
|
val registerInfo: RegisterInfo?,
|
||||||
val typeMode: TypeMode
|
val typeMode: TypeMode
|
||||||
): UIState()
|
): CredentialState()
|
||||||
data class LaunchFileDatabaseSelectActivityForSelection(
|
data class LaunchFileDatabaseSelectActivityForSelection(
|
||||||
val searchInfo: SearchInfo?,
|
val searchInfo: SearchInfo?,
|
||||||
val typeMode: TypeMode
|
val typeMode: TypeMode
|
||||||
): UIState()
|
): CredentialState()
|
||||||
data class LaunchFileDatabaseSelectActivityForRegistration(
|
data class LaunchFileDatabaseSelectActivityForRegistration(
|
||||||
val registerInfo: RegisterInfo?,
|
val registerInfo: RegisterInfo?,
|
||||||
val typeMode: TypeMode
|
val typeMode: TypeMode
|
||||||
): UIState()
|
): CredentialState()
|
||||||
data class SetActivityResult(
|
data class SetActivityResult(
|
||||||
val lockDatabase: Boolean,
|
val lockDatabase: Boolean,
|
||||||
val resultCode: Int,
|
val resultCode: Int,
|
||||||
val data: Intent? = null
|
val data: Intent? = null
|
||||||
): UIState()
|
): CredentialState()
|
||||||
data class ShowError(
|
data class ShowError(
|
||||||
val error: Throwable
|
val error: Throwable
|
||||||
): UIState()
|
): CredentialState()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -0,0 +1,297 @@
|
|||||||
|
package com.kunzisoft.keepass.credentialprovider.viewmodel
|
||||||
|
|
||||||
|
import android.app.Activity.RESULT_CANCELED
|
||||||
|
import android.app.Activity.RESULT_OK
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.result.ActivityResult
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveAndRemoveEntries
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveNodeId
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
||||||
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
|
import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseException
|
||||||
|
import com.kunzisoft.keepass.database.helper.SearchHelper
|
||||||
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
|
import com.kunzisoft.keepass.otp.OtpEntryFields
|
||||||
|
import com.kunzisoft.keepass.utils.KeyboardUtil.isKeyboardActivatedInSettings
|
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class EntrySelectionViewModel(application: Application): CredentialLauncherViewModel(application) {
|
||||||
|
|
||||||
|
private var searchShareForMagikeyboard: Boolean = false
|
||||||
|
private var mLockDatabaseAfterSelection: Boolean = false
|
||||||
|
private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
|
||||||
|
val uiState: StateFlow<UIState> = mUiState
|
||||||
|
|
||||||
|
fun initialize() {
|
||||||
|
searchShareForMagikeyboard = getApplication<Application>().isKeyboardActivatedInSettings()
|
||||||
|
mLockDatabaseAfterSelection = false // TODO Close database after selection
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchActionIfNeeded(
|
||||||
|
intent: Intent,
|
||||||
|
specialMode: SpecialMode,
|
||||||
|
database: ContextualDatabase?
|
||||||
|
) {
|
||||||
|
// Launch with database when a nodeId is present
|
||||||
|
if ((database != null && database.loaded) || intent.retrieveNodeId() == null) {
|
||||||
|
super.launchActionIfNeeded(intent, specialMode, database)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun launchAction(
|
||||||
|
intent: Intent,
|
||||||
|
specialMode: SpecialMode,
|
||||||
|
database: ContextualDatabase?
|
||||||
|
) {
|
||||||
|
val searchInfo: SearchInfo? = intent.retrieveSearchInfo()
|
||||||
|
if (searchInfo != null) {
|
||||||
|
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 = extra.toUri().host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launchSelection(database, sharedWebDomain, otpString)
|
||||||
|
}
|
||||||
|
Intent.ACTION_VIEW -> {
|
||||||
|
// Retrieve OTP
|
||||||
|
intent.dataString?.let { extra ->
|
||||||
|
if (OtpEntryFields.isOTPUri(extra))
|
||||||
|
otpString = extra
|
||||||
|
}
|
||||||
|
launchSelection(database, null, otpString)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (database != null && database.loaded) {
|
||||||
|
mUiState.value = UIState.LaunchGroupActivityForSearch(
|
||||||
|
database = database,
|
||||||
|
searchInfo = SearchInfo()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mUiState.value = UIState.LaunchFileDatabaseSelectForSearch(
|
||||||
|
searchInfo = SearchInfo()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
// Selection
|
||||||
|
// -------------
|
||||||
|
|
||||||
|
private fun launchSelection(
|
||||||
|
database: ContextualDatabase?,
|
||||||
|
sharedWebDomain: String?,
|
||||||
|
otpString: String?
|
||||||
|
) {
|
||||||
|
// Build domain search param
|
||||||
|
val searchInfo = SearchInfo().apply {
|
||||||
|
this.webDomain = sharedWebDomain
|
||||||
|
this.otpString = otpString
|
||||||
|
}
|
||||||
|
launch(database, searchInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun launch(
|
||||||
|
database: ContextualDatabase?,
|
||||||
|
searchInfo: SearchInfo
|
||||||
|
) {
|
||||||
|
// If database is open
|
||||||
|
val readOnly = database?.isReadOnly != false
|
||||||
|
SearchHelper.checkAutoSearchInfo(
|
||||||
|
context = getApplication(),
|
||||||
|
database = database,
|
||||||
|
searchInfo = searchInfo,
|
||||||
|
onItemsFound = { openedDatabase, items ->
|
||||||
|
// Items found
|
||||||
|
if (searchInfo.otpString != null) {
|
||||||
|
if (!readOnly) {
|
||||||
|
mCredentialUiState.value =
|
||||||
|
CredentialState.LaunchGroupActivityForRegistration(
|
||||||
|
database = openedDatabase,
|
||||||
|
registerInfo = searchInfo.toRegisterInfo(),
|
||||||
|
typeMode = TypeMode.DEFAULT
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mCredentialUiState.value = CredentialState.ShowError(
|
||||||
|
RegisterInReadOnlyDatabaseException()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (searchShareForMagikeyboard) {
|
||||||
|
MagikeyboardService.performSelection(
|
||||||
|
items,
|
||||||
|
{ entryInfo ->
|
||||||
|
populateKeyboard(entryInfo)
|
||||||
|
},
|
||||||
|
{ autoSearch ->
|
||||||
|
mCredentialUiState.value = CredentialState.LaunchGroupActivityForSelection(
|
||||||
|
database = openedDatabase,
|
||||||
|
searchInfo = searchInfo,
|
||||||
|
typeMode = TypeMode.MAGIKEYBOARD
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mUiState.value = UIState.LaunchGroupActivityForSearch(
|
||||||
|
database = openedDatabase,
|
||||||
|
searchInfo = searchInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onItemNotFound = { openedDatabase ->
|
||||||
|
// Show the database UI to select the entry
|
||||||
|
if (searchInfo.otpString != null) {
|
||||||
|
if (!readOnly) {
|
||||||
|
mCredentialUiState.value =
|
||||||
|
CredentialState.LaunchGroupActivityForRegistration(
|
||||||
|
database = openedDatabase,
|
||||||
|
registerInfo = searchInfo.toRegisterInfo(),
|
||||||
|
typeMode = TypeMode.DEFAULT
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mCredentialUiState.value = CredentialState.ShowError(
|
||||||
|
RegisterInReadOnlyDatabaseException()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (searchShareForMagikeyboard) {
|
||||||
|
mCredentialUiState.value = CredentialState.LaunchGroupActivityForSelection(
|
||||||
|
database = openedDatabase,
|
||||||
|
searchInfo = searchInfo,
|
||||||
|
typeMode = TypeMode.MAGIKEYBOARD
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mUiState.value = UIState.LaunchGroupActivityForSearch(
|
||||||
|
database = openedDatabase,
|
||||||
|
searchInfo = searchInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDatabaseClosed = {
|
||||||
|
// If database not open
|
||||||
|
if (searchInfo.otpString != null) {
|
||||||
|
mCredentialUiState.value = CredentialState.LaunchFileDatabaseSelectActivityForRegistration(
|
||||||
|
registerInfo = searchInfo.toRegisterInfo(),
|
||||||
|
typeMode = TypeMode.DEFAULT
|
||||||
|
)
|
||||||
|
} else if (searchShareForMagikeyboard) {
|
||||||
|
mCredentialUiState.value = CredentialState.LaunchFileDatabaseSelectActivityForSelection(
|
||||||
|
searchInfo = searchInfo,
|
||||||
|
typeMode = TypeMode.MAGIKEYBOARD
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mUiState.value = UIState.LaunchFileDatabaseSelectForSearch(
|
||||||
|
searchInfo = searchInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateKeyboard(entryInfo: EntryInfo) {
|
||||||
|
// Automatically populate keyboard
|
||||||
|
mUiState.value = UIState.PopulateKeyboard(entryInfo)
|
||||||
|
setResult(Intent(), lockDatabase = mLockDatabaseAfterSelection)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun manageSelectionResult(
|
||||||
|
database: ContextualDatabase,
|
||||||
|
activityResult: ActivityResult
|
||||||
|
) {
|
||||||
|
super.manageSelectionResult(database, activityResult)
|
||||||
|
val intent = activityResult.data
|
||||||
|
viewModelScope.launch(CoroutineExceptionHandler { _, e ->
|
||||||
|
Log.e(TAG, "Unable to create selection response for Magikeyboard", e)
|
||||||
|
showError(e)
|
||||||
|
}) {
|
||||||
|
when (activityResult.resultCode) {
|
||||||
|
RESULT_OK -> {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
Log.d(TAG, "Magikeyboard selection result")
|
||||||
|
if (intent == null)
|
||||||
|
throw IOException("Intent is null")
|
||||||
|
val entries = intent.retrieveAndRemoveEntries(database)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
// Populate Magikeyboard with entry
|
||||||
|
entries.firstOrNull()?.let { entryInfo ->
|
||||||
|
populateKeyboard(entryInfo)
|
||||||
|
} // TODO Manage multiple entries in Magikeyboard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RESULT_CANCELED -> {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
cancelResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun manageRegistrationResult(activityResult: ActivityResult) {
|
||||||
|
super.manageRegistrationResult(activityResult)
|
||||||
|
viewModelScope.launch(CoroutineExceptionHandler { _, e ->
|
||||||
|
Log.e(TAG, "Unable to create selection response for Magikeyboard", e)
|
||||||
|
showError(e)
|
||||||
|
}) {
|
||||||
|
when (activityResult.resultCode) {
|
||||||
|
RESULT_OK -> {
|
||||||
|
// Empty data result
|
||||||
|
// TODO Show Toast indicating value is saved
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
setResult(Intent(), lockDatabase = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RESULT_CANCELED -> {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
cancelResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class UIState {
|
||||||
|
object Loading: UIState()
|
||||||
|
data class PopulateKeyboard(
|
||||||
|
val entryInfo: EntryInfo
|
||||||
|
): UIState()
|
||||||
|
data class LaunchFileDatabaseSelectForSearch(
|
||||||
|
val searchInfo: SearchInfo
|
||||||
|
): UIState()
|
||||||
|
data class LaunchGroupActivityForSearch(
|
||||||
|
val database: ContextualDatabase,
|
||||||
|
val searchInfo: SearchInfo
|
||||||
|
): UIState()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = EntrySelectionViewModel::class.java.name
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,14 +64,16 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
private var mPasskey: Passkey? = null
|
private var mPasskey: Passkey? = null
|
||||||
|
|
||||||
private var mLockDatabaseAfterSelection: Boolean = false
|
private var mLockDatabaseAfterSelection: Boolean = false
|
||||||
|
private var mUserVerified: Boolean = true
|
||||||
private var mBackupEligibility: Boolean = true
|
private var mBackupEligibility: Boolean = true
|
||||||
private var mBackupState: Boolean = false
|
private var mBackupState: Boolean = false
|
||||||
|
|
||||||
private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
|
private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
|
||||||
val uiState: StateFlow<UIState> = mUiState
|
val uiState: StateFlow<UIState> = mUiState
|
||||||
|
|
||||||
fun initialize() {
|
fun initialize(userVerified: Boolean) {
|
||||||
mLockDatabaseAfterSelection = PreferencesUtil.isPasskeyCloseDatabaseEnable(getApplication())
|
mLockDatabaseAfterSelection = PreferencesUtil.isPasskeyCloseDatabaseEnable(getApplication())
|
||||||
|
mUserVerified = userVerified
|
||||||
mBackupEligibility = PreferencesUtil.isPasskeyBackupEligibilityEnable(getApplication())
|
mBackupEligibility = PreferencesUtil.isPasskeyBackupEligibilityEnable(getApplication())
|
||||||
mBackupState = PreferencesUtil.isPasskeyBackupStateEnable(getApplication())
|
mBackupState = PreferencesUtil.isPasskeyBackupStateEnable(getApplication())
|
||||||
}
|
}
|
||||||
@@ -149,6 +151,16 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun launchActionIfNeeded(
|
||||||
|
userVerified: Boolean,
|
||||||
|
intent: Intent,
|
||||||
|
specialMode: SpecialMode,
|
||||||
|
database: ContextualDatabase?
|
||||||
|
) {
|
||||||
|
this.mUserVerified = userVerified
|
||||||
|
launchActionIfNeeded(intent, specialMode, database)
|
||||||
|
}
|
||||||
|
|
||||||
override fun launchActionIfNeeded(
|
override fun launchActionIfNeeded(
|
||||||
intent: Intent,
|
intent: Intent,
|
||||||
specialMode: SpecialMode,
|
specialMode: SpecialMode,
|
||||||
@@ -238,7 +250,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
"launch manual selection in opened database"
|
"launch manual selection in opened database"
|
||||||
)
|
)
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchGroupActivityForSelection(
|
CredentialState.LaunchGroupActivityForSelection(
|
||||||
database = openedDatabase,
|
database = openedDatabase,
|
||||||
searchInfo = searchInfo,
|
searchInfo = searchInfo,
|
||||||
typeMode = TypeMode.PASSKEY
|
typeMode = TypeMode.PASSKEY
|
||||||
@@ -247,7 +259,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
onDatabaseClosed = {
|
onDatabaseClosed = {
|
||||||
Log.d(TAG, "Manual passkey selection in closed database")
|
Log.d(TAG, "Manual passkey selection in closed database")
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchFileDatabaseSelectActivityForSelection(
|
CredentialState.LaunchFileDatabaseSelectActivityForSelection(
|
||||||
searchInfo = searchInfo,
|
searchInfo = searchInfo,
|
||||||
typeMode = TypeMode.PASSKEY
|
typeMode = TypeMode.PASSKEY
|
||||||
)
|
)
|
||||||
@@ -307,6 +319,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
appOrigin = appOrigin
|
appOrigin = appOrigin
|
||||||
),
|
),
|
||||||
passkey = passkey,
|
passkey = passkey,
|
||||||
|
userVerified = mUserVerified,
|
||||||
defaultBackupEligibility = mBackupEligibility,
|
defaultBackupEligibility = mBackupEligibility,
|
||||||
defaultBackupState = mBackupState
|
defaultBackupState = mBackupState
|
||||||
)
|
)
|
||||||
@@ -363,6 +376,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
appOrigin = appOrigin
|
appOrigin = appOrigin
|
||||||
),
|
),
|
||||||
passkey = passkey,
|
passkey = passkey,
|
||||||
|
userVerified = mUserVerified,
|
||||||
defaultBackupEligibility = mBackupEligibility,
|
defaultBackupEligibility = mBackupEligibility,
|
||||||
defaultBackupState = mBackupState
|
defaultBackupState = mBackupState
|
||||||
)
|
)
|
||||||
@@ -426,7 +440,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
"but launch manual registration for a new entry"
|
"but launch manual registration for a new entry"
|
||||||
)
|
)
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchGroupActivityForRegistration(
|
CredentialState.LaunchGroupActivityForRegistration(
|
||||||
database = openedDatabase,
|
database = openedDatabase,
|
||||||
registerInfo = registerInfo,
|
registerInfo = registerInfo,
|
||||||
typeMode = TypeMode.PASSKEY
|
typeMode = TypeMode.PASSKEY
|
||||||
@@ -435,7 +449,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
onItemNotFound = { openedDatabase ->
|
onItemNotFound = { openedDatabase ->
|
||||||
Log.d(TAG, "Launch new manual registration in opened database")
|
Log.d(TAG, "Launch new manual registration in opened database")
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchGroupActivityForRegistration(
|
CredentialState.LaunchGroupActivityForRegistration(
|
||||||
database = openedDatabase,
|
database = openedDatabase,
|
||||||
registerInfo = registerInfo,
|
registerInfo = registerInfo,
|
||||||
typeMode = TypeMode.PASSKEY
|
typeMode = TypeMode.PASSKEY
|
||||||
@@ -444,7 +458,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
onDatabaseClosed = {
|
onDatabaseClosed = {
|
||||||
Log.d(TAG, "Manual passkey registration in closed database")
|
Log.d(TAG, "Manual passkey registration in closed database")
|
||||||
mCredentialUiState.value =
|
mCredentialUiState.value =
|
||||||
CredentialLauncherViewModel.UIState.LaunchFileDatabaseSelectActivityForRegistration(
|
CredentialState.LaunchFileDatabaseSelectActivityForRegistration(
|
||||||
registerInfo = registerInfo,
|
registerInfo = registerInfo,
|
||||||
typeMode = TypeMode.PASSKEY
|
typeMode = TypeMode.PASSKEY
|
||||||
)
|
)
|
||||||
@@ -505,6 +519,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
|
|||||||
intent = responseIntent,
|
intent = responseIntent,
|
||||||
response = buildCreatePublicKeyCredentialResponse(
|
response = buildCreatePublicKeyCredentialResponse(
|
||||||
publicKeyCredentialCreationParameters = it,
|
publicKeyCredentialCreationParameters = it,
|
||||||
|
userVerified = mUserVerified,
|
||||||
backupEligibility = passkey?.backupEligibility
|
backupEligibility = passkey?.backupEligibility
|
||||||
?: mBackupEligibility,
|
?: mBackupEligibility,
|
||||||
backupState = passkey?.backupState
|
backupState = passkey?.backupState
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ class DatabaseTaskProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initServiceConnection() {
|
private fun initServiceConnection() {
|
||||||
actionTaskListener?.onActionStopped()
|
|
||||||
if (serviceConnection == null) {
|
if (serviceConnection == null) {
|
||||||
serviceConnection = object : ServiceConnection {
|
serviceConnection = object : ServiceConnection {
|
||||||
override fun onBindingDied(name: ComponentName?) {
|
override fun onBindingDied(name: ComponentName?) {
|
||||||
@@ -239,7 +238,7 @@ class DatabaseTaskProvider(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
context.unregisterReceiver(databaseTaskBroadcastReceiver)
|
context.unregisterReceiver(databaseTaskBroadcastReceiver)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (_: IllegalArgumentException) {
|
||||||
// If receiver not register, do nothing
|
// If receiver not register, do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,7 +319,6 @@ class DatabaseTaskProvider(
|
|||||||
databaseUri: Uri,
|
databaseUri: Uri,
|
||||||
mainCredential: MainCredential
|
mainCredential: MainCredential
|
||||||
) {
|
) {
|
||||||
|
|
||||||
start(Bundle().apply {
|
start(Bundle().apply {
|
||||||
putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, databaseUri)
|
putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, databaseUri)
|
||||||
putParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY, mainCredential)
|
putParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY, mainCredential)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import androidx.annotation.StringRes
|
|||||||
|
|
||||||
data class ProgressMessage(
|
data class ProgressMessage(
|
||||||
@StringRes
|
@StringRes
|
||||||
var titleId: Int,
|
var titleId: Int? = null,
|
||||||
@StringRes
|
@StringRes
|
||||||
var messageId: Int? = null,
|
var messageId: Int? = null,
|
||||||
@StringRes
|
@StringRes
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ object SearchHelper {
|
|||||||
callback.invoke(
|
callback.invoke(
|
||||||
SearchParameters().apply {
|
SearchParameters().apply {
|
||||||
searchQuery = query
|
searchQuery = query
|
||||||
|
searchOptions = optionsString()
|
||||||
allowEmptyQuery = false
|
allowEmptyQuery = false
|
||||||
searchInTitles = false
|
searchInTitles = false
|
||||||
searchInUsernames = false
|
searchInUsernames = false
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (attachmentNotificationList.isEmpty()) {
|
if (attachmentNotificationList.isEmpty()) {
|
||||||
stopSelf()
|
stopService()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class ClipboardEntryNotificationService : LockNotificationService() {
|
|||||||
sendBroadcast(Intent(LOCK_ACTION))
|
sendBroadcast(Intent(LOCK_ACTION))
|
||||||
}
|
}
|
||||||
// Stop the service
|
// Stop the service
|
||||||
stopSelf()
|
stopService()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.activities.GroupActivity
|
import com.kunzisoft.keepass.activities.GroupActivity
|
||||||
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
|
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
|
||||||
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
|
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.activity.HardwareKeyActivity
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.MainCredential
|
import com.kunzisoft.keepass.database.MainCredential
|
||||||
import com.kunzisoft.keepass.database.ProgressMessage
|
import com.kunzisoft.keepass.database.ProgressMessage
|
||||||
@@ -61,7 +62,6 @@ import com.kunzisoft.keepass.database.element.node.Node
|
|||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.database.element.node.Type
|
import com.kunzisoft.keepass.database.element.node.Type
|
||||||
import com.kunzisoft.keepass.hardware.HardwareKey
|
import com.kunzisoft.keepass.hardware.HardwareKey
|
||||||
import com.kunzisoft.keepass.credentialprovider.activity.HardwareKeyActivity
|
|
||||||
import com.kunzisoft.keepass.model.CipherEncryptDatabase
|
import com.kunzisoft.keepass.model.CipherEncryptDatabase
|
||||||
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
|
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
@@ -262,11 +262,12 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
/* Do not stopped here, service cannot be connected
|
||||||
mActionTaskListeners.forEach { actionTaskListener ->
|
mActionTaskListeners.forEach { actionTaskListener ->
|
||||||
actionTaskListener.onActionStopped(
|
actionTaskListener.onActionStopped(
|
||||||
database
|
database
|
||||||
)
|
)
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +339,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
val intentAction = intent?.action
|
val intentAction = intent?.action
|
||||||
|
|
||||||
if (intentAction == null && !database.loaded) {
|
if (intentAction == null && !database.loaded) {
|
||||||
stopSelf()
|
stopService()
|
||||||
}
|
}
|
||||||
|
|
||||||
val actionRunnable: ActionRunnable? = when (intentAction) {
|
val actionRunnable: ActionRunnable? = when (intentAction) {
|
||||||
@@ -447,10 +448,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
TimeoutHelper.releaseTemporarilyDisableTimeout()
|
TimeoutHelper.releaseTemporarilyDisableTimeout()
|
||||||
// Stop service after save if user remove task
|
// Stop service after save if user remove task
|
||||||
if (save && mTaskRemovedRequested) {
|
if (save && mTaskRemovedRequested) {
|
||||||
actionOnLock()
|
stopService()
|
||||||
} else if (TimeoutHelper.checkTimeAndLockIfTimeout(this@DatabaseTaskNotificationService)) {
|
} else if (TimeoutHelper.checkTimeAndLockIfTimeout(this@DatabaseTaskNotificationService)) {
|
||||||
if (!database.loaded) {
|
if (!database.loaded) {
|
||||||
stopSelf()
|
stopService()
|
||||||
} else {
|
} else {
|
||||||
// Restart the service to open lock notification
|
// Restart the service to open lock notification
|
||||||
try {
|
try {
|
||||||
@@ -535,11 +536,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
|
|
||||||
val notificationBuilder = buildNewNotification().apply {
|
val notificationBuilder = buildNewNotification().apply {
|
||||||
setSmallIcon(iconId)
|
setSmallIcon(iconId)
|
||||||
intent?.let {
|
val titleId = mProgressMessage.titleId?.let {
|
||||||
setContentTitle(getString(
|
intent?.getIntExtra(DATABASE_TASK_TITLE_KEY, it)
|
||||||
intent.getIntExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId))
|
} ?: R.string.app_name
|
||||||
)
|
setContentTitle(getString(titleId))
|
||||||
}
|
|
||||||
setAutoCancel(false)
|
setAutoCancel(false)
|
||||||
setContentIntent(null)
|
setContentIntent(null)
|
||||||
}
|
}
|
||||||
@@ -661,7 +661,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateMessage(resId: Int) {
|
private fun updateMessage(resId: Int) {
|
||||||
mProgressMessage.messageId = resId
|
mProgressMessage = mProgressMessage.copy(
|
||||||
|
messageId = resId
|
||||||
|
)
|
||||||
notifyProgressMessage()
|
notifyProgressMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,7 +675,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
updateMessage(R.string.decrypting_db)
|
updateMessage(R.string.decrypting_db)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun actionOnLock() {
|
override fun stopService() {
|
||||||
if (!TimeoutHelper.temporarilyDisableLock) {
|
if (!TimeoutHelper.temporarilyDisableLock) {
|
||||||
closeDatabase(mDatabase)
|
closeDatabase(mDatabase)
|
||||||
// Remove the database during the lock
|
// Remove the database during the lock
|
||||||
@@ -685,7 +687,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
// Remove the lock timer (no more needed if it exists)
|
// Remove the lock timer (no more needed if it exists)
|
||||||
TimeoutHelper.cancelLockTimer(this)
|
TimeoutHelper.cancelLockTimer(this)
|
||||||
// Service is stopped after receive the broadcast
|
// Service is stopped after receive the broadcast
|
||||||
super.actionOnLock()
|
super.stopService()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,7 +731,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
// Close channels
|
// Close channels
|
||||||
closeChallengeResponse()
|
closeChallengeResponse()
|
||||||
// Restore previous message
|
// Restore previous message
|
||||||
mProgressMessage = previousMessage
|
mProgressMessage = previousMessage.apply {
|
||||||
|
cancelable = null
|
||||||
|
}
|
||||||
notifyProgressMessage()
|
notifyProgressMessage()
|
||||||
}
|
}
|
||||||
return response
|
return response
|
||||||
@@ -913,7 +917,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
|
|
||||||
private fun buildDatabaseAssignCredentialActionTask(
|
private fun buildDatabaseAssignCredentialActionTask(
|
||||||
intent: Intent,
|
intent: Intent,
|
||||||
database: ContextualDatabase,
|
database: ContextualDatabase
|
||||||
): ActionRunnable? {
|
): ActionRunnable? {
|
||||||
return if (intent.hasExtra(DATABASE_URI_KEY)
|
return if (intent.hasExtra(DATABASE_URI_KEY)
|
||||||
&& intent.hasExtra(MAIN_CREDENTIAL_KEY)
|
&& intent.hasExtra(MAIN_CREDENTIAL_KEY)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class KeyboardEntryNotificationService : LockNotificationService() {
|
|||||||
sendBroadcast(Intent(LOCK_ACTION))
|
sendBroadcast(Intent(LOCK_ACTION))
|
||||||
}
|
}
|
||||||
// Stop the service
|
// Stop the service
|
||||||
stopSelf()
|
stopService()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package com.kunzisoft.keepass.services
|
package com.kunzisoft.keepass.services
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.core.app.ServiceCompat
|
|
||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
import com.kunzisoft.keepass.utils.LockReceiver
|
import com.kunzisoft.keepass.utils.LockReceiver
|
||||||
import com.kunzisoft.keepass.utils.registerLockReceiver
|
import com.kunzisoft.keepass.utils.registerLockReceiver
|
||||||
@@ -29,13 +28,7 @@ import com.kunzisoft.keepass.utils.unregisterLockReceiver
|
|||||||
abstract class LockNotificationService : NotificationService() {
|
abstract class LockNotificationService : NotificationService() {
|
||||||
|
|
||||||
private var mLockReceiver: LockReceiver = LockReceiver {
|
private var mLockReceiver: LockReceiver = LockReceiver {
|
||||||
actionOnLock()
|
stopService()
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun actionOnLock() {
|
|
||||||
// Stop the service in all cases
|
|
||||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
|
||||||
stopSelf()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
@@ -46,7 +39,7 @@ abstract class LockNotificationService : NotificationService() {
|
|||||||
|
|
||||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||||
if (!TimeoutHelper.temporarilyDisableLock) {
|
if (!TimeoutHelper.temporarilyDisableLock) {
|
||||||
actionOnLock()
|
stopService()
|
||||||
}
|
}
|
||||||
super.onTaskRemoved(rootIntent)
|
super.onTaskRemoved(rootIntent)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import android.util.TypedValue
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.core.app.ServiceCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.stylish.Stylish
|
import com.kunzisoft.keepass.activities.stylish.Stylish
|
||||||
@@ -114,6 +115,12 @@ abstract class NotificationService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun stopService() {
|
||||||
|
// Stop the service in all cases
|
||||||
|
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||||
|
stopSelf()
|
||||||
|
}
|
||||||
|
|
||||||
protected fun defineTimerJob(builder: NotificationCompat.Builder,
|
protected fun defineTimerJob(builder: NotificationCompat.Builder,
|
||||||
type: NotificationServiceType,
|
type: NotificationServiceType,
|
||||||
timeoutMilliseconds: Long,
|
timeoutMilliseconds: Long,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ abstract class ExternalSettingsActivity : DatabaseModeActivity() {
|
|||||||
|
|
||||||
private var lockView: FloatingActionButton? = null
|
private var lockView: FloatingActionButton? = null
|
||||||
|
|
||||||
|
override fun manageDatabaseInfo(): Boolean = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
biometricUnlockEnablePreference.isChecked = false
|
biometricUnlockEnablePreference.isChecked = false
|
||||||
warningMessage(activity, keystoreWarning = true, deleteKeys = true) {
|
warningMessage(activity, keystoreWarning = true, deleteKeys = true) {
|
||||||
biometricUnlockEnablePreference.isChecked = true
|
biometricUnlockEnablePreference.isChecked = true
|
||||||
deviceCredentialUnlockEnablePreference?.isChecked = false
|
deviceCredentialUnlockEnablePreference.isChecked = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
biometricUnlockEnablePreference.isChecked = false
|
biometricUnlockEnablePreference.isChecked = false
|
||||||
@@ -349,7 +349,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
deviceCredentialUnlockEnablePreference.isChecked = false
|
deviceCredentialUnlockEnablePreference.isChecked = false
|
||||||
warningMessage(activity, keystoreWarning = true, deleteKeys = true) {
|
warningMessage(activity, keystoreWarning = true, deleteKeys = true) {
|
||||||
deviceCredentialUnlockEnablePreference.isChecked = true
|
deviceCredentialUnlockEnablePreference.isChecked = true
|
||||||
biometricUnlockEnablePreference?.isChecked = false
|
biometricUnlockEnablePreference.isChecked = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
deviceCredentialUnlockEnablePreference.isChecked = false
|
deviceCredentialUnlockEnablePreference.isChecked = false
|
||||||
@@ -412,7 +412,6 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
}
|
}
|
||||||
warningAlertDialog = AlertDialog.Builder(activity)
|
warningAlertDialog = AlertDialog.Builder(activity)
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
|
||||||
.setPositiveButton(resources.getString(android.R.string.ok)
|
.setPositiveButton(resources.getString(android.R.string.ok)
|
||||||
) { _, _ ->
|
) { _, _ ->
|
||||||
validate?.invoke()
|
validate?.invoke()
|
||||||
@@ -524,27 +523,23 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisplayPreferenceDialog(preference: Preference) {
|
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||||
|
|
||||||
var otherDialogFragment = false
|
|
||||||
|
|
||||||
var dialogFragment: DialogFragment? = null
|
var dialogFragment: DialogFragment? = null
|
||||||
// Main Preferences
|
|
||||||
when (preference.key) {
|
when (preference.key) {
|
||||||
getString(R.string.app_timeout_key),
|
getString(R.string.app_timeout_key),
|
||||||
getString(R.string.clipboard_timeout_key),
|
getString(R.string.clipboard_timeout_key),
|
||||||
getString(R.string.temp_device_unlock_timeout_key) -> {
|
getString(R.string.temp_device_unlock_timeout_key) -> {
|
||||||
dialogFragment = DurationDialogFragmentCompat.newInstance(preference.key)
|
dialogFragment = DurationDialogFragmentCompat.newInstance(preference.key)
|
||||||
}
|
}
|
||||||
else -> otherDialogFragment = true
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dialogFragment != null) {
|
if (dialogFragment != null) {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
dialogFragment.setTargetFragment(this, 0)
|
dialogFragment.setTargetFragment(this, 0)
|
||||||
dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
|
dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
|
||||||
}
|
} else {
|
||||||
// Could not be handled here. Try with the super method.
|
// Could not be handled here. Try with the super method.
|
||||||
else if (otherDialogFragment) {
|
|
||||||
super.onDisplayPreferenceDialog(preference)
|
super.onDisplayPreferenceDialog(preference)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.activities.dialogs.SetMainCredentialDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.SetMainCredentialDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
||||||
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
|
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationActionType
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||||
@@ -74,11 +77,16 @@ import com.kunzisoft.keepass.tasks.ActionRunnable
|
|||||||
import com.kunzisoft.keepass.utils.getParcelableCompat
|
import com.kunzisoft.keepass.utils.getParcelableCompat
|
||||||
import com.kunzisoft.keepass.utils.getSerializableCompat
|
import com.kunzisoft.keepass.utils.getSerializableCompat
|
||||||
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
||||||
|
import com.kunzisoft.keepass.viewmodels.SettingsViewModel
|
||||||
|
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetrieval {
|
class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetrieval {
|
||||||
|
|
||||||
|
private val mSettingsViewModel: SettingsViewModel by activityViewModels()
|
||||||
private val mDatabaseViewModel: DatabaseViewModel by activityViewModels()
|
private val mDatabaseViewModel: DatabaseViewModel by activityViewModels()
|
||||||
|
private val mUserVerificationViewModel: UserVerificationViewModel by activityViewModels()
|
||||||
|
|
||||||
private val mDatabase: ContextualDatabase?
|
private val mDatabase: ContextualDatabase?
|
||||||
get() = mDatabaseViewModel.database
|
get() = mDatabaseViewModel.database
|
||||||
private var mDatabaseReadOnly: Boolean = false
|
private var mDatabaseReadOnly: Boolean = false
|
||||||
@@ -171,6 +179,51 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
mUserVerificationViewModel.userVerificationState.collect { state ->
|
||||||
|
when (state) {
|
||||||
|
is UserVerificationViewModel.UVState.Loading -> {}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationCanceled -> {
|
||||||
|
mSettingsViewModel.showError(state.error)
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
is UserVerificationViewModel.UVState.OnUserVerificationSucceeded -> {
|
||||||
|
val data = state.dataToVerify
|
||||||
|
when (data.actionType) {
|
||||||
|
UserVerificationActionType.EDIT_DATABASE_SETTING -> {
|
||||||
|
val database = data.database
|
||||||
|
val preferenceKey = data.preferenceKey
|
||||||
|
if (database != null && preferenceKey != null) {
|
||||||
|
// Main Preferences
|
||||||
|
when (preferenceKey) {
|
||||||
|
// Master Key
|
||||||
|
getString(R.string.settings_database_change_credentials_key) -> {
|
||||||
|
SetMainCredentialDialogFragment
|
||||||
|
.getInstance(database.allowNoMasterKey)
|
||||||
|
.show(parentFragmentManager, "passwordDialog")
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
// TODO Settings in compose
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
mSettingsViewModel.dialogFragment?.let { dialogFragment ->
|
||||||
|
dialogFragment.setTargetFragment(
|
||||||
|
this@NestedDatabaseSettingsFragment, 0
|
||||||
|
)
|
||||||
|
dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
|
||||||
|
}
|
||||||
|
mSettingsViewModel.dialogFragment = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -325,7 +378,6 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
}
|
}
|
||||||
// Change the recycle bin group
|
// Change the recycle bin group
|
||||||
recycleBinGroupPref?.setOnPreferenceClickListener {
|
recycleBinGroupPref?.setOnPreferenceClickListener {
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
// Recycle Bin group
|
// Recycle Bin group
|
||||||
@@ -431,11 +483,18 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onCreateDatabaseMasterKeyPreference(database: ContextualDatabase) {
|
private fun onCreateDatabaseMasterKeyPreference(database: ContextualDatabase) {
|
||||||
findPreference<Preference>(getString(R.string.settings_database_change_credentials_key))?.apply {
|
val changeCredentialKey = getString(R.string.settings_database_change_credentials_key)
|
||||||
|
findPreference<Preference>(changeCredentialKey)?.apply {
|
||||||
isEnabled = if (!mDatabaseReadOnly) {
|
isEnabled = if (!mDatabaseReadOnly) {
|
||||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
SetMainCredentialDialogFragment.getInstance(database.allowNoMasterKey)
|
checkUserVerification(
|
||||||
.show(parentFragmentManager, "passwordDialog")
|
mUserVerificationViewModel,
|
||||||
|
UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.EDIT_DATABASE_SETTING,
|
||||||
|
database = database,
|
||||||
|
preferenceKey = changeCredentialKey
|
||||||
|
)
|
||||||
|
)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -462,7 +521,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
// To reassign color listener after orientation change
|
// To reassign color listener after orientation change
|
||||||
val chromaDialog = parentFragmentManager.findFragmentByTag(TAG_PREF_FRAGMENT) as DatabaseColorPreferenceDialogFragmentCompat?
|
val chromaDialog = parentFragmentManager.findFragmentByTag(TAG_PREF_FRAGMENT) as DatabaseColorPreferenceDialogFragmentCompat?
|
||||||
chromaDialog?.onColorSelectedListener = colorSelectedListener
|
chromaDialog?.onColorSelectedListener = colorSelectedListener
|
||||||
} catch (e: Exception) {}
|
} catch (_: Exception) {}
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
@@ -730,9 +789,15 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dialogFragment != null && !mDatabaseReadOnly) {
|
if (dialogFragment != null && !mDatabaseReadOnly) {
|
||||||
@Suppress("DEPRECATION")
|
mSettingsViewModel.dialogFragment = dialogFragment
|
||||||
dialogFragment.setTargetFragment(this, 0)
|
checkUserVerification(
|
||||||
dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
|
mUserVerificationViewModel,
|
||||||
|
UserVerificationData(
|
||||||
|
actionType = UserVerificationActionType.EDIT_DATABASE_SETTING,
|
||||||
|
database = mDatabase,
|
||||||
|
preferenceKey = preference.key
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// Could not be handled here. Try with the super method.
|
// Could not be handled here. Try with the super method.
|
||||||
else if (otherDialogFragment) {
|
else if (otherDialogFragment) {
|
||||||
@@ -742,7 +807,6 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
context?.let { context ->
|
context?.let { context ->
|
||||||
mDatabaseAutoSaveEnabled = PreferencesUtil.isAutoSaveDatabaseEnabled(context)
|
mDatabaseAutoSaveEnabled = PreferencesUtil.isAutoSaveDatabaseEnabled(context)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,17 @@ import com.kunzisoft.keepass.activities.dialogs.UnderDevelopmentFeatureDialogFra
|
|||||||
abstract class NestedSettingsFragment : PreferenceFragmentCompat() {
|
abstract class NestedSettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
enum class Screen {
|
enum class Screen {
|
||||||
APPLICATION, FORM_FILLING, DEVICE_UNLOCK, APPEARANCE, DATABASE, DATABASE_SECURITY, DATABASE_MASTER_KEY
|
APPLICATION,
|
||||||
|
FORM_FILLING,
|
||||||
|
DEVICE_UNLOCK,
|
||||||
|
APPEARANCE,
|
||||||
|
DATABASE,
|
||||||
|
DATABASE_SECURITY,
|
||||||
|
DATABASE_MASTER_KEY
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getScreen(): Screen {
|
fun getScreen(): Screen {
|
||||||
return Screen.values()[requireArguments().getInt(TAG_KEY)]
|
return Screen.entries[requireArguments().getInt(TAG_KEY)]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
@@ -50,8 +56,7 @@ abstract class NestedSettingsFragment : PreferenceFragmentCompat() {
|
|||||||
preferenceInDev.setOnPreferenceClickListener { preference ->
|
preferenceInDev.setOnPreferenceClickListener { preference ->
|
||||||
try { // don't check if we can
|
try { // don't check if we can
|
||||||
(preference as TwoStatePreference).isChecked = false
|
(preference as TwoStatePreference).isChecked = false
|
||||||
} catch (ignored: Exception) {
|
} catch (_: Exception) {}
|
||||||
}
|
|
||||||
UnderDevelopmentFeatureDialogFragment().show(parentFragmentManager, "underDevFeatureDialog")
|
UnderDevelopmentFeatureDialogFragment().show(parentFragmentManager, "underDevFeatureDialog")
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,12 +132,6 @@ object PreferencesUtil {
|
|||||||
context.resources.getBoolean(R.bool.hide_templates_default))
|
context.resources.getBoolean(R.bool.hide_templates_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 {
|
fun colorizePassword(context: Context): Boolean {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
return prefs.getBoolean(context.getString(R.string.colorize_password_key),
|
return prefs.getBoolean(context.getString(R.string.colorize_password_key),
|
||||||
@@ -696,6 +690,18 @@ object PreferencesUtil {
|
|||||||
context.resources.getBoolean(R.bool.passkeys_close_database_default))
|
context.resources.getBoolean(R.bool.passkeys_close_database_default))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isUserVerificationDeviceCredential(context: Context): Boolean {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
return prefs.getBoolean(context.getString(R.string.user_verification_device_credential_key),
|
||||||
|
context.resources.getBoolean(R.bool.user_verification_device_credential_default))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isUserVerificationPreferred(context: Context): Boolean {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
return prefs.getBoolean(context.getString(R.string.user_verification_preferred_key),
|
||||||
|
context.resources.getBoolean(R.bool.user_verification_preferred_default))
|
||||||
|
}
|
||||||
|
|
||||||
fun isPasskeyBackupEligibilityEnable(context: Context): Boolean {
|
fun isPasskeyBackupEligibilityEnable(context: Context): Boolean {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
return prefs.getBoolean(context.getString(R.string.passkeys_backup_eligibility_key),
|
return prefs.getBoolean(context.getString(R.string.passkeys_backup_eligibility_key),
|
||||||
@@ -882,7 +888,6 @@ object PreferencesUtil {
|
|||||||
context.getString(R.string.show_entry_colors_key) -> editor.putBoolean(name, value.toBoolean())
|
context.getString(R.string.show_entry_colors_key) -> editor.putBoolean(name, value.toBoolean())
|
||||||
context.getString(R.string.hide_expired_entries_key) -> editor.putBoolean(name, value.toBoolean())
|
context.getString(R.string.hide_expired_entries_key) -> editor.putBoolean(name, value.toBoolean())
|
||||||
context.getString(R.string.hide_templates_key) -> editor.putBoolean(name, value.toBoolean())
|
context.getString(R.string.hide_templates_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.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_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.list_groups_show_number_entries_key) -> editor.putBoolean(name, value.toBoolean())
|
||||||
|
|||||||
@@ -28,9 +28,13 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.dialogs.SetMainCredentialDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.SetMainCredentialDialogFragment
|
||||||
@@ -41,6 +45,9 @@ import com.kunzisoft.keepass.database.MainCredential
|
|||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
||||||
|
import com.kunzisoft.keepass.view.showError
|
||||||
|
import com.kunzisoft.keepass.viewmodels.SettingsViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
@@ -49,6 +56,8 @@ open class SettingsActivity
|
|||||||
MainPreferenceFragment.Callback,
|
MainPreferenceFragment.Callback,
|
||||||
SetMainCredentialDialogFragment.AssignMainCredentialDialogListener {
|
SetMainCredentialDialogFragment.AssignMainCredentialDialogListener {
|
||||||
|
|
||||||
|
private val mSettingsViewModel: SettingsViewModel by viewModels()
|
||||||
|
|
||||||
private var backupManager: BackupManager? = null
|
private var backupManager: BackupManager? = null
|
||||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||||
|
|
||||||
@@ -118,7 +127,7 @@ open class SettingsActivity
|
|||||||
if (savedInstanceState?.getString(TITLE_KEY).isNullOrEmpty())
|
if (savedInstanceState?.getString(TITLE_KEY).isNullOrEmpty())
|
||||||
toolbar?.setTitle(R.string.settings)
|
toolbar?.setTitle(R.string.settings)
|
||||||
else
|
else
|
||||||
toolbar?.title = savedInstanceState?.getString(TITLE_KEY)
|
toolbar?.title = savedInstanceState.getString(TITLE_KEY)
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
@@ -145,6 +154,20 @@ open class SettingsActivity
|
|||||||
// Eat state
|
// Eat state
|
||||||
intent.removeExtra(FRAGMENT_ARG)
|
intent.removeExtra(FRAGMENT_ARG)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
mSettingsViewModel.settingsState.collect { settingsState ->
|
||||||
|
when (settingsState) {
|
||||||
|
is SettingsViewModel.SettingsState.Wait -> {}
|
||||||
|
is SettingsViewModel.SettingsState.ShowError -> {
|
||||||
|
coordinatorLayout?.showError(settingsState.error)
|
||||||
|
mSettingsViewModel.errorShown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -27,32 +27,27 @@ import android.view.View
|
|||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
open class ProgressTaskDialogFragment : DialogFragment() {
|
open class ProgressTaskDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
@StringRes
|
|
||||||
private var title = UNDEFINED
|
|
||||||
@StringRes
|
|
||||||
private var message = UNDEFINED
|
|
||||||
@StringRes
|
|
||||||
private var warning = UNDEFINED
|
|
||||||
private var cancellable: (() -> Unit)? = null
|
|
||||||
|
|
||||||
private var titleView: TextView? = null
|
private var titleView: TextView? = null
|
||||||
private var messageView: TextView? = null
|
private var messageView: TextView? = null
|
||||||
private var warningView: TextView? = null
|
private var warningView: TextView? = null
|
||||||
private var cancelButton: Button? = null
|
private var cancelButton: Button? = null
|
||||||
private var progressView: ProgressBar? = null
|
private var progressView: ProgressBar? = null
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
private val progressTaskViewModel: ProgressTaskViewModel by activityViewModels()
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
try {
|
try {
|
||||||
activity?.let {
|
activity?.let {
|
||||||
val builder = AlertDialog.Builder(it)
|
val builder = AlertDialog.Builder(it)
|
||||||
@@ -71,68 +66,63 @@ open class ProgressTaskDialogFragment : DialogFragment() {
|
|||||||
cancelButton = root.findViewById(R.id.progress_dialog_cancel)
|
cancelButton = root.findViewById(R.id.progress_dialog_cancel)
|
||||||
progressView = root.findViewById(R.id.progress_dialog_bar)
|
progressView = root.findViewById(R.id.progress_dialog_bar)
|
||||||
|
|
||||||
updateTitle(title)
|
|
||||||
updateMessage(message)
|
|
||||||
updateWarning(warning)
|
|
||||||
setCancellable(cancellable)
|
|
||||||
|
|
||||||
isCancelable = false
|
isCancelable = false
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
progressTaskViewModel.progressTaskState.collect { state ->
|
||||||
|
when (state) {
|
||||||
|
is ProgressTaskViewModel.ProgressTaskState.Show -> {
|
||||||
|
val value = state.value
|
||||||
|
updateView(
|
||||||
|
titleView,
|
||||||
|
value.titleId?.let { title ->
|
||||||
|
getString(title)
|
||||||
|
})
|
||||||
|
updateView(
|
||||||
|
messageView,
|
||||||
|
value.messageId?.let { message ->
|
||||||
|
getString(message)
|
||||||
|
})
|
||||||
|
updateView(
|
||||||
|
warningView,
|
||||||
|
value.warningId?.let { warning ->
|
||||||
|
getString(warning)
|
||||||
|
})
|
||||||
|
cancelButton?.apply {
|
||||||
|
isVisible = value.cancelable != null
|
||||||
|
setOnClickListener {
|
||||||
|
value.cancelable?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Nothing here, this fragment is stopped externally
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return builder.create()
|
return builder.create()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Unable to create progress dialog")
|
Log.e(TAG, "Unable to create progress dialog", e)
|
||||||
}
|
}
|
||||||
return super.onCreateDialog(savedInstanceState)
|
return super.onCreateDialog(savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTitle(@StringRes titleId: Int) {
|
private fun updateView(textView: TextView?, value: String?) {
|
||||||
this.title = titleId
|
if (value == null) {
|
||||||
}
|
textView?.visibility = View.GONE
|
||||||
|
} else {
|
||||||
private fun updateView(textView: TextView?, @StringRes resId: Int) {
|
textView?.text = value
|
||||||
activity?.lifecycleScope?.launch {
|
textView?.visibility = View.VISIBLE
|
||||||
if (resId == UNDEFINED) {
|
|
||||||
textView?.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
textView?.setText(resId)
|
|
||||||
textView?.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCancelable() {
|
|
||||||
activity?.lifecycleScope?.launch {
|
|
||||||
cancelButton?.isVisible = cancellable != null
|
|
||||||
cancelButton?.setOnClickListener {
|
|
||||||
cancellable?.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateTitle(@StringRes resId: Int?) {
|
|
||||||
this.title = resId ?: UNDEFINED
|
|
||||||
updateView(titleView, title)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateMessage(@StringRes resId: Int?) {
|
|
||||||
this.message = resId ?: UNDEFINED
|
|
||||||
updateView(messageView, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateWarning(@StringRes resId: Int?) {
|
|
||||||
this.warning = resId ?: UNDEFINED
|
|
||||||
updateView(warningView, warning)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setCancellable(cancellable: (() -> Unit)?) {
|
|
||||||
this.cancellable = cancellable
|
|
||||||
updateCancelable()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = ProgressTaskDialogFragment::class.java.simpleName
|
private val TAG = ProgressTaskDialogFragment::class.java.simpleName
|
||||||
const val PROGRESS_TASK_DIALOG_TAG = "progressDialogFragment"
|
const val PROGRESS_TASK_DIALOG_TAG = "progressDialogFragment"
|
||||||
const val UNDEFINED = -1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.kunzisoft.keepass.tasks
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.kunzisoft.keepass.database.ProgressMessage
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
|
class ProgressTaskViewModel: ViewModel() {
|
||||||
|
|
||||||
|
private val mProgressTaskState = MutableStateFlow<ProgressTaskState>(ProgressTaskState.Hide)
|
||||||
|
val progressTaskState: StateFlow<ProgressTaskState> = mProgressTaskState
|
||||||
|
|
||||||
|
fun show(value: ProgressMessage) {
|
||||||
|
mProgressTaskState.update { currentState ->
|
||||||
|
ProgressTaskState.Show(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hide() {
|
||||||
|
mProgressTaskState.value = ProgressTaskState.Hide
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ProgressTaskState {
|
||||||
|
data class Show(val value: ProgressMessage): ProgressTaskState()
|
||||||
|
object Hide: ProgressTaskState()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -98,25 +98,43 @@ object AppUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val processedPackageNames = mutableSetOf<String>()
|
val processedPackageNames = mutableSetOf<String>()
|
||||||
|
|
||||||
for (resolveInfo in resolveInfoList) {
|
for (resolveInfo in resolveInfoList) {
|
||||||
val packageName = resolveInfo.activityInfo.packageName
|
val packageName = resolveInfo.activityInfo.packageName
|
||||||
if (packageName != null && !processedPackageNames.contains(packageName)) {
|
if (packageName != null && !processedPackageNames.contains(packageName)) {
|
||||||
try {
|
buildAndroidPrivilegedApp(packageManager, packageName)?.let { privilegedApp ->
|
||||||
val packageInfo = packageManager.getPackageInfo(
|
browserList.add(privilegedApp)
|
||||||
packageName,
|
processedPackageNames.add(packageName)
|
||||||
PackageManager.GET_SIGNING_CERTIFICATES
|
|
||||||
)
|
|
||||||
val signatureFingerprints = packageInfo.signingInfo?.getAllFingerprints()
|
|
||||||
signatureFingerprints?.let {
|
|
||||||
browserList.add(AndroidPrivilegedApp(packageName, signatureFingerprints))
|
|
||||||
processedPackageNames.add(packageName)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(AppUtil::class.simpleName, "Error processing package: $packageName", e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the Play Service
|
||||||
|
val gServices = "com.google.android.gms"
|
||||||
|
buildAndroidPrivilegedApp(packageManager, gServices)?.let { privilegedApp ->
|
||||||
|
browserList.add(privilegedApp)
|
||||||
|
processedPackageNames.add(gServices)
|
||||||
|
}
|
||||||
|
|
||||||
return browserList.distinctBy { it.packageName } // Ensure uniqueness just in case
|
return browserList.distinctBy { it.packageName } // Ensure uniqueness just in case
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.P)
|
||||||
|
private fun buildAndroidPrivilegedApp(
|
||||||
|
packageManager: PackageManager,
|
||||||
|
packageName: String
|
||||||
|
): AndroidPrivilegedApp? {
|
||||||
|
return try {
|
||||||
|
val packageInfo = packageManager.getPackageInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.GET_SIGNING_CERTIFICATES
|
||||||
|
)
|
||||||
|
val signatureFingerprints = packageInfo.signingInfo?.getAllFingerprints()
|
||||||
|
signatureFingerprints?.let {
|
||||||
|
AndroidPrivilegedApp(packageName, signatureFingerprints)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(AppUtil::class.simpleName, "Error processing package: $packageName", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.view
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.InputType
|
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
@@ -51,7 +50,6 @@ class PasswordEditView @JvmOverloads constructor(context: Context,
|
|||||||
|
|
||||||
private var mViewHint: String = ""
|
private var mViewHint: String = ""
|
||||||
private var mMaxLines: Int = 3
|
private var mMaxLines: Int = 3
|
||||||
private var mShowPassword: Boolean = false
|
|
||||||
|
|
||||||
private var mPasswordTextWatchers: MutableList<TextWatcher> = mutableListOf()
|
private var mPasswordTextWatchers: MutableList<TextWatcher> = mutableListOf()
|
||||||
private var mPasswordTextWatcher: TextWatcher? = null
|
private var mPasswordTextWatcher: TextWatcher? = null
|
||||||
@@ -65,8 +63,6 @@ class PasswordEditView @JvmOverloads constructor(context: Context,
|
|||||||
mViewHint = getString(R.styleable.PasswordView_passwordHint)
|
mViewHint = getString(R.styleable.PasswordView_passwordHint)
|
||||||
?: context.getString(R.string.password)
|
?: context.getString(R.string.password)
|
||||||
mMaxLines = getInteger(R.styleable.PasswordView_passwordMaxLines, mMaxLines)
|
mMaxLines = getInteger(R.styleable.PasswordView_passwordMaxLines, mMaxLines)
|
||||||
mShowPassword = getBoolean(R.styleable.PasswordView_passwordVisible,
|
|
||||||
!PreferencesUtil.hideProtectedValue(context))
|
|
||||||
} finally {
|
} finally {
|
||||||
recycle()
|
recycle()
|
||||||
}
|
}
|
||||||
@@ -76,16 +72,12 @@ class PasswordEditView @JvmOverloads constructor(context: Context,
|
|||||||
inflater?.inflate(R.layout.view_password_edit, this)
|
inflater?.inflate(R.layout.view_password_edit, this)
|
||||||
|
|
||||||
passwordInputLayout = findViewById(R.id.password_edit_input_layout)
|
passwordInputLayout = findViewById(R.id.password_edit_input_layout)
|
||||||
passwordInputLayout?.hint = mViewHint
|
passwordInputLayout.hint = mViewHint
|
||||||
passwordText = findViewById(R.id.password_edit_text)
|
passwordText = findViewById(R.id.password_edit_text)
|
||||||
if (mShowPassword) {
|
passwordText.maxLines = mMaxLines
|
||||||
passwordText?.inputType = passwordText.inputType or
|
passwordText.applyFontVisibility()
|
||||||
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
|
||||||
}
|
|
||||||
passwordText?.maxLines = mMaxLines
|
|
||||||
passwordText?.applyFontVisibility()
|
|
||||||
passwordStrengthProgress = findViewById(R.id.password_edit_strength_progress)
|
passwordStrengthProgress = findViewById(R.id.password_edit_strength_progress)
|
||||||
passwordStrengthProgress?.apply {
|
passwordStrengthProgress.apply {
|
||||||
setIndicatorColor(PasswordEntropy.Strength.RISKY.color)
|
setIndicatorColor(PasswordEntropy.Strength.RISKY.color)
|
||||||
progress = 0
|
progress = 0
|
||||||
max = 100
|
max = 100
|
||||||
@@ -93,7 +85,7 @@ class PasswordEditView @JvmOverloads constructor(context: Context,
|
|||||||
passwordEntropy = findViewById(R.id.password_edit_entropy)
|
passwordEntropy = findViewById(R.id.password_edit_entropy)
|
||||||
|
|
||||||
mPasswordEntropyCalculator = PasswordEntropy {
|
mPasswordEntropyCalculator = PasswordEntropy {
|
||||||
passwordText?.text?.toString()?.let { firstPassword ->
|
passwordText.text?.toString()?.let { firstPassword ->
|
||||||
getEntropyStrength(firstPassword)
|
getEntropyStrength(firstPassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +111,7 @@ class PasswordEditView @JvmOverloads constructor(context: Context,
|
|||||||
PasswordGenerator.colorizedPassword(editable)
|
PasswordGenerator.colorizedPassword(editable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
passwordText?.addTextChangedListener(mPasswordTextWatcher)
|
passwordText.addTextChangedListener(mPasswordTextWatcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getEntropyStrength(passwordText: String) {
|
private fun getEntropyStrength(passwordText: String) {
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.kunzisoft.keepass.view
|
||||||
|
|
||||||
|
import android.view.View.OnClickListener
|
||||||
|
|
||||||
|
interface ProtectedFieldView {
|
||||||
|
fun setProtection(
|
||||||
|
protection: Boolean,
|
||||||
|
isCurrentlyProtected: Boolean,
|
||||||
|
onUnprotectClickListener: OnClickListener?
|
||||||
|
)
|
||||||
|
fun isCurrentlyProtected(): Boolean
|
||||||
|
fun protect()
|
||||||
|
fun unprotect()
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 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.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.os.Parcelable.Creator
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import com.kunzisoft.keepass.utils.readBooleanCompat
|
||||||
|
import com.kunzisoft.keepass.utils.writeBooleanCompat
|
||||||
|
|
||||||
|
|
||||||
|
abstract class ProtectedTextFieldView @JvmOverloads constructor(context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyle: Int = 0)
|
||||||
|
: RelativeLayout(context, attrs, defStyle),
|
||||||
|
GenericTextFieldView, ProtectedFieldView {
|
||||||
|
|
||||||
|
var isProtected: Boolean = false
|
||||||
|
private set
|
||||||
|
private var mIsCurrentlyProtected: Boolean = true
|
||||||
|
|
||||||
|
// Only to fix rebuild view from template
|
||||||
|
var onSaveInstanceState: (() -> Unit)? = null
|
||||||
|
|
||||||
|
override fun isCurrentlyProtected(): Boolean {
|
||||||
|
return mIsCurrentlyProtected
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun protect() {
|
||||||
|
mIsCurrentlyProtected = true
|
||||||
|
changeProtectedValueParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unprotect() {
|
||||||
|
mIsCurrentlyProtected = false
|
||||||
|
changeProtectedValueParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setProtection(
|
||||||
|
protection: Boolean,
|
||||||
|
isCurrentlyProtected: Boolean,
|
||||||
|
onUnprotectClickListener: OnClickListener?
|
||||||
|
) {
|
||||||
|
this.isProtected = protection
|
||||||
|
this.mIsCurrentlyProtected = isCurrentlyProtected
|
||||||
|
if (isProtected) {
|
||||||
|
changeProtectedValueParameters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun changeProtectedValueParameters()
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(): Parcelable? {
|
||||||
|
onSaveInstanceState?.invoke()
|
||||||
|
return ProtectionState(super.onSaveInstanceState()).apply {
|
||||||
|
this.isCurrentlyProtected = isCurrentlyProtected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRestoreInstanceState(state: Parcelable?) {
|
||||||
|
when (state) {
|
||||||
|
is ProtectionState -> {
|
||||||
|
super.onRestoreInstanceState(state.superState)
|
||||||
|
mIsCurrentlyProtected = state.isCurrentlyProtected
|
||||||
|
}
|
||||||
|
else -> super.onRestoreInstanceState(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ProtectionState : BaseSavedState {
|
||||||
|
|
||||||
|
var isCurrentlyProtected: Boolean = true
|
||||||
|
|
||||||
|
constructor(superState: Parcelable?) : super(superState)
|
||||||
|
|
||||||
|
private constructor(parcel: Parcel) : super(parcel) {
|
||||||
|
isCurrentlyProtected = parcel.readBooleanCompat()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||||
|
super.writeToParcel(out, flags)
|
||||||
|
out.writeBooleanCompat(isCurrentlyProtected)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Creator<ProtectionState> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): ProtectionState {
|
||||||
|
return ProtectionState(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<ProtectionState?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ import com.kunzisoft.keepass.otp.OtpElement
|
|||||||
import com.kunzisoft.keepass.otp.OtpEntryFields
|
import com.kunzisoft.keepass.otp.OtpEntryFields
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.utils.KeyboardUtil.hideKeyboard
|
import com.kunzisoft.keepass.utils.KeyboardUtil.hideKeyboard
|
||||||
|
import com.kunzisoft.keepass.utils.readListCompat
|
||||||
import com.kunzisoft.keepass.utils.readParcelableCompat
|
import com.kunzisoft.keepass.utils.readParcelableCompat
|
||||||
|
|
||||||
|
|
||||||
@@ -45,11 +46,12 @@ abstract class TemplateAbstractView<
|
|||||||
private var mTemplate: Template? = null
|
private var mTemplate: Template? = null
|
||||||
protected var mEntryInfo: EntryInfo? = null
|
protected var mEntryInfo: EntryInfo? = null
|
||||||
|
|
||||||
|
// To keep unprotected views during orientation change
|
||||||
|
protected var mUnprotectedFields = mutableListOf<Field>()
|
||||||
|
|
||||||
private var mViewFields = mutableListOf<ViewField>()
|
private var mViewFields = mutableListOf<ViewField>()
|
||||||
|
|
||||||
protected var mFontInVisibility: Boolean = PreferencesUtil.fieldFontIsInVisibility(context)
|
protected var mFontInVisibility: Boolean = PreferencesUtil.fieldFontIsInVisibility(context)
|
||||||
protected var mHideProtectedValue: Boolean = PreferencesUtil.hideProtectedValue(context)
|
|
||||||
|
|
||||||
protected var headerContainerView: ViewGroup
|
protected var headerContainerView: ViewGroup
|
||||||
protected var entryIconView: ImageView
|
protected var entryIconView: ImageView
|
||||||
protected var backgroundColorView: View
|
protected var backgroundColorView: View
|
||||||
@@ -121,9 +123,6 @@ abstract class TemplateAbstractView<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun buildTemplate() {
|
private fun buildTemplate() {
|
||||||
// Retrieve preferences
|
|
||||||
mHideProtectedValue = PreferencesUtil.hideProtectedValue(context)
|
|
||||||
|
|
||||||
// Build each template section
|
// Build each template section
|
||||||
titleContainerView.removeAllViews()
|
titleContainerView.removeAllViews()
|
||||||
templateContainerView.removeAllViews()
|
templateContainerView.removeAllViews()
|
||||||
@@ -284,10 +283,6 @@ abstract class TemplateAbstractView<
|
|||||||
this.mFontInVisibility = fontInVisibility
|
this.mFontInVisibility = fontInVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setHideProtectedValue(hideProtectedValue: Boolean) {
|
|
||||||
this.mHideProtectedValue = hideProtectedValue
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setEntryInfo(entryInfo: EntryInfo?) {
|
fun setEntryInfo(entryInfo: EntryInfo?) {
|
||||||
mEntryInfo = entryInfo
|
mEntryInfo = entryInfo
|
||||||
buildTemplateAndPopulateInfo()
|
buildTemplateAndPopulateInfo()
|
||||||
@@ -578,7 +573,7 @@ abstract class TemplateAbstractView<
|
|||||||
}
|
}
|
||||||
|
|
||||||
return if (!isStandardFieldName(customField.name)) {
|
return if (!isStandardFieldName(customField.name)) {
|
||||||
customFieldsContainerView.visibility = View.VISIBLE
|
customFieldsContainerView.visibility = VISIBLE
|
||||||
if (getIndexViewFieldByName(customField.name) >= 0) {
|
if (getIndexViewFieldByName(customField.name) >= 0) {
|
||||||
// Update a custom field with a new value,
|
// Update a custom field with a new value,
|
||||||
// new field name must be the same as old field name
|
// new field name must be the same as old field name
|
||||||
@@ -683,6 +678,16 @@ abstract class TemplateAbstractView<
|
|||||||
putCustomField(Field(otpField.name, otpField.protectedValue))
|
putCustomField(Field(otpField.name, otpField.protectedValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveUnprotectedFieldState(field: Field, isCurrentlyProtected: Boolean) {
|
||||||
|
try {
|
||||||
|
if (!isCurrentlyProtected) {
|
||||||
|
mUnprotectedFields.add(field)
|
||||||
|
} else {
|
||||||
|
mUnprotectedFields.remove(field)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onRestoreInstanceState(state: Parcelable?) {
|
override fun onRestoreInstanceState(state: Parcelable?) {
|
||||||
//begin boilerplate code so parent classes can restore state
|
//begin boilerplate code so parent classes can restore state
|
||||||
if (state !is SavedState) {
|
if (state !is SavedState) {
|
||||||
@@ -691,6 +696,7 @@ abstract class TemplateAbstractView<
|
|||||||
} else {
|
} else {
|
||||||
mTemplate = state.template
|
mTemplate = state.template
|
||||||
mEntryInfo = state.entryInfo
|
mEntryInfo = state.entryInfo
|
||||||
|
mUnprotectedFields = state.unprotectedFields
|
||||||
onRestoreEntryInstanceState(state)
|
onRestoreEntryInstanceState(state)
|
||||||
buildTemplateAndPopulateInfo()
|
buildTemplateAndPopulateInfo()
|
||||||
super.onRestoreInstanceState(state.superState)
|
super.onRestoreInstanceState(state.superState)
|
||||||
@@ -706,6 +712,7 @@ abstract class TemplateAbstractView<
|
|||||||
retrieveDefaultValues = false)
|
retrieveDefaultValues = false)
|
||||||
saveState.template = this.mTemplate
|
saveState.template = this.mTemplate
|
||||||
saveState.entryInfo = this.mEntryInfo
|
saveState.entryInfo = this.mEntryInfo
|
||||||
|
saveState.unprotectedFields = this.mUnprotectedFields
|
||||||
onSaveEntryInstanceState(saveState)
|
onSaveEntryInstanceState(saveState)
|
||||||
return saveState
|
return saveState
|
||||||
}
|
}
|
||||||
@@ -715,6 +722,7 @@ abstract class TemplateAbstractView<
|
|||||||
protected class SavedState : BaseSavedState {
|
protected class SavedState : BaseSavedState {
|
||||||
var template: Template? = null
|
var template: Template? = null
|
||||||
var entryInfo: EntryInfo? = null
|
var entryInfo: EntryInfo? = null
|
||||||
|
var unprotectedFields = mutableListOf<Field>()
|
||||||
// TODO Move
|
// TODO Move
|
||||||
var tempDateTimeViewId: Int? = null
|
var tempDateTimeViewId: Int? = null
|
||||||
|
|
||||||
@@ -723,6 +731,7 @@ abstract class TemplateAbstractView<
|
|||||||
private constructor(parcel: Parcel) : super(parcel) {
|
private constructor(parcel: Parcel) : super(parcel) {
|
||||||
template = parcel.readParcelableCompat() ?: template
|
template = parcel.readParcelableCompat() ?: template
|
||||||
entryInfo = parcel.readParcelableCompat() ?: entryInfo
|
entryInfo = parcel.readParcelableCompat() ?: entryInfo
|
||||||
|
parcel.readListCompat<Field>(unprotectedFields)
|
||||||
val dateTimeViewId = parcel.readInt()
|
val dateTimeViewId = parcel.readInt()
|
||||||
if (dateTimeViewId != -1)
|
if (dateTimeViewId != -1)
|
||||||
tempDateTimeViewId = dateTimeViewId
|
tempDateTimeViewId = dateTimeViewId
|
||||||
@@ -732,6 +741,7 @@ abstract class TemplateAbstractView<
|
|||||||
super.writeToParcel(out, flags)
|
super.writeToParcel(out, flags)
|
||||||
out.writeParcelable(template, flags)
|
out.writeParcelable(template, flags)
|
||||||
out.writeParcelable(entryInfo, flags)
|
out.writeParcelable(entryInfo, flags)
|
||||||
|
out.writeList(unprotectedFields)
|
||||||
out.writeInt(tempDateTimeViewId ?: -1)
|
out.writeInt(tempDateTimeViewId ?: -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ import com.kunzisoft.keepass.database.element.template.TemplateAttributeAction
|
|||||||
import com.kunzisoft.keepass.database.element.template.TemplateField
|
import com.kunzisoft.keepass.database.element.template.TemplateField
|
||||||
import com.kunzisoft.keepass.database.helper.getLocalizedName
|
import com.kunzisoft.keepass.database.helper.getLocalizedName
|
||||||
import com.kunzisoft.keepass.database.helper.isStandardPasswordName
|
import com.kunzisoft.keepass.database.helper.isStandardPasswordName
|
||||||
|
import com.kunzisoft.keepass.model.AppOriginEntryField
|
||||||
import com.kunzisoft.keepass.model.DataDate
|
import com.kunzisoft.keepass.model.DataDate
|
||||||
import com.kunzisoft.keepass.model.DataTime
|
import com.kunzisoft.keepass.model.DataTime
|
||||||
import com.kunzisoft.keepass.model.AppOriginEntryField
|
|
||||||
import com.kunzisoft.keepass.model.PasskeyEntryFields
|
import com.kunzisoft.keepass.model.PasskeyEntryFields
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields
|
import com.kunzisoft.keepass.otp.OtpEntryFields
|
||||||
|
|
||||||
@@ -35,6 +35,11 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
@IdRes
|
@IdRes
|
||||||
private var mTempDateTimeViewId: Int? = null
|
private var mTempDateTimeViewId: Int? = null
|
||||||
|
|
||||||
|
private var mOnUnprotectClickListener: ((Field, ProtectedFieldView) -> Unit)? = null
|
||||||
|
fun setOnUnprotectClickListener(listener: ((Field, ProtectedFieldView) -> Unit)?) {
|
||||||
|
this.mOnUnprotectClickListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
private var mOnCustomEditionActionClickListener: ((Field) -> Unit)? = null
|
private var mOnCustomEditionActionClickListener: ((Field) -> Unit)? = null
|
||||||
fun setOnCustomEditionActionClickListener(listener: ((Field) -> Unit)?) {
|
fun setOnCustomEditionActionClickListener(listener: ((Field) -> Unit)?) {
|
||||||
this.mOnCustomEditionActionClickListener = listener
|
this.mOnCustomEditionActionClickListener = listener
|
||||||
@@ -80,9 +85,9 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
if (color != null) {
|
if (color != null) {
|
||||||
backgroundColorView.background.colorFilter = BlendModeColorFilterCompat
|
backgroundColorView.background.colorFilter = BlendModeColorFilterCompat
|
||||||
.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_ATOP)
|
.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_ATOP)
|
||||||
backgroundColorView.visibility = View.VISIBLE
|
backgroundColorView.visibility = VISIBLE
|
||||||
} else {
|
} else {
|
||||||
backgroundColorView.visibility = View.GONE
|
backgroundColorView.visibility = GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,9 +108,9 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
if (color != null) {
|
if (color != null) {
|
||||||
foregroundColorView.background.colorFilter = BlendModeColorFilterCompat
|
foregroundColorView.background.colorFilter = BlendModeColorFilterCompat
|
||||||
.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_ATOP)
|
.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_ATOP)
|
||||||
foregroundColorView.visibility = View.VISIBLE
|
foregroundColorView.visibility = VISIBLE
|
||||||
} else {
|
} else {
|
||||||
foregroundColorView.visibility = View.GONE
|
foregroundColorView.visibility = GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,14 +118,25 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
headerContainerView.isVisible = true
|
headerContainerView.isVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun buildLinearTextView(templateAttribute: TemplateAttribute,
|
override fun buildLinearTextView(
|
||||||
field: Field): TextEditFieldView? {
|
templateAttribute: TemplateAttribute,
|
||||||
|
field: Field
|
||||||
|
): TextEditFieldView? {
|
||||||
return context?.let {
|
return context?.let {
|
||||||
(if (TemplateField.isStandardPasswordName(context, templateAttribute.label))
|
(if (TemplateField.isStandardPasswordName(context, templateAttribute.label))
|
||||||
PasswordTextEditFieldView(it)
|
PasswordTextEditFieldView(it)
|
||||||
else TextEditFieldView(it)).apply {
|
else TextEditFieldView(it)).apply {
|
||||||
// hiddenProtectedValue (mHideProtectedValue) don't work with TextInputLayout
|
// hiddenProtectedValue (mHideProtectedValue) don't work with TextInputLayout
|
||||||
setProtection(field.protectedValue.isProtected)
|
setProtection(
|
||||||
|
protection = field.protectedValue.isProtected,
|
||||||
|
isCurrentlyProtected = mUnprotectedFields.contains(field).not()
|
||||||
|
) {
|
||||||
|
mOnUnprotectClickListener?.invoke(field, this)
|
||||||
|
}
|
||||||
|
// Trick to bypass the onSaveInstanceState in rebuild child
|
||||||
|
onSaveInstanceState = {
|
||||||
|
saveUnprotectedFieldState(field, isCurrentlyProtected())
|
||||||
|
}
|
||||||
default = templateAttribute.default
|
default = templateAttribute.default
|
||||||
setMaxChars(templateAttribute.options.getNumberChars())
|
setMaxChars(templateAttribute.options.getNumberChars())
|
||||||
setMaxLines(templateAttribute.options.getNumberLines())
|
setMaxLines(templateAttribute.options.getNumberLines())
|
||||||
@@ -129,7 +145,7 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
textDirection = TEXT_DIRECTION_LTR
|
textDirection = TEXT_DIRECTION_LTR
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO
|
importantForAutofill = IMPORTANT_FOR_AUTOFILL_NO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,7 +159,7 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
default = templateAttribute.default
|
default = templateAttribute.default
|
||||||
setActionClick(templateAttribute, field, this)
|
setActionClick(templateAttribute, field, this)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO
|
importantForAutofill = IMPORTANT_FOR_AUTOFILL_NO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,7 +173,7 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
label = templateAttribute.alias
|
label = templateAttribute.alias
|
||||||
?: TemplateField.getLocalizedName(context, field.name)
|
?: TemplateField.getLocalizedName(context, field.name)
|
||||||
val fieldValue = field.protectedValue.stringValue
|
val fieldValue = field.protectedValue.stringValue
|
||||||
value = if (fieldValue.isEmpty()) templateAttribute.default else fieldValue
|
value = fieldValue.ifEmpty { templateAttribute.default }
|
||||||
// TODO edition and password generator at same time
|
// TODO edition and password generator at same time
|
||||||
when (templateAttribute.action) {
|
when (templateAttribute.action) {
|
||||||
TemplateAttributeAction.NONE -> {
|
TemplateAttributeAction.NONE -> {
|
||||||
@@ -187,7 +203,7 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
val value = field.protectedValue.toString().trim()
|
val value = field.protectedValue.toString().trim()
|
||||||
type = dateInstantType
|
type = dateInstantType
|
||||||
activation = value.isNotEmpty()
|
activation = value.isNotEmpty()
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
type = dateInstantType
|
type = dateInstantType
|
||||||
activation = false
|
activation = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,17 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
: TemplateAbstractView<TextFieldView, TextFieldView, DateTimeFieldView>
|
: TemplateAbstractView<TextFieldView, TextFieldView, DateTimeFieldView>
|
||||||
(context, attrs, defStyle) {
|
(context, attrs, defStyle) {
|
||||||
|
|
||||||
|
private var mOnUnprotectClickListener: ((ProtectedFieldView) -> Unit)? = null
|
||||||
|
fun setOnUnprotectClickListener(listener: ((ProtectedFieldView) -> Unit)?) {
|
||||||
|
this.mOnUnprotectClickListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
private var mOnAskCopySafeClickListener: (() -> Unit)? = null
|
private var mOnAskCopySafeClickListener: (() -> Unit)? = null
|
||||||
fun setOnAskCopySafeClickListener(listener: (() -> Unit)? = null) {
|
fun setOnAskCopySafeClickListener(listener: (() -> Unit)? = null) {
|
||||||
this.mOnAskCopySafeClickListener = listener
|
this.mOnAskCopySafeClickListener = listener
|
||||||
}
|
}
|
||||||
private var mOnCopyActionClickListener: ((Field) -> Unit)? = null
|
private var mOnCopyActionClickListener: ((Field, ProtectedFieldView) -> Unit)? = null
|
||||||
fun setOnCopyActionClickListener(listener: ((Field) -> Unit)? = null) {
|
fun setOnCopyActionClickListener(listener: ((Field, ProtectedFieldView) -> Unit)? = null) {
|
||||||
this.mOnCopyActionClickListener = listener
|
this.mOnCopyActionClickListener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +63,16 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
PasskeyTextFieldView(it)
|
PasskeyTextFieldView(it)
|
||||||
else TextFieldView(it)).apply {
|
else TextFieldView(it)).apply {
|
||||||
applyFontVisibility(mFontInVisibility)
|
applyFontVisibility(mFontInVisibility)
|
||||||
setProtection(field.protectedValue.isProtected, mHideProtectedValue)
|
setProtection(
|
||||||
|
protection = field.protectedValue.isProtected,
|
||||||
|
isCurrentlyProtected = mUnprotectedFields.contains(field).not()
|
||||||
|
) {
|
||||||
|
mOnUnprotectClickListener?.invoke(this)
|
||||||
|
}
|
||||||
|
// Trick to bypass the onSaveInstanceState in rebuild child
|
||||||
|
onSaveInstanceState = {
|
||||||
|
saveUnprotectedFieldState(field, isCurrentlyProtected())
|
||||||
|
}
|
||||||
label = templateAttribute.alias
|
label = templateAttribute.alias
|
||||||
?: TemplateField.getLocalizedName(context, field.name)
|
?: TemplateField.getLocalizedName(context, field.name)
|
||||||
setMaxChars(templateAttribute.options.getNumberChars())
|
setMaxChars(templateAttribute.options.getNumberChars())
|
||||||
@@ -78,7 +92,15 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||||
setCopyButtonClickListener { label, value ->
|
setCopyButtonClickListener { label, value ->
|
||||||
mOnCopyActionClickListener
|
mOnCopyActionClickListener
|
||||||
?.invoke(Field(label, ProtectedString(true, value)))
|
?.invoke(
|
||||||
|
Field(
|
||||||
|
name = label,
|
||||||
|
value = ProtectedString(
|
||||||
|
enableProtection = true,
|
||||||
|
string = value
|
||||||
|
)
|
||||||
|
), this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setCopyButtonState(TextFieldView.ButtonState.GONE)
|
setCopyButtonState(TextFieldView.ButtonState.GONE)
|
||||||
@@ -89,7 +111,15 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||||
setCopyButtonClickListener { label, value ->
|
setCopyButtonClickListener { label, value ->
|
||||||
mOnCopyActionClickListener
|
mOnCopyActionClickListener
|
||||||
?.invoke(Field(label, ProtectedString(false, value)))
|
?.invoke(
|
||||||
|
Field(
|
||||||
|
name = label,
|
||||||
|
value = ProtectedString(
|
||||||
|
enableProtection = false,
|
||||||
|
string = value
|
||||||
|
)
|
||||||
|
), this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +144,7 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
try {
|
try {
|
||||||
val value = field.protectedValue.toString().trim()
|
val value = field.protectedValue.toString().trim()
|
||||||
activation = value.isNotEmpty()
|
activation = value.isNotEmpty()
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
activation = false
|
activation = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,9 +207,15 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
value = otpElement.tokenString
|
value = otpElement.tokenString
|
||||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||||
setCopyButtonClickListener { _, _ ->
|
setCopyButtonClickListener { _, _ ->
|
||||||
mOnCopyActionClickListener?.invoke(Field(
|
mOnCopyActionClickListener?.invoke(
|
||||||
otpElement.type.name,
|
Field(
|
||||||
ProtectedString(false, otpElement.token)))
|
name = otpElement.type.name,
|
||||||
|
value = ProtectedString(
|
||||||
|
enableProtection = false,
|
||||||
|
string = otpElement.token
|
||||||
|
)
|
||||||
|
), this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
textDirection = TEXT_DIRECTION_LTR
|
textDirection = TEXT_DIRECTION_LTR
|
||||||
mLastOtpTokenView = this
|
mLastOtpTokenView = this
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import android.text.InputFilter
|
|||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
|
import android.text.method.PasswordTransformationMethod
|
||||||
|
import android.text.method.SingleLineTransformationMethod
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.appcompat.view.ContextThemeWrapper
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
import androidx.appcompat.widget.AppCompatImageButton
|
import androidx.appcompat.widget.AppCompatImageButton
|
||||||
@@ -21,12 +22,11 @@ import androidx.core.view.isVisible
|
|||||||
import com.google.android.material.textfield.TextInputEditText
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
|
||||||
|
|
||||||
open class TextEditFieldView @JvmOverloads constructor(context: Context,
|
open class TextEditFieldView @JvmOverloads constructor(context: Context,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyle: Int = 0)
|
defStyle: Int = 0)
|
||||||
: RelativeLayout(context, attrs, defStyle), GenericTextFieldView {
|
: ProtectedTextFieldView(context, attrs, defStyle) {
|
||||||
|
|
||||||
private var labelViewId = ViewCompat.generateViewId()
|
private var labelViewId = ViewCompat.generateViewId()
|
||||||
private var valueViewId = ViewCompat.generateViewId()
|
private var valueViewId = ViewCompat.generateViewId()
|
||||||
@@ -169,14 +169,29 @@ open class TextEditFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setProtection(protection: Boolean) {
|
override fun setProtection(
|
||||||
if (protection) {
|
protection: Boolean,
|
||||||
|
isCurrentlyProtected: Boolean,
|
||||||
|
onUnprotectClickListener: OnClickListener?
|
||||||
|
) {
|
||||||
|
super.setProtection(protection, isCurrentlyProtected, onUnprotectClickListener)
|
||||||
|
if (isProtected) {
|
||||||
labelView.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE
|
labelView.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE
|
||||||
val visibilityTag = if (PreferencesUtil.hideProtectedValue(context))
|
// FIXME Called by itself during orientation change
|
||||||
InputType.TYPE_TEXT_VARIATION_PASSWORD
|
/*
|
||||||
else
|
labelView.setEndIconOnClickListener {
|
||||||
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
onUnprotectClickListener?.onClick(this@TextEditFieldView)
|
||||||
valueView.inputType = valueView.inputType or visibilityTag
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun changeProtectedValueParameters() {
|
||||||
|
if (isCurrentlyProtected()) {
|
||||||
|
valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||||
|
valueView.transformationMethod = PasswordTransformationMethod.getInstance()
|
||||||
|
} else {
|
||||||
|
valueView.inputType = valueView.inputType or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||||
|
valueView.transformationMethod = SingleLineTransformationMethod.getInstance()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import android.util.AttributeSet
|
|||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.ContextThemeWrapper
|
import android.view.ContextThemeWrapper
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.widget.AppCompatImageButton
|
import androidx.appcompat.widget.AppCompatImageButton
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
@@ -42,7 +41,7 @@ import com.kunzisoft.keepass.utils.AppUtil.openExternalApp
|
|||||||
open class TextFieldView @JvmOverloads constructor(context: Context,
|
open class TextFieldView @JvmOverloads constructor(context: Context,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyle: Int = 0)
|
defStyle: Int = 0)
|
||||||
: RelativeLayout(context, attrs, defStyle), GenericTextFieldView {
|
: ProtectedTextFieldView(context, attrs, defStyle) {
|
||||||
|
|
||||||
protected var labelViewId = ViewCompat.generateViewId()
|
protected var labelViewId = ViewCompat.generateViewId()
|
||||||
private var valueViewId = ViewCompat.generateViewId()
|
private var valueViewId = ViewCompat.generateViewId()
|
||||||
@@ -204,25 +203,29 @@ open class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setProtection(protection: Boolean, hiddenProtectedValue: Boolean = false) {
|
override fun setProtection(
|
||||||
showButton.isVisible = protection
|
protection: Boolean,
|
||||||
showButton.isSelected = hiddenProtectedValue
|
isCurrentlyProtected: Boolean,
|
||||||
showButton.setOnClickListener {
|
onUnprotectClickListener: OnClickListener?
|
||||||
showButton.isSelected = !showButton.isSelected
|
) {
|
||||||
changeProtectedValueParameters()
|
super.setProtection(protection, isCurrentlyProtected, onUnprotectClickListener)
|
||||||
|
showButton.isVisible = isProtected
|
||||||
|
if (isProtected) {
|
||||||
|
showButton.setOnClickListener {
|
||||||
|
onUnprotectClickListener?.onClick(this@TextFieldView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
changeProtectedValueParameters()
|
|
||||||
invalidate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun changeProtectedValueParameters() {
|
override fun changeProtectedValueParameters() {
|
||||||
valueView.apply {
|
valueView.apply {
|
||||||
if (showButton.isVisible) {
|
if (showButton.isVisible) {
|
||||||
applyHiddenStyle(showButton.isSelected)
|
applyHiddenStyle(isCurrentlyProtected())
|
||||||
} else {
|
} else {
|
||||||
linkify()
|
linkify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun linkify() {
|
private fun linkify() {
|
||||||
|
|||||||
@@ -238,15 +238,13 @@ fun View.updateLockPaddingStart() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.toastError(e: Throwable) {
|
fun Context.toastError(e: Throwable?) {
|
||||||
Toast.makeText(
|
val message = if (e is LocalizedException)
|
||||||
applicationContext,
|
e.getLocalizedMessage(resources)
|
||||||
if (e is LocalizedException)
|
else e?.localizedMessage
|
||||||
e.getLocalizedMessage(resources)
|
message?.let {
|
||||||
else
|
Toast.makeText(applicationContext, message, Toast.LENGTH_LONG).show()
|
||||||
e.localizedMessage,
|
}
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.showActionErrorIfNeeded(result: ActionRunnable.Result) {
|
fun Context.showActionErrorIfNeeded(result: ActionRunnable.Result) {
|
||||||
@@ -259,6 +257,15 @@ fun Context.showActionErrorIfNeeded(result: ActionRunnable.Result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun CoordinatorLayout.showError(error: Throwable?) {
|
||||||
|
val message = if (error is LocalizedException) {
|
||||||
|
error.getLocalizedMessage(resources) ?: error.message
|
||||||
|
} else error?.message
|
||||||
|
message?.let {
|
||||||
|
Snackbar.make(this, message, Snackbar.LENGTH_LONG).asError().show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun CoordinatorLayout.showActionErrorIfNeeded(result: ActionRunnable.Result) {
|
fun CoordinatorLayout.showActionErrorIfNeeded(result: ActionRunnable.Result) {
|
||||||
if (!result.isSuccess) {
|
if (!result.isSuccess) {
|
||||||
result.exception?.getLocalizedMessage(resources)?.let { errorMessage ->
|
result.exception?.getLocalizedMessage(resources)?.let { errorMessage ->
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class DatabaseViewModel(application: Application): AndroidViewModel(application)
|
|||||||
val database: ContextualDatabase?
|
val database: ContextualDatabase?
|
||||||
get() = databaseState.value
|
get() = databaseState.value
|
||||||
|
|
||||||
private val mActionState = MutableStateFlow<ActionState>(ActionState.Loading)
|
private val mActionState = MutableStateFlow<ActionState>(ActionState.Wait)
|
||||||
val actionState: StateFlow<ActionState> = mActionState
|
val actionState: StateFlow<ActionState> = mActionState
|
||||||
|
|
||||||
private var mDatabaseTaskProvider: DatabaseTaskProvider = DatabaseTaskProvider(
|
private var mDatabaseTaskProvider: DatabaseTaskProvider = DatabaseTaskProvider(
|
||||||
@@ -469,7 +469,7 @@ class DatabaseViewModel(application: Application): AndroidViewModel(application)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed class ActionState {
|
sealed class ActionState {
|
||||||
object Loading: ActionState()
|
object Wait: ActionState()
|
||||||
object OnDatabaseReloaded: ActionState()
|
object OnDatabaseReloaded: ActionState()
|
||||||
data class OnDatabaseActionRequested(
|
data class OnDatabaseActionRequested(
|
||||||
val bundle: Bundle? = null,
|
val bundle: Bundle? = null,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.kunzisoft.keepass.model.RegisterInfo
|
|||||||
import com.kunzisoft.keepass.model.StreamDirection
|
import com.kunzisoft.keepass.model.StreamDirection
|
||||||
import com.kunzisoft.keepass.otp.OtpElement
|
import com.kunzisoft.keepass.otp.OtpElement
|
||||||
import com.kunzisoft.keepass.utils.IOActionTask
|
import com.kunzisoft.keepass.utils.IOActionTask
|
||||||
|
import com.kunzisoft.keepass.view.ProtectedFieldView
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@@ -37,7 +38,6 @@ class EntryEditViewModel: NodeEditViewModel() {
|
|||||||
|
|
||||||
// To show dialog only one time
|
// To show dialog only one time
|
||||||
var backPressedAlreadyApproved = false
|
var backPressedAlreadyApproved = false
|
||||||
var warningOverwriteDataAlreadyApproved = false
|
|
||||||
|
|
||||||
// Useful to not relaunch a current action
|
// Useful to not relaunch a current action
|
||||||
private var actionLocked: Boolean = false
|
private var actionLocked: Boolean = false
|
||||||
@@ -81,8 +81,8 @@ class EntryEditViewModel: NodeEditViewModel() {
|
|||||||
val onBinaryPreviewLoaded : LiveData<AttachmentPosition> get() = _onBinaryPreviewLoaded
|
val onBinaryPreviewLoaded : LiveData<AttachmentPosition> get() = _onBinaryPreviewLoaded
|
||||||
private val _onBinaryPreviewLoaded = SingleLiveEvent<AttachmentPosition>()
|
private val _onBinaryPreviewLoaded = SingleLiveEvent<AttachmentPosition>()
|
||||||
|
|
||||||
private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
|
private val mEntryEditState = MutableStateFlow<EntryEditState>(EntryEditState.Loading)
|
||||||
val uiState: StateFlow<UIState> = mUiState
|
val entryEditState: StateFlow<EntryEditState> = mEntryEditState
|
||||||
|
|
||||||
fun loadTemplateEntry(database: ContextualDatabase?) {
|
fun loadTemplateEntry(database: ContextualDatabase?) {
|
||||||
loadTemplateEntry(database, mEntryId, mParentId, mRegisterInfo)
|
loadTemplateEntry(database, mEntryId, mParentId, mRegisterInfo)
|
||||||
@@ -125,7 +125,7 @@ class EntryEditViewModel: NodeEditViewModel() {
|
|||||||
mEntryId = null
|
mEntryId = null
|
||||||
_templatesEntry.value = templatesEntry
|
_templatesEntry.value = templatesEntry
|
||||||
if (templatesEntry?.overwrittenData == true) {
|
if (templatesEntry?.overwrittenData == true) {
|
||||||
mUiState.value = UIState.ShowOverwriteMessage
|
mEntryEditState.value = EntryEditState.ShowOverwriteMessage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
).execute()
|
).execute()
|
||||||
@@ -293,6 +293,10 @@ class EntryEditViewModel: NodeEditViewModel() {
|
|||||||
_onPasswordSelected.value = passwordField
|
_onPasswordSelected.value = passwordField
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun requestUnprotectField(fieldView: ProtectedFieldView) {
|
||||||
|
mEntryEditState.value = EntryEditState.RequestUnprotectField(fieldView)
|
||||||
|
}
|
||||||
|
|
||||||
fun requestCustomFieldEdition(customField: Field) {
|
fun requestCustomFieldEdition(customField: Field) {
|
||||||
_requestCustomFieldEdition.value = customField
|
_requestCustomFieldEdition.value = customField
|
||||||
}
|
}
|
||||||
@@ -348,6 +352,10 @@ class EntryEditViewModel: NodeEditViewModel() {
|
|||||||
_onBinaryPreviewLoaded.value = AttachmentPosition(entryAttachmentState, viewPosition)
|
_onBinaryPreviewLoaded.value = AttachmentPosition(entryAttachmentState, viewPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun actionPerformed() {
|
||||||
|
mEntryEditState.value = EntryEditState.Loading
|
||||||
|
}
|
||||||
|
|
||||||
data class TemplatesEntry(
|
data class TemplatesEntry(
|
||||||
val isTemplate: Boolean,
|
val isTemplate: Boolean,
|
||||||
val templates: List<Template>,
|
val templates: List<Template>,
|
||||||
@@ -362,9 +370,12 @@ class EntryEditViewModel: NodeEditViewModel() {
|
|||||||
data class AttachmentUpload(val attachmentToUploadUri: Uri, val attachment: Attachment)
|
data class AttachmentUpload(val attachmentToUploadUri: Uri, val attachment: Attachment)
|
||||||
data class AttachmentPosition(val entryAttachmentState: EntryAttachmentState, val viewPosition: Float)
|
data class AttachmentPosition(val entryAttachmentState: EntryAttachmentState, val viewPosition: Float)
|
||||||
|
|
||||||
sealed class UIState {
|
sealed class EntryEditState {
|
||||||
object Loading: UIState()
|
object Loading: EntryEditState()
|
||||||
object ShowOverwriteMessage: UIState()
|
object ShowOverwriteMessage: EntryEditState()
|
||||||
|
data class RequestUnprotectField(
|
||||||
|
val protectedFieldView: ProtectedFieldView
|
||||||
|
): EntryEditState()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -19,25 +19,43 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.viewmodels
|
package com.kunzisoft.keepass.viewmodels
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
|
import com.kunzisoft.keepass.database.element.Field
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||||
import com.kunzisoft.keepass.database.element.template.Template
|
import com.kunzisoft.keepass.database.element.template.Template
|
||||||
|
import com.kunzisoft.keepass.database.element.template.TemplateField
|
||||||
|
import com.kunzisoft.keepass.database.helper.getLocalizedName
|
||||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||||
import com.kunzisoft.keepass.model.EntryInfo
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
import com.kunzisoft.keepass.otp.OtpElement
|
import com.kunzisoft.keepass.otp.OtpElement
|
||||||
|
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
||||||
import com.kunzisoft.keepass.utils.IOActionTask
|
import com.kunzisoft.keepass.utils.IOActionTask
|
||||||
|
import com.kunzisoft.keepass.view.ProtectedFieldView
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
class EntryViewModel: ViewModel() {
|
class EntryViewModel(application: Application): AndroidViewModel(application) {
|
||||||
|
|
||||||
private var mMainEntryId: NodeId<UUID>? = null
|
var mainEntryId: NodeId<UUID>? = null
|
||||||
private var mHistoryPosition: Int = -1
|
private set
|
||||||
|
var entryInfo: EntryInfo? = null
|
||||||
|
private set
|
||||||
|
var historyPosition: Int = -1
|
||||||
|
private set
|
||||||
|
var entryIsHistory: Boolean = false
|
||||||
|
private set
|
||||||
|
var entryLoaded = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
private var mClipboardHelper: ClipboardHelper = ClipboardHelper(application)
|
||||||
|
|
||||||
val entryInfoHistory : LiveData<EntryInfoHistory?> get() = _entryInfoHistory
|
val entryInfoHistory : LiveData<EntryInfoHistory?> get() = _entryInfoHistory
|
||||||
private val _entryInfoHistory = MutableLiveData<EntryInfoHistory?>()
|
private val _entryInfoHistory = MutableLiveData<EntryInfoHistory?>()
|
||||||
@@ -59,13 +77,16 @@ class EntryViewModel: ViewModel() {
|
|||||||
val historySelected : LiveData<EntryHistory> get() = _historySelected
|
val historySelected : LiveData<EntryHistory> get() = _historySelected
|
||||||
private val _historySelected = SingleLiveEvent<EntryHistory>()
|
private val _historySelected = SingleLiveEvent<EntryHistory>()
|
||||||
|
|
||||||
|
private val mEntryState = MutableStateFlow<EntryState>(EntryState.Loading)
|
||||||
|
val entryState: StateFlow<EntryState> = mEntryState
|
||||||
|
|
||||||
fun loadDatabase(database: ContextualDatabase?) {
|
fun loadDatabase(database: ContextualDatabase?) {
|
||||||
loadEntry(database, mMainEntryId, mHistoryPosition)
|
loadEntry(database, mainEntryId, historyPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadEntry(database: ContextualDatabase?, mainEntryId: NodeId<UUID>?, historyPosition: Int = -1) {
|
fun loadEntry(database: ContextualDatabase?, mainEntryId: NodeId<UUID>?, historyPosition: Int = -1) {
|
||||||
this.mMainEntryId = mainEntryId
|
this.mainEntryId = mainEntryId
|
||||||
this.mHistoryPosition = historyPosition
|
this.historyPosition = historyPosition
|
||||||
|
|
||||||
if (database != null && mainEntryId != null) {
|
if (database != null && mainEntryId != null) {
|
||||||
IOActionTask(
|
IOActionTask(
|
||||||
@@ -104,6 +125,13 @@ class EntryViewModel: ViewModel() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ entryInfoHistory ->
|
{ entryInfoHistory ->
|
||||||
|
if (entryInfoHistory != null) {
|
||||||
|
this.mainEntryId = entryInfoHistory.mainEntryId
|
||||||
|
this.entryInfo = entryInfoHistory.entryInfo
|
||||||
|
this.historyPosition = historyPosition
|
||||||
|
this.entryIsHistory = historyPosition > -1
|
||||||
|
this.entryLoaded = true
|
||||||
|
}
|
||||||
_entryInfoHistory.value = entryInfoHistory
|
_entryInfoHistory.value = entryInfoHistory
|
||||||
_entryHistory.value = entryInfoHistory?.entryHistory
|
_entryHistory.value = entryInfoHistory?.entryHistory
|
||||||
}
|
}
|
||||||
@@ -111,6 +139,18 @@ class EntryViewModel: ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun requestUnprotectField(fieldView: ProtectedFieldView) {
|
||||||
|
mEntryState.value = EntryState.RequestUnprotectField(fieldView)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestCopyField(field: Field, fieldView: ProtectedFieldView) {
|
||||||
|
// Only request the User Verification if the field is protected and not shown
|
||||||
|
if (field.protectedValue.isProtected && fieldView.isCurrentlyProtected())
|
||||||
|
mEntryState.value = EntryState.RequestCopyProtectedField(field)
|
||||||
|
else
|
||||||
|
copyToClipboard(field)
|
||||||
|
}
|
||||||
|
|
||||||
fun onOtpElementUpdated(optElement: OtpElement?) {
|
fun onOtpElementUpdated(optElement: OtpElement?) {
|
||||||
_onOtpElementUpdated.value = optElement
|
_onOtpElementUpdated.value = optElement
|
||||||
}
|
}
|
||||||
@@ -131,6 +171,22 @@ class EntryViewModel: ViewModel() {
|
|||||||
_sectionSelected.value = section
|
_sectionSelected.value = section
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun copyToClipboard(field: Field) {
|
||||||
|
mClipboardHelper.timeoutCopyToClipboard(
|
||||||
|
TemplateField.getLocalizedName(getApplication(), field.name),
|
||||||
|
field.protectedValue.stringValue,
|
||||||
|
field.protectedValue.isProtected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyToClipboard(text: String) {
|
||||||
|
mClipboardHelper.timeoutCopyToClipboard(text, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun actionPerformed() {
|
||||||
|
mEntryState.value = EntryState.Loading
|
||||||
|
}
|
||||||
|
|
||||||
data class EntryInfoHistory(var mainEntryId: NodeId<UUID>,
|
data class EntryInfoHistory(var mainEntryId: NodeId<UUID>,
|
||||||
var historyPosition: Int,
|
var historyPosition: Int,
|
||||||
val template: Template,
|
val template: Template,
|
||||||
@@ -152,6 +208,16 @@ class EntryViewModel: ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class EntryState {
|
||||||
|
object Loading: EntryState()
|
||||||
|
data class RequestUnprotectField(
|
||||||
|
val protectedFieldView: ProtectedFieldView
|
||||||
|
): EntryState()
|
||||||
|
data class RequestCopyProtectedField(
|
||||||
|
val field: Field
|
||||||
|
): EntryState()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = EntryViewModel::class.java.name
|
private val TAG = EntryViewModel::class.java.name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.kunzisoft.keepass.viewmodels
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.kunzisoft.keepass.database.MainCredential
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ViewModel for the Main Credential Dialog
|
||||||
|
* Easily retrieves main credential from the database identified by its URI
|
||||||
|
*/
|
||||||
|
class MainCredentialViewModel: ViewModel() {
|
||||||
|
|
||||||
|
private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
|
||||||
|
val uiState: StateFlow<UIState> = mUiState
|
||||||
|
|
||||||
|
fun validateMainCredential(
|
||||||
|
databaseUri: Uri,
|
||||||
|
mainCredential: MainCredential
|
||||||
|
) {
|
||||||
|
mUiState.value = UIState.OnMainCredentialEntered(databaseUri, mainCredential)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelMainCredential(
|
||||||
|
databaseUri: Uri?,
|
||||||
|
error: Throwable? = null
|
||||||
|
) {
|
||||||
|
mUiState.value = UIState.OnMainCredentialCanceled(databaseUri, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onActionReceived() {
|
||||||
|
mUiState.value = UIState.Loading
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class UIState {
|
||||||
|
object Loading: UIState()
|
||||||
|
data class OnMainCredentialEntered(
|
||||||
|
val databaseUri: Uri,
|
||||||
|
val mainCredential: MainCredential
|
||||||
|
): UIState()
|
||||||
|
data class OnMainCredentialCanceled(
|
||||||
|
val databaseUri: Uri?,
|
||||||
|
val error: Throwable?
|
||||||
|
): UIState()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.kunzisoft.keepass.viewmodels
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
class SettingsViewModel(application: Application): AndroidViewModel(application) {
|
||||||
|
|
||||||
|
private val mSettingsState = MutableStateFlow<SettingsState>(SettingsState.Wait)
|
||||||
|
val settingsState: StateFlow<SettingsState> = mSettingsState
|
||||||
|
|
||||||
|
|
||||||
|
var dialogFragment: DialogFragment? = null
|
||||||
|
|
||||||
|
fun showError(error: Throwable?) {
|
||||||
|
mSettingsState.value = SettingsState.ShowError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun errorShown() {
|
||||||
|
mSettingsState.value = SettingsState.Wait
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class SettingsState {
|
||||||
|
object Wait: SettingsState()
|
||||||
|
data class ShowError(
|
||||||
|
val error: Throwable? = null
|
||||||
|
): SettingsState()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.kunzisoft.keepass.viewmodels
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
|
import com.kunzisoft.keepass.database.element.MasterCredential.CREATOR.getCheckKey
|
||||||
|
import com.kunzisoft.keepass.database.exception.InvalidCredentialsDatabaseException
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ViewModel for the User Verification
|
||||||
|
*/
|
||||||
|
class UserVerificationViewModel: ViewModel() {
|
||||||
|
|
||||||
|
private val mUVState = MutableStateFlow<UVState>(UVState.Loading)
|
||||||
|
val userVerificationState: StateFlow<UVState> = mUVState
|
||||||
|
|
||||||
|
var dataToVerify: UserVerificationData? = null
|
||||||
|
|
||||||
|
fun checkMainCredential(checkString: String) {
|
||||||
|
// Check the password part
|
||||||
|
val data = dataToVerify
|
||||||
|
if (data?.database?.checkKey(getCheckKey(checkString)) == true)
|
||||||
|
onUserVerificationSucceeded(data)
|
||||||
|
else {
|
||||||
|
onUserVerificationFailed(dataToVerify, InvalidCredentialsDatabaseException())
|
||||||
|
}
|
||||||
|
dataToVerify = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onUserVerificationSucceeded(dataToVerify: UserVerificationData) {
|
||||||
|
mUVState.value = UVState.OnUserVerificationSucceeded(dataToVerify)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onUserVerificationFailed(
|
||||||
|
dataToVerify: UserVerificationData? = null,
|
||||||
|
error: Throwable? = null
|
||||||
|
) {
|
||||||
|
this.dataToVerify = dataToVerify
|
||||||
|
mUVState.value = UVState.OnUserVerificationCanceled(dataToVerify, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onUserVerificationReceived() {
|
||||||
|
mUVState.value = UVState.Loading
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class UVState {
|
||||||
|
object Loading: UVState()
|
||||||
|
data class OnUserVerificationSucceeded(
|
||||||
|
val dataToVerify: UserVerificationData
|
||||||
|
): UVState()
|
||||||
|
data class OnUserVerificationCanceled(
|
||||||
|
val dataToVerify: UserVerificationData?,
|
||||||
|
val error: Throwable?
|
||||||
|
): UVState()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M11,4C9.95,4 9.063,4.363 8.338,5.088C7.613,5.813 7.25,6.7 7.25,7.75C7.25,8.8 7.613,9.687 8.338,10.412C9.063,11.137 9.95,11.5 11,11.5C12.05,11.5 12.937,11.137 13.662,10.412C14.387,9.687 14.75,8.8 14.75,7.75C14.75,6.7 14.387,5.813 13.662,5.088C12.937,4.363 12.05,4 11,4zM11,13.5C10.567,13.5 10.147,13.521 9.738,13.563C9.33,13.604 8.908,13.667 8.475,13.75C8.47,13.754 8.049,13.845 7.912,13.875C6.887,14.125 5.816,14.5 4.699,15C4.199,15.233 3.791,15.575 3.475,16.025C3.158,16.475 3,17.017 3,17.65L3,20L6,20L13.682,20C13.429,19.394 13.287,18.729 13.287,18.031C13.287,16.501 13.971,15.144 15.033,14.205C14.724,14.11 14.389,13.949 14.088,13.875C14.08,13.877 13.525,13.75 13.525,13.75C13.301,13.707 13.089,13.684 12.871,13.652C12.721,13.63 12.57,13.603 12.42,13.586C12.368,13.58 12.313,13.568 12.262,13.563C11.853,13.521 11.433,13.5 11,13.5zM18.414,14.445C16.433,14.445 14.826,16.05 14.826,18.031C14.826,20.012 16.433,21.619 18.414,21.619C20.395,21.619 22,20.012 22,18.031C22,16.05 20.395,14.445 18.414,14.445zM19.848,16.314C19.956,16.314 20.064,16.355 20.146,16.438L20.443,16.736C20.608,16.9 20.607,17.168 20.443,17.332L18.145,19.633C17.98,19.797 17.711,19.797 17.547,19.633L16.391,18.475C16.226,18.31 16.226,18.043 16.391,17.879L16.689,17.58C16.854,17.416 17.121,17.416 17.285,17.58L17.846,18.139L19.549,16.438C19.631,16.355 19.739,16.314 19.848,16.314z"
|
||||||
|
android:fillColor="#7D7D7D"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2025 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/>.
|
||||||
|
-->
|
||||||
|
<ScrollView 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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<LinearLayout
|
||||||
|
android:padding="@dimen/default_margin"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:importantForAutofill="noExcludeDescendants"
|
||||||
|
tools:targetApi="o">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal" >
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/user_verification_information"
|
||||||
|
android:text="@string/user_verification_required_title"
|
||||||
|
style="@style/KeepassDXStyle.Title"/>
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/user_verification_information"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:src="@drawable/ic_info_white_24dp"
|
||||||
|
style="@style/KeepassDXStyle.ImageButton.Simple"
|
||||||
|
android:contentDescription="@string/content_description_user_verification_information"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/user_verification_required_description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/card_view_margin_horizontal"
|
||||||
|
android:layout_marginLeft="@dimen/card_view_margin_horizontal"
|
||||||
|
android:layout_marginEnd="@dimen/card_view_margin_horizontal"
|
||||||
|
android:layout_marginRight="@dimen/card_view_margin_horizontal"
|
||||||
|
android:text="@string/user_verification_database_credential"
|
||||||
|
android:textColor="?attr/colorSecondary"/>
|
||||||
|
|
||||||
|
<!-- Password -->
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/setup_check_password_input_layout"
|
||||||
|
android:layout_margin="@dimen/card_view_margin_horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:endIconMode="password_toggle"
|
||||||
|
app:endIconTint="?attr/colorSecondary">
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/setup_check_password_edit_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusedByDefault="true"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:hint="@string/first_chars" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
@@ -75,8 +75,7 @@
|
|||||||
<com.kunzisoft.keepass.view.PasswordEditView
|
<com.kunzisoft.keepass.view.PasswordEditView
|
||||||
android:id="@+id/password_view"
|
android:id="@+id/password_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"/>
|
||||||
app:passwordVisible="false"/>
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/password_repeat_input_layout"
|
android:id="@+id/password_repeat_input_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
1
app/src/main/res/resources.properties
Normal file
1
app/src/main/res/resources.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
unqualifiedResLocale=en-US
|
||||||
@@ -61,7 +61,6 @@
|
|||||||
<string name="list_size_summary">حجم النص في قائمة العناصر</string>
|
<string name="list_size_summary">حجم النص في قائمة العناصر</string>
|
||||||
<string name="loading_database">يحمل قاعدة البيانات…</string>
|
<string name="loading_database">يحمل قاعدة البيانات…</string>
|
||||||
<string name="lowercase">حروف صغيرة</string>
|
<string name="lowercase">حروف صغيرة</string>
|
||||||
<string name="hide_password_summary">أخفِ كلمات السر (***) افتراضيًا</string>
|
|
||||||
<string name="about">عن التطبيق</string>
|
<string name="about">عن التطبيق</string>
|
||||||
<string name="menu_change_key_settings">تغيير المفتاح الرئيسي</string>
|
<string name="menu_change_key_settings">تغيير المفتاح الرئيسي</string>
|
||||||
<string name="settings">الإعدادات</string>
|
<string name="settings">الإعدادات</string>
|
||||||
@@ -143,13 +142,11 @@
|
|||||||
<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_generated_password">كلمة السر مولّدة</string>
|
||||||
<string name="hint_keyfile">ملف المفتاح</string>
|
<string name="hint_keyfile">ملف المفتاح</string>
|
||||||
<string name="hide_password_title">أخفِ كلمات السر</string>
|
|
||||||
<string name="copy_field">نُسخة من %1$s</string>
|
<string name="copy_field">نُسخة من %1$s</string>
|
||||||
<string name="menu_copy">نسخ</string>
|
<string name="menu_copy">نسخ</string>
|
||||||
<string name="menu_move">نقل</string>
|
<string name="menu_move">نقل</string>
|
||||||
<string name="menu_paste">لصق</string>
|
<string name="menu_paste">لصق</string>
|
||||||
<string name="menu_cancel">ألغِ</string>
|
<string name="menu_cancel">ألغِ</string>
|
||||||
<string name="menu_hide_password">أخفِ كلمة السر</string>
|
|
||||||
<string name="menu_showpass">أظهر كلمة السر</string>
|
<string name="menu_showpass">أظهر كلمة السر</string>
|
||||||
<string name="menu_url">الانتقال الى الرابط</string>
|
<string name="menu_url">الانتقال الى الرابط</string>
|
||||||
<string name="menu_file_selection_read_only">محمي من التعديل</string>
|
<string name="menu_file_selection_read_only">محمي من التعديل</string>
|
||||||
@@ -335,7 +332,7 @@
|
|||||||
<string name="device_unlock_explanation_summary">استخدم إلغاء القفل الجهاز لفتح قاعدة البيانات بسهولة</string>
|
<string name="device_unlock_explanation_summary">استخدم إلغاء القفل الجهاز لفتح قاعدة البيانات بسهولة</string>
|
||||||
<string name="lock_database_show_button_summary">يعرض زر القَفل في الواجهة</string>
|
<string name="lock_database_show_button_summary">يعرض زر القَفل في الواجهة</string>
|
||||||
<string name="lock_database_show_button_title">أظهر زر القفل</string>
|
<string name="lock_database_show_button_title">أظهر زر القفل</string>
|
||||||
<string name="lock_database_back_root_summary">قفل قاعدة البيانات عند النقر على زر الرجوع في الشاشة الرئيسية</string>
|
<string name="lock_database_back_root_summary">اضغط على \"رجوع\" لقفل قاعدة البيانات إذا كنت في الشاشة الجذر لقاعدة البيانات</string>
|
||||||
<string name="lock_database_back_root_title">اضغط على \"رجوع\" للإقفال</string>
|
<string name="lock_database_back_root_title">اضغط على \"رجوع\" للإقفال</string>
|
||||||
<string name="clipboard_explanation_summary">انسخ حقول المدخل باستخدام الحافظة</string>
|
<string name="clipboard_explanation_summary">انسخ حقول المدخل باستخدام الحافظة</string>
|
||||||
<string name="database_opened">قاعدة البيانات مفتوحة</string>
|
<string name="database_opened">قاعدة البيانات مفتوحة</string>
|
||||||
@@ -642,8 +639,8 @@
|
|||||||
<string name="show_uuid_title">أظهر \"المعرّف العام المميز\" UUID</string>
|
<string name="show_uuid_title">أظهر \"المعرّف العام المميز\" UUID</string>
|
||||||
<string name="unlock_and_link_biometric">رابط فتح الجهاز</string>
|
<string name="unlock_and_link_biometric">رابط فتح الجهاز</string>
|
||||||
<string name="device_unlock_invalid_key">لا يمكن قراءة مفتاح فتح الجهاز. يرجى حذفه وتكرار إجراء التعرف على الفتح.</string>
|
<string name="device_unlock_invalid_key">لا يمكن قراءة مفتاح فتح الجهاز. يرجى حذفه وتكرار إجراء التعرف على الفتح.</string>
|
||||||
<string name="menu_appearance_settings_summary">المظاهر والألوان والسمات</string>
|
<string name="menu_appearance_settings_summary">المظاهر والألوان والأيقونات والخطوط والسمات</string>
|
||||||
<string name="autofill_explanation_summary">تمكين الملء التلقائي لملء النماذج بسرعة في التطبيقات الأخرى</string>
|
<string name="autofill_explanation_summary">اضبط الملء التلقائي لملء النماذج بسرعة في التطبيقات الأخرى</string>
|
||||||
<string name="device_credential_unlock_enable_summary">يتيح لك استخدام بيانات اعتماد جهازك لفتح قاعدة البيانات</string>
|
<string name="device_credential_unlock_enable_summary">يتيح لك استخدام بيانات اعتماد جهازك لفتح قاعدة البيانات</string>
|
||||||
<string name="unlock">فتح</string>
|
<string name="unlock">فتح</string>
|
||||||
<string name="menu_app_settings_summary">البحث، القفل، التاريخ، الخصائص</string>
|
<string name="menu_app_settings_summary">البحث، القفل، التاريخ، الخصائص</string>
|
||||||
@@ -691,4 +688,45 @@
|
|||||||
<string name="warning_large_keyfile">لا يُنصح بإضافة ملف مفتاحي كبير، فقد يؤدي هذا إلى منع فتح قاعدة البيانات.</string>
|
<string name="warning_large_keyfile">لا يُنصح بإضافة ملف مفتاحي كبير، فقد يؤدي هذا إلى منع فتح قاعدة البيانات.</string>
|
||||||
<string name="hide_templates_title">أخفِ القوالب</string>
|
<string name="hide_templates_title">أخفِ القوالب</string>
|
||||||
<string name="error_otp_secret_length">يجب أن يتكوّن المفتاح السري من %1$d أحرف على الأقل.</string>
|
<string name="error_otp_secret_length">يجب أن يتكوّن المفتاح السري من %1$d أحرف على الأقل.</string>
|
||||||
|
<string name="entry_application_id">معرّف التطبيق</string>
|
||||||
|
<string name="warning_overwrite_data_title">أتريد الكتابة فوق البيانات الموجودة؟</string>
|
||||||
|
<string name="warning_overwrite_data_description">سيؤدي هذا الإجراء إلى استبدال البيانات الموجودة في الإدخال، ويمكنك استرداد البيانات القديمة إذا كانت المحفوظات مفعلة.</string>
|
||||||
|
<string name="credential_provider">موفّر بيانات الاعتماد</string>
|
||||||
|
<string name="passkeys">مفاتيح المرور</string>
|
||||||
|
<string name="passkeys_explanation_summary">اضبط مفاتيح المرور لتسجيل دخول سريع وآمن بدون كلمة سر</string>
|
||||||
|
<string name="passkeys_preference_title">إعدادات مفاتيح المرور</string>
|
||||||
|
<string name="passkeys_close_database_title">أغلق قاعدة البيانات</string>
|
||||||
|
<string name="passkeys_close_database_summary">أغلق قاعدة البيانات بعد اختيار مفتاح المرور</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">التطبيقات المتميزة</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">أدر المتصفحات في القائمة المخصّصة للتطبيقات المتميزة</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">تحذير: يعمل تطبيق مميز كبوابة لاسترداد أصل الاستيثاق. تأكد من شرعيته لتجنب المشكلات الأمنية.</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">التطبيق غير معروف</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">يحاول %1$s تنفيذ إجراء مفتاح المرور.\n\nأتريد إضافته إلى قائمة التطبيقات المتميزة؟</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">التوقيع مفقود</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">تحذير: أُنشئ مفتاح المرور من عميل آخر أو حُذف التوقيع. تأكد من أن التطبيق الذي تريد الاستيثاق عليه جزء من نفس الخدمة وأنه شرعي لتجنب المشكلات الأمنية.\nإذا كان التطبيق متصفحًا، فلا تضف توقيعه إلى الإدخال، بل إلى قائمة التطبيقات الموثوقة في الإعدادات.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">%1$s غير معروف ويحاول الاستيثاق باستخدام مفتاح مرور موجود.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">إضافة توقيع التطبيق إلى إدخال مفتاح المرور؟</string>
|
||||||
|
<string name="passkeys_auto_select_title">تحديد تلقائي</string>
|
||||||
|
<string name="passkeys_auto_select_summary">حدّد تلقائي إذا كان هناك إدخال واحد فقط وقاعدة البيانات مفتوحة، فقط إذا كان التطبيق الطالب متوافقًا</string>
|
||||||
|
<string name="passkeys_backup_eligibility_title">أهلية النسخ الاحتياطي</string>
|
||||||
|
<string name="passkeys_backup_eligibility_summary">تحديد وقت الإنشاء ما إذا كان مسموحًا بنسخ مصدر بيانات اعتماد المفتاح العام احتياطيًا</string>
|
||||||
|
<string name="passkeys_backup_state_title">حالة النسخ الاحتياطي</string>
|
||||||
|
<string name="passkeys_backup_state_summary">أشر إلى أن بيانات الاعتماد مدعومة ومحمية ضد فقدان جهاز واحد</string>
|
||||||
|
<string name="credential_provider_service_subtitle">مفاتيح المرور، موفّر بيانات اعتماد الملء التلقائي</string>
|
||||||
|
<string name="passkey">مفتاح المرور</string>
|
||||||
|
<string name="passkey_service_name">موفّر بيانات اعتماد KeePassDX</string>
|
||||||
|
<string name="passkey_creation_description">احفظ مفتاح المرور في مدخل جديد</string>
|
||||||
|
<string name="passkey_update_description">حدِّث مفتاح المرور في %1$s</string>
|
||||||
|
<string name="passkey_selection_username">لم يُعثر على مفتاح مرور</string>
|
||||||
|
<string name="passkey_selection_description">حدّد مفتاح مرور موجود</string>
|
||||||
|
<string name="passkey_database_username">قاعدة بيانات KeePassDX</string>
|
||||||
|
<string name="passkey_locked_database_description">حدّد لفتح القفل</string>
|
||||||
|
<string name="passkey_username">اسم مستخدم مفتاح المرور</string>
|
||||||
|
<string name="passkey_private_key">المفتاح الخاص لمفتاح المرور</string>
|
||||||
|
<string name="passkey_credential_id">معرّف بيانات مفتاح المرور</string>
|
||||||
|
<string name="passkey_user_handle">معرّف مستخدم مفتاح المرور</string>
|
||||||
|
<string name="passkey_relying_party">الطرف المعتمد لمفتاح المرور</string>
|
||||||
|
<string name="passkey_backup_eligibility">أهلية النسخ الاحتياطي لمفتاح المرور</string>
|
||||||
|
<string name="passkey_backup_state">حالة النسخ الاحتياطي لمفتاح المرور</string>
|
||||||
|
<string name="error_passkey_result">تعذر إرجاع مفتاح المرور</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -142,7 +142,6 @@
|
|||||||
<string name="menu_paste">Mubadilə buferindən əlavə et</string>
|
<string name="menu_paste">Mubadilə buferindən əlavə et</string>
|
||||||
<string name="menu_delete">Sil</string>
|
<string name="menu_delete">Sil</string>
|
||||||
<string name="menu_cancel">Ləğv et</string>
|
<string name="menu_cancel">Ləğv et</string>
|
||||||
<string name="menu_hide_password">Şifrəni gizlət</string>
|
|
||||||
<string name="menu_lock">Məlumat bazasını kilidlə</string>
|
<string name="menu_lock">Məlumat bazasını kilidlə</string>
|
||||||
<string name="menu_save_database">Məlumatları yadda saxla</string>
|
<string name="menu_save_database">Məlumatları yadda saxla</string>
|
||||||
<string name="menu_merge_database">Məlumatları birləşdir</string>
|
<string name="menu_merge_database">Məlumatları birləşdir</string>
|
||||||
@@ -448,8 +447,6 @@
|
|||||||
<string name="invalid_db_sig">Məlumat bazasının formatını tanımaq mümkün olmadı.</string>
|
<string name="invalid_db_sig">Məlumat bazasının formatını tanımaq mümkün olmadı.</string>
|
||||||
<string name="keyfile_is_empty">Açar faylı boşdur.</string>
|
<string name="keyfile_is_empty">Açar faylı boşdur.</string>
|
||||||
<string name="length">Uzunluq</string>
|
<string name="length">Uzunluq</string>
|
||||||
<string name="hide_password_title">Şifrələri gizlət</string>
|
|
||||||
<string name="hide_password_summary">Şifrələri standart olaraq (***) ilə maskala</string>
|
|
||||||
<string name="colorize_password_title">Şifrələri rəngləndir</string>
|
<string name="colorize_password_title">Şifrələri rəngləndir</string>
|
||||||
<string name="colorize_password_summary">Şifrə hərflərini (simvollarını) növə görə rəngləndir</string>
|
<string name="colorize_password_summary">Şifrə hərflərini (simvollarını) növə görə rəngləndir</string>
|
||||||
<string name="list_entries_show_username_title">İstifadəçi adlarını göstər</string>
|
<string name="list_entries_show_username_title">İstifadəçi adlarını göstər</string>
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
<string name="menu_open">Otvori</string>
|
<string name="menu_open">Otvori</string>
|
||||||
<string name="menu_save_database">Sačuvaj podatake</string>
|
<string name="menu_save_database">Sačuvaj podatake</string>
|
||||||
<string name="menu_lock">Zaključaj bazu podataka</string>
|
<string name="menu_lock">Zaključaj bazu podataka</string>
|
||||||
<string name="menu_hide_password">Sakrij lozinku</string>
|
|
||||||
<string name="menu_cancel">Otkaži</string>
|
<string name="menu_cancel">Otkaži</string>
|
||||||
<string name="menu_delete">Izbriši</string>
|
<string name="menu_delete">Izbriši</string>
|
||||||
<string name="menu_paste">Nalepi</string>
|
<string name="menu_paste">Nalepi</string>
|
||||||
@@ -47,8 +46,6 @@
|
|||||||
<string name="copy_field">Kopija od %1$s</string>
|
<string name="copy_field">Kopija od %1$s</string>
|
||||||
<string name="menu_change_key_settings">Promeni glavni ključ</string>
|
<string name="menu_change_key_settings">Promeni glavni ključ</string>
|
||||||
<string name="about">O aplikaciji</string>
|
<string name="about">O aplikaciji</string>
|
||||||
<string name="hide_password_summary">Podrazumevaj maskiranje lozinki sa (***)</string>
|
|
||||||
<string name="hide_password_title">Sakrij lozinke</string>
|
|
||||||
<string name="lowercase">Mala slova</string>
|
<string name="lowercase">Mala slova</string>
|
||||||
<string name="loading_database">Učitavanje baze podataka…</string>
|
<string name="loading_database">Učitavanje baze podataka…</string>
|
||||||
<string name="creating_database">Kreiranje baze podataka…</string>
|
<string name="creating_database">Kreiranje baze podataka…</string>
|
||||||
|
|||||||
@@ -216,8 +216,6 @@
|
|||||||
<string name="keyfile_is_empty">Файл ключа пусты.</string>
|
<string name="keyfile_is_empty">Файл ключа пусты.</string>
|
||||||
<string name="length">Даўжыня</string>
|
<string name="length">Даўжыня</string>
|
||||||
<string name="nodes">Вузлы</string>
|
<string name="nodes">Вузлы</string>
|
||||||
<string name="hide_password_title">Схаваць паролі</string>
|
|
||||||
<string name="hide_password_summary">Маскіраваць паролі (***) па змаўчанні</string>
|
|
||||||
<string name="colorize_password_title">Размаляваць паролі</string>
|
<string name="colorize_password_title">Размаляваць паролі</string>
|
||||||
<string name="colorize_password_summary">Размаляваць сімвалы пароля па тыпу</string>
|
<string name="colorize_password_summary">Размаляваць сімвалы пароля па тыпу</string>
|
||||||
<string name="list_entries_show_username_title">Паказаць імёны карыстальнікаў</string>
|
<string name="list_entries_show_username_title">Паказаць імёны карыстальнікаў</string>
|
||||||
@@ -258,7 +256,6 @@
|
|||||||
<string name="menu_paste">Уставіць</string>
|
<string name="menu_paste">Уставіць</string>
|
||||||
<string name="menu_delete">Выдаліць</string>
|
<string name="menu_delete">Выдаліць</string>
|
||||||
<string name="menu_cancel">Адмена</string>
|
<string name="menu_cancel">Адмена</string>
|
||||||
<string name="menu_hide_password">Схаваць пароль</string>
|
|
||||||
<string name="menu_lock">Заблакаваць базу дадзеных</string>
|
<string name="menu_lock">Заблакаваць базу дадзеных</string>
|
||||||
<string name="menu_save_database">Захаваць дадзеныя</string>
|
<string name="menu_save_database">Захаваць дадзеныя</string>
|
||||||
<string name="menu_merge_database">Аб\'яднаць дадзеныя</string>
|
<string name="menu_merge_database">Аб\'яднаць дадзеныя</string>
|
||||||
|
|||||||
@@ -71,10 +71,8 @@
|
|||||||
<string name="unlock">Отключване</string>
|
<string name="unlock">Отключване</string>
|
||||||
<string name="unavailable_feature_hardware">Необходимият хардуер не може да бъде намерен.</string>
|
<string name="unavailable_feature_hardware">Необходимият хардуер не може да бъде намерен.</string>
|
||||||
<string name="hardware_key">Хардуерен ключ</string>
|
<string name="hardware_key">Хардуерен ключ</string>
|
||||||
<string name="hide_password_summary">Скриване на паролите (***) по подразбиране</string>
|
|
||||||
<string name="select_database_file">Отключване на хранилище</string>
|
<string name="select_database_file">Отключване на хранилище</string>
|
||||||
<string name="content_description_hardware_key_checkbox">Отметка на поле с хардуерен ключ</string>
|
<string name="content_description_hardware_key_checkbox">Отметка на поле с хардуерен ключ</string>
|
||||||
<string name="hide_password_title">Скриване на пароли</string>
|
|
||||||
<string name="hint_pass">Парола</string>
|
<string name="hint_pass">Парола</string>
|
||||||
<string name="education_select_database_title">Отворете съществуващо хранилище</string>
|
<string name="education_select_database_title">Отворете съществуващо хранилище</string>
|
||||||
<string name="content_description_keyfile_checkbox">Отметка на поле за файл с ключ</string>
|
<string name="content_description_keyfile_checkbox">Отметка на поле за файл с ключ</string>
|
||||||
@@ -157,7 +155,6 @@
|
|||||||
<string name="template">Шаблон</string>
|
<string name="template">Шаблон</string>
|
||||||
<string name="menu_move">Преместване</string>
|
<string name="menu_move">Преместване</string>
|
||||||
<string name="menu_cancel">Отказ</string>
|
<string name="menu_cancel">Отказ</string>
|
||||||
<string name="menu_hide_password">Скриване на парола</string>
|
|
||||||
<string name="auto_focus_search_summary">Търсене при отключване на хранилище</string>
|
<string name="auto_focus_search_summary">Търсене при отключване на хранилище</string>
|
||||||
<string name="saving_database">Запазване на хранилището…</string>
|
<string name="saving_database">Запазване на хранилището…</string>
|
||||||
<string name="command_execution">Изпълнение на команда…</string>
|
<string name="command_execution">Изпълнение на команда…</string>
|
||||||
@@ -232,7 +229,7 @@
|
|||||||
<string name="auto_focus_search_title">Бързо търсене</string>
|
<string name="auto_focus_search_title">Бързо търсене</string>
|
||||||
<string name="subdomain_search_title">Търсене на поддомейни</string>
|
<string name="subdomain_search_title">Търсене на поддомейни</string>
|
||||||
<string name="menu_delete">Изтриване</string>
|
<string name="menu_delete">Изтриване</string>
|
||||||
<string name="menu_appearance_settings_summary">Теми, цветове, атрибути</string>
|
<string name="menu_appearance_settings_summary">Теми, цветове, икони, шрифтове, атрибути</string>
|
||||||
<string name="download_initialization">Подготвяне…</string>
|
<string name="download_initialization">Подготвяне…</string>
|
||||||
<string name="content_description_entry_background_color">Цвят на фона на запис</string>
|
<string name="content_description_entry_background_color">Цвят на фона на запис</string>
|
||||||
<string name="html_about_licence">KeePassDX © %1$d Kunzisoft е приложение с <strong>отворен код</strong> и <strong>без реклами</strong>. \nРазпространява се под лиценза <strong>GPLv3</strong> без каквато и да е гаранция.</string>
|
<string name="html_about_licence">KeePassDX © %1$d Kunzisoft е приложение с <strong>отворен код</strong> и <strong>без реклами</strong>. \nРазпространява се под лиценза <strong>GPLv3</strong> без каквато и да е гаранция.</string>
|
||||||
@@ -283,11 +280,11 @@
|
|||||||
<string name="search">Търсене</string>
|
<string name="search">Търсене</string>
|
||||||
<string name="uppercase">Горен регистър</string>
|
<string name="uppercase">Горен регистър</string>
|
||||||
<string name="warning">Внимание</string>
|
<string name="warning">Внимание</string>
|
||||||
<string name="lock_database_back_root_summary">Заключва хранилището при докосване на бутона „Назад“ на началния екран</string>
|
<string name="lock_database_back_root_summary">Бутонът „Назад“ в кореновата папка заключва хранилището</string>
|
||||||
<string name="read_only">Само за четене</string>
|
<string name="read_only">Само за четене</string>
|
||||||
<string name="contains_duplicate_uuid">Хранилището съдържа повтарящ се идентификатор.</string>
|
<string name="contains_duplicate_uuid">Хранилището съдържа повтарящ се идентификатор.</string>
|
||||||
<string name="biometric">Биометричен ключ</string>
|
<string name="biometric">Биометричен ключ</string>
|
||||||
<string name="set_credential_provider_service_title">Задаване на подразбирана услуга за автоматично попълване</string>
|
<string name="set_credential_provider_service_title">Услуга за автоматично попълване на регистрации</string>
|
||||||
<string name="password_size_title">Дължина на създаваните пароли</string>
|
<string name="password_size_title">Дължина на създаваните пароли</string>
|
||||||
<string name="lock_database_back_root_title">Заключване при „Назад“</string>
|
<string name="lock_database_back_root_title">Заключване при „Назад“</string>
|
||||||
<string name="content">Съдържание</string>
|
<string name="content">Съдържание</string>
|
||||||
@@ -398,7 +395,7 @@
|
|||||||
<string name="education_search_summary">Търсете по заглавие, потребител или съдържание на други полета, за да намерите своите пароли.</string>
|
<string name="education_search_summary">Търсете по заглавие, потребител или съдържание на други полета, за да намерите своите пароли.</string>
|
||||||
<string name="html_about_contribution">За <strong>запазване на нашата независимост</strong>, <strong>отстраняване на дефекти</strong>, <strong>добавяне на нови възможности</strong> и <strong>поддържане на активна разработка</strong>, разчитаме на вашата <strong>поддръжка</strong>.</string>
|
<string name="html_about_contribution">За <strong>запазване на нашата независимост</strong>, <strong>отстраняване на дефекти</strong>, <strong>добавяне на нови възможности</strong> и <strong>поддържане на активна разработка</strong>, разчитаме на вашата <strong>поддръжка</strong>.</string>
|
||||||
<string name="lock_database_show_button_title">Бутон за заключване</string>
|
<string name="lock_database_show_button_title">Бутон за заключване</string>
|
||||||
<string name="autofill_explanation_summary">Включете услугата за попълване на формуляри в други приложения</string>
|
<string name="autofill_explanation_summary">Настройки на услугата за попълване на формуляри в други приложения</string>
|
||||||
<string name="properties">Свойства</string>
|
<string name="properties">Свойства</string>
|
||||||
<string name="education_validate_entry_summary">Не забравяйте да потвърдите записа и да го запазите в хранилището.
|
<string name="education_validate_entry_summary">Не забравяйте да потвърдите записа и да го запазите в хранилището.
|
||||||
\n
|
\n
|
||||||
@@ -653,7 +650,7 @@
|
|||||||
<string name="biometric_auto_open_prompt_summary">Автоматична заявка за отключване на устройството ако хранилището се отключва с устройството</string>
|
<string name="biometric_auto_open_prompt_summary">Автоматична заявка за отключване на устройството ако хранилището се отключва с устройството</string>
|
||||||
<string name="allow_copy_password_summary">Разрешава копиране на паролите и защитените полета от записите в междинната памет</string>
|
<string name="allow_copy_password_summary">Разрешава копиране на паролите и защитените полета от записите в междинната памет</string>
|
||||||
<string name="error_rebuild_list">Списъкът не може да бъде изграден отново.</string>
|
<string name="error_rebuild_list">Списъкът не може да бъде изграден отново.</string>
|
||||||
<string name="error_otp_type">Формулярът не разпознава този вид OTP и може да не създава правилни кодове за достъп.</string>
|
<string name="error_otp_type">Формулярът не разпознава този вид OTP и може да не създава верни кодове за достъп.</string>
|
||||||
<string name="warning_keyfile_integrity">Отпечатъкът от файла не е сигурен, защото Андроид може да променя данните в движение. Променете разширението на файла на .bin, за бъде невредим.</string>
|
<string name="warning_keyfile_integrity">Отпечатъкът от файла не е сигурен, защото Андроид може да променя данните в движение. Променете разширението на файла на .bin, за бъде невредим.</string>
|
||||||
<string name="allow_copy_password_title">Доверяване на междинната памет</string>
|
<string name="allow_copy_password_title">Доверяване на междинната памет</string>
|
||||||
<string name="warning_exact_alarm">Приложението няма права за използване на точен будилник. В резултат на това дейностите, които зависят от него няма да се изпълняват на време.</string>
|
<string name="warning_exact_alarm">Приложението няма права за използване на точен будилник. В резултат на това дейностите, които зависят от него няма да се изпълняват на време.</string>
|
||||||
@@ -681,4 +678,45 @@
|
|||||||
<string name="hide_templates_summary">Шаблоните не се показват</string>
|
<string name="hide_templates_summary">Шаблоните не се показват</string>
|
||||||
<string name="hide_templates_title">Скриване на шаблоните</string>
|
<string name="hide_templates_title">Скриване на шаблоните</string>
|
||||||
<string name="error_otp_secret_length">Тайният ключ трябва да бъде най-малко %1$d знаци.</string>
|
<string name="error_otp_secret_length">Тайният ключ трябва да бъде най-малко %1$d знаци.</string>
|
||||||
|
<string name="entry_application_id">Идент. приложение</string>
|
||||||
|
<string name="warning_overwrite_data_title">Презаписване на информация?</string>
|
||||||
|
<string name="warning_overwrite_data_description">Чрез това действие ще бъде презаоисана информация на записа. Може да намерите старите данни ако историята е включена.</string>
|
||||||
|
<string name="credential_provider">Доставчик на регистрации</string>
|
||||||
|
<string name="passkeys">Ключове за достъп</string>
|
||||||
|
<string name="passkeys_explanation_summary">Настройки на ключове за достъп за бърз и сигурен вход без парола</string>
|
||||||
|
<string name="passkeys_preference_title">Настройки на ключове за достъп</string>
|
||||||
|
<string name="passkeys_close_database_title">Затваряне на хранилище</string>
|
||||||
|
<string name="passkeys_close_database_summary">Затваряне на хранилището след избор на ключ за достъп</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">ВНИМАНИЕ: Приложенията с допълнителни права играят ролята на шлюз, от който се получава произхода на удостоверяването. За да избегнете проблеми със сигурността се уверете в неговата автентичност.</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">Приложения с допълнителни права</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">Управление на мрежови четци в потребителския списък на приложения с допълнителни права</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">Неразпознато приложение</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">Приложението „%1$s“ опитва да извърши действия с ключ за достъп .\n\nДа бъде ли добавено в списъка на приложения с допълнителни права?</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">Липсващ подпис</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">Приложението „%1$s“ не е разпознато, но се опитва да извърши удостоверяване с ключ за достъп.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">Добавяне на подпис към запис на ключ за достъп?</string>
|
||||||
|
<string name="passkeys_auto_select_title">Автоматичен избор</string>
|
||||||
|
<string name="passkeys_auto_select_summary">Работи при отключено хранилище, съвпадение на един запис и съвместимо запитващо приложение</string>
|
||||||
|
<string name="passkeys_backup_eligibility_title">Възможност за резервно копие</string>
|
||||||
|
<string name="passkeys_backup_eligibility_summary">Настройка, определяща още при създаване разрешението за резервно копие на публичния ключ на източника на регистрацията</string>
|
||||||
|
<string name="passkeys_backup_state_title">Наличие на резервно копие</string>
|
||||||
|
<string name="passkeys_backup_state_summary">Указва дали регистрацията е включена в резервно копие и е защитена от загуба на едно устройство</string>
|
||||||
|
<string name="credential_provider_service_subtitle">Услуга за автоматично попълване на ключове за достъп и регистрации</string>
|
||||||
|
<string name="passkey">Ключ за достъп</string>
|
||||||
|
<string name="passkey_service_name">Доставчик на регистрации на KeePassDX</string>
|
||||||
|
<string name="passkey_creation_description">Запазване ключа за достъп в друг запис</string>
|
||||||
|
<string name="passkey_update_description">Обновяване ключа за достъп в/ъв %1$s</string>
|
||||||
|
<string name="passkey_selection_username">Ключ за достъп не е намерен</string>
|
||||||
|
<string name="passkey_selection_description">Изберете съществуващ ключ за достъп</string>
|
||||||
|
<string name="passkey_database_username">Хранилище на KeePassDX</string>
|
||||||
|
<string name="passkey_locked_database_description">Изберете за отключване</string>
|
||||||
|
<string name="passkey_username">Потребител на ключа за достъп</string>
|
||||||
|
<string name="passkey_private_key">Частен ключ на ключа за достъп</string>
|
||||||
|
<string name="passkey_credential_id">Идент. на регистрация на ключа за достъп</string>
|
||||||
|
<string name="passkey_user_handle">Идент. на потребител на ключа за достъп</string>
|
||||||
|
<string name="passkey_backup_eligibility">Възможност за резервно копие на ключа за достъп</string>
|
||||||
|
<string name="passkey_backup_state">Състояние на резервно копие на ключа за достъп</string>
|
||||||
|
<string name="error_passkey_result">Грешка при връщане на ключ за достъп</string>
|
||||||
|
<string name="passkey_relying_party">Доверяваща страна на ключа за достъп</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">ВНИМАНИЕ: Ключът за достъп е създаден от друг клиент или подписът е премахнат. За да избегнете проблеми със сигурността се уверете, че приложението, което удостоверявате е част от същата услуга и е автентично.\nАко приложението е мрежов четец не добавяйте подписа му към записа, а в настройките го добавете в списъка на приложенията с допълнителни права.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -182,7 +182,6 @@
|
|||||||
<string name="invalid_db_same_uuid">একই UUID সহ %1$s %2$s ইতিমধ্যেই বিদ্যমান।</string>
|
<string name="invalid_db_same_uuid">একই UUID সহ %1$s %2$s ইতিমধ্যেই বিদ্যমান।</string>
|
||||||
<string name="passphrase">পাসফ্রেজ</string>
|
<string name="passphrase">পাসফ্রেজ</string>
|
||||||
<string name="keyfile_is_empty">কী ফাইলটি খালি।</string>
|
<string name="keyfile_is_empty">কী ফাইলটি খালি।</string>
|
||||||
<string name="hide_password_title">পাসওয়ার্ড লুকান</string>
|
|
||||||
<string name="list_size_summary">উপাদান তালিকায় পাঠ্যের আকার</string>
|
<string name="list_size_summary">উপাদান তালিকায় পাঠ্যের আকার</string>
|
||||||
<string name="creating_database">ডাটাবেস তৈরি করা হচ্ছে…</string>
|
<string name="creating_database">ডাটাবেস তৈরি করা হচ্ছে…</string>
|
||||||
<string name="loading_database">ডাটাবেস লোড হচ্ছে…</string>
|
<string name="loading_database">ডাটাবেস লোড হচ্ছে…</string>
|
||||||
@@ -316,7 +315,6 @@
|
|||||||
<string name="field_value">ক্ষেত্রের মান</string>
|
<string name="field_value">ক্ষেত্রের মান</string>
|
||||||
<string name="corrupted_file">দূষিত ফাইল।</string>
|
<string name="corrupted_file">দূষিত ফাইল।</string>
|
||||||
<string name="show_uuid_title">UUID দেখান</string>
|
<string name="show_uuid_title">UUID দেখান</string>
|
||||||
<string name="hide_password_summary">ডিফল্টরূপে মাস্ক পাসওয়ার্ড (***)</string>
|
|
||||||
<string name="list_entries_show_username_title">ব্যবহারকারীর নাম দেখান</string>
|
<string name="list_entries_show_username_title">ব্যবহারকারীর নাম দেখান</string>
|
||||||
<string name="show_uuid_summary">একটি এন্ট্রি বা একটি গ্রুপের সাথে সংযুক্ত UUID প্রদর্শন করে</string>
|
<string name="show_uuid_summary">একটি এন্ট্রি বা একটি গ্রুপের সাথে সংযুক্ত UUID প্রদর্শন করে</string>
|
||||||
<string name="menu_reload_database">ডেটা পুনরায় লোড করুন</string>
|
<string name="menu_reload_database">ডেটা পুনরায় লোড করুন</string>
|
||||||
@@ -330,7 +328,6 @@
|
|||||||
<string name="menu_merge_database">ডেটা মার্জ করুন</string>
|
<string name="menu_merge_database">ডেটা মার্জ করুন</string>
|
||||||
<string name="menu_merge_from">থেকে মার্জ করুন…</string>
|
<string name="menu_merge_from">থেকে মার্জ করুন…</string>
|
||||||
<string name="menu_showpass">পাসওয়ার্ড দেখাও</string>
|
<string name="menu_showpass">পাসওয়ার্ড দেখাও</string>
|
||||||
<string name="menu_hide_password">পাসওয়ার্ড লুকান</string>
|
|
||||||
<string name="menu_lock">ডাটাবেস লক করুন</string>
|
<string name="menu_lock">ডাটাবেস লক করুন</string>
|
||||||
<string name="menu_url">URL-এ যান</string>
|
<string name="menu_url">URL-এ যান</string>
|
||||||
<string name="menu_empty_recycle_bin">রিসাইকেল বিন খালি করুন</string>
|
<string name="menu_empty_recycle_bin">রিসাইকেল বিন খালি করুন</string>
|
||||||
|
|||||||
@@ -84,8 +84,6 @@
|
|||||||
<string name="list_size_summary">Mida del text a la llista de grups</string>
|
<string name="list_size_summary">Mida del text a la llista de grups</string>
|
||||||
<string name="loading_database">Carregant base de dades…</string>
|
<string name="loading_database">Carregant base de dades…</string>
|
||||||
<string name="lowercase">Minúscules</string>
|
<string name="lowercase">Minúscules</string>
|
||||||
<string name="hide_password_title">Emmascara contrasenya</string>
|
|
||||||
<string name="hide_password_summary">Amaga les contrasenyes per defecte</string>
|
|
||||||
<string name="about">Sobre</string>
|
<string name="about">Sobre</string>
|
||||||
<string name="menu_change_key_settings">Canvia Clau Mestra</string>
|
<string name="menu_change_key_settings">Canvia Clau Mestra</string>
|
||||||
<string name="settings">Paràmetres</string>
|
<string name="settings">Paràmetres</string>
|
||||||
@@ -93,7 +91,6 @@
|
|||||||
<string name="menu_delete">Esborra</string>
|
<string name="menu_delete">Esborra</string>
|
||||||
<string name="menu_donate">Donar</string>
|
<string name="menu_donate">Donar</string>
|
||||||
<string name="menu_edit">Editar</string>
|
<string name="menu_edit">Editar</string>
|
||||||
<string name="menu_hide_password">Amaga contrasenya</string>
|
|
||||||
<string name="menu_lock">Bloca la base de dades</string>
|
<string name="menu_lock">Bloca la base de dades</string>
|
||||||
<string name="menu_open">Obre</string>
|
<string name="menu_open">Obre</string>
|
||||||
<string name="menu_search">Cerca</string>
|
<string name="menu_search">Cerca</string>
|
||||||
|
|||||||
@@ -90,8 +90,6 @@
|
|||||||
<string name="list_size_summary">Velikost textu v seznamu prvků</string>
|
<string name="list_size_summary">Velikost textu v seznamu prvků</string>
|
||||||
<string name="loading_database">Načítám databázi…</string>
|
<string name="loading_database">Načítám databázi…</string>
|
||||||
<string name="lowercase">Malá písmena</string>
|
<string name="lowercase">Malá písmena</string>
|
||||||
<string name="hide_password_title">Skrýt hesla</string>
|
|
||||||
<string name="hide_password_summary">Ve výchozím stavu zobrazit (***) místo hesla</string>
|
|
||||||
<string name="about">O aplikaci</string>
|
<string name="about">O aplikaci</string>
|
||||||
<string name="menu_change_key_settings">Změnit hlavní klíč</string>
|
<string name="menu_change_key_settings">Změnit hlavní klíč</string>
|
||||||
<string name="settings">Nastavení</string>
|
<string name="settings">Nastavení</string>
|
||||||
@@ -99,7 +97,6 @@
|
|||||||
<string name="menu_delete">Smazat</string>
|
<string name="menu_delete">Smazat</string>
|
||||||
<string name="menu_donate">Přispět darem</string>
|
<string name="menu_donate">Přispět darem</string>
|
||||||
<string name="menu_edit">Upravit</string>
|
<string name="menu_edit">Upravit</string>
|
||||||
<string name="menu_hide_password">Skrýt heslo</string>
|
|
||||||
<string name="menu_lock">Zamknout databázi</string>
|
<string name="menu_lock">Zamknout databázi</string>
|
||||||
<string name="menu_open">Otevřít</string>
|
<string name="menu_open">Otevřít</string>
|
||||||
<string name="menu_search">Hledat</string>
|
<string name="menu_search">Hledat</string>
|
||||||
@@ -180,8 +177,8 @@
|
|||||||
<string name="general">Obecné</string>
|
<string name="general">Obecné</string>
|
||||||
<string name="autofill">Samovyplnění</string>
|
<string name="autofill">Samovyplnění</string>
|
||||||
<string name="autofill_sign_in_prompt">Přihlásit se s KeePassDX</string>
|
<string name="autofill_sign_in_prompt">Přihlásit se s KeePassDX</string>
|
||||||
<string name="set_credential_provider_service_title">Nastavit výchozí službu samovyplnění</string>
|
<string name="set_credential_provider_service_title">Služba poskytovatele údajů</string>
|
||||||
<string name="autofill_explanation_summary">Zapnout samovyplnění formulářů za účelem rychlého vyplnění v ostatních aplikacích</string>
|
<string name="autofill_explanation_summary">Nastavit automatické vyplnění formulářů za účelem rychlého vyplnění v ostatních aplikacích</string>
|
||||||
<string name="password_size_title">Délka generovaného hesla</string>
|
<string name="password_size_title">Délka generovaného hesla</string>
|
||||||
<string name="password_size_summary">Nastavení výchozí délky generovaných hesel</string>
|
<string name="password_size_summary">Nastavení výchozí délky generovaných hesel</string>
|
||||||
<string name="list_password_generator_options_title">Znaky hesla</string>
|
<string name="list_password_generator_options_title">Znaky hesla</string>
|
||||||
@@ -206,7 +203,7 @@
|
|||||||
<string name="assign_master_key">Přiřadit hlavní klíč</string>
|
<string name="assign_master_key">Přiřadit hlavní klíč</string>
|
||||||
<string name="create_keepass_file">Vytvořit nový trezor</string>
|
<string name="create_keepass_file">Vytvořit nový trezor</string>
|
||||||
<string name="recycle_bin_title">Využití koše</string>
|
<string name="recycle_bin_title">Využití koše</string>
|
||||||
<string name="recycle_bin_summary">Před smazáním přesune vybrané položky do skupiny s názvem \"Koš\"</string>
|
<string name="recycle_bin_summary">Před smazáním přesune vybrané záznamy do skupiny s názvem „Koš“</string>
|
||||||
<string name="monospace_font_fields_enable_title">Písmo kolonek</string>
|
<string name="monospace_font_fields_enable_title">Písmo kolonek</string>
|
||||||
<string name="monospace_font_fields_enable_summary">Čitelnost znaků v kolonkách můžete přizpůsobit změnou písma</string>
|
<string name="monospace_font_fields_enable_summary">Čitelnost znaků v kolonkách můžete přizpůsobit změnou písma</string>
|
||||||
<string name="allow_copy_password_title">Důvěřovat schránce</string>
|
<string name="allow_copy_password_title">Důvěřovat schránce</string>
|
||||||
@@ -283,9 +280,9 @@
|
|||||||
<string name="keyboard_setting_label">Magikeyboard nastavení</string>
|
<string name="keyboard_setting_label">Magikeyboard nastavení</string>
|
||||||
<string name="keyboard_entry_category">Záznam</string>
|
<string name="keyboard_entry_category">Záznam</string>
|
||||||
<string name="keyboard_entry_timeout_title">Časový limit</string>
|
<string name="keyboard_entry_timeout_title">Časový limit</string>
|
||||||
<string name="keyboard_entry_timeout_summary">Doba uchování položky v Magikeyboardu</string>
|
<string name="keyboard_entry_timeout_summary">Doba uchování položky v klávesnici</string>
|
||||||
<string name="keyboard_notification_entry_title">Informace o oznámení</string>
|
<string name="keyboard_notification_entry_title">Informace o oznámení</string>
|
||||||
<string name="keyboard_notification_entry_summary">Zobrazit oznámení, když je položka dostupná</string>
|
<string name="keyboard_notification_entry_summary">Zobrazit oznámení, když je záznam dostupný</string>
|
||||||
<string name="keyboard_notification_entry_content_title_text">Záznam</string>
|
<string name="keyboard_notification_entry_content_title_text">Záznam</string>
|
||||||
<string name="keyboard_notification_entry_content_title">%1$s dostupné v Magikeyboardu</string>
|
<string name="keyboard_notification_entry_content_title">%1$s dostupné v Magikeyboardu</string>
|
||||||
<string name="keyboard_notification_entry_content_text">%1$s</string>
|
<string name="keyboard_notification_entry_content_text">%1$s</string>
|
||||||
@@ -299,7 +296,7 @@
|
|||||||
<string name="selection_mode">Režim výběru</string>
|
<string name="selection_mode">Režim výběru</string>
|
||||||
<string name="do_not_kill_app">Nezavírejte aplikaci…</string>
|
<string name="do_not_kill_app">Nezavírejte aplikaci…</string>
|
||||||
<string name="lock_database_back_root_title">K uzamknutí stiskněte Zpět</string>
|
<string name="lock_database_back_root_title">K uzamknutí stiskněte Zpět</string>
|
||||||
<string name="lock_database_back_root_summary">Zamknout obrazovku, pokud uživatel stiskne tlačítko Zpět v hlavním panelu</string>
|
<string name="lock_database_back_root_summary">Stiskněte „Zpět“ pro uzamčení databáze, pokud se nacházíte na hlavní obrazovce databáze</string>
|
||||||
<string name="clear_clipboard_notification_title">Vymazat při ukončení</string>
|
<string name="clear_clipboard_notification_title">Vymazat při ukončení</string>
|
||||||
<string name="clear_clipboard_notification_summary">Uzamknout databázi, jakmile trvání schránky vyprší nebo po uzavření oznámení</string>
|
<string name="clear_clipboard_notification_summary">Uzamknout databázi, jakmile trvání schránky vyprší nebo po uzavření oznámení</string>
|
||||||
<string name="recycle_bin">Koš</string>
|
<string name="recycle_bin">Koš</string>
|
||||||
@@ -524,7 +521,7 @@
|
|||||||
<string name="unit_mebibyte">MiB</string>
|
<string name="unit_mebibyte">MiB</string>
|
||||||
<string name="unit_kibibyte">KiB</string>
|
<string name="unit_kibibyte">KiB</string>
|
||||||
<string name="unit_byte">B</string>
|
<string name="unit_byte">B</string>
|
||||||
<string name="error_otp_type">Nemohu rozpoznat existující typ OTP v této formě, jeho validace patrně nebude generovat správný token.</string>
|
<string name="error_otp_type">Nemohu rozpoznat existující typ OTP v této formě a jeho validace patrně nebude generovat správný token.</string>
|
||||||
<string name="download_canceled">Zrušeno!</string>
|
<string name="download_canceled">Zrušeno!</string>
|
||||||
<string name="icon_section_custom">Vlastní</string>
|
<string name="icon_section_custom">Vlastní</string>
|
||||||
<string name="icon_section_standard">Standardní</string>
|
<string name="icon_section_standard">Standardní</string>
|
||||||
@@ -679,7 +676,7 @@
|
|||||||
<string name="ask">Zeptat se</string>
|
<string name="ask">Zeptat se</string>
|
||||||
<string name="configure">Nastavit</string>
|
<string name="configure">Nastavit</string>
|
||||||
<string name="unlock_and_link_biometric">Propojení s odemykáním zařízení</string>
|
<string name="unlock_and_link_biometric">Propojení s odemykáním zařízení</string>
|
||||||
<string name="menu_appearance_settings_summary">Motivy, barvy, atributy</string>
|
<string name="menu_appearance_settings_summary">Motivy, barvy, ikony, písma, atributy</string>
|
||||||
<string name="unlock">Odemknout</string>
|
<string name="unlock">Odemknout</string>
|
||||||
<string name="education_validate_entry_title">Ověřit vstup</string>
|
<string name="education_validate_entry_title">Ověřit vstup</string>
|
||||||
<string name="education_validate_entry_summary">Nezapomeňte ověřit svůj vstup a uložit databázi.
|
<string name="education_validate_entry_summary">Nezapomeňte ověřit svůj vstup a uložit databázi.
|
||||||
@@ -705,4 +702,45 @@
|
|||||||
<string name="hide_templates_title">Skrýt šablony</string>
|
<string name="hide_templates_title">Skrýt šablony</string>
|
||||||
<string name="hide_templates_summary">Šablony nejsou zobrazeny</string>
|
<string name="hide_templates_summary">Šablony nejsou zobrazeny</string>
|
||||||
<string name="error_otp_secret_length">Tajný klíč musí obsahovat alespoň %1$d znaků.</string>
|
<string name="error_otp_secret_length">Tajný klíč musí obsahovat alespoň %1$d znaků.</string>
|
||||||
|
<string name="entry_application_id">ID aplikace</string>
|
||||||
|
<string name="warning_overwrite_data_title">Přepsat existující data?</string>
|
||||||
|
<string name="warning_overwrite_data_description">Tato akce nahradí existující data v položce, pokud je povolena historie, můžete původní data získat.</string>
|
||||||
|
<string name="credential_provider">Poskytovatel údajů</string>
|
||||||
|
<string name="passkeys">Přístupové klíče</string>
|
||||||
|
<string name="passkeys_explanation_summary">Nastavte přístupové klíče pro rychlé a bezpečné přihlášení bez hesla</string>
|
||||||
|
<string name="passkeys_preference_title">Nastavení přístupových klíčů</string>
|
||||||
|
<string name="passkeys_close_database_title">Zavřít databázi</string>
|
||||||
|
<string name="passkeys_close_database_summary">Zavřít databázi po výběru přístupového klíče</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">Privilegované aplikace</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">Spravovat prohlížeče ve vlastním seznamu privilegovaných aplikací</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">VAROVÁNÍ: Privilegovaná aplikace funguje jako brána k získání původu autentifikace. Ujistěte se, že se jedná o důvěryhodnou aplikaci, pro zabránění bezpečnostním problémům.</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">Aplikace nerozpoznána</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">%1$s se pokouší provést akci přísupového klíče.\n\nChcete ji přidat do seznamu privilegovaných aplikací?</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">Chybějící podpis</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">VAROVÁNÍ: Přístupový klíč byl vytvořen z jiného klienta nebo byl vymazán podpis. Ujistěte se, že aplikace, kterou chcete autentifikovat, je součástí stejné služby a že je legitimní pro zabránění bezpečnostním problémům.\nPokud je aplikace prohlížeč, nepřidávejte její podpis do záznamu, ale do seznamu privilegovaných aplikací v nastavení.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">%1$s je nerozpoznaná a pokouší se o autentifikaci s existujícím přístupovým klíčem.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">Přidat podpis aplikace do záznamu přístupového klíče?</string>
|
||||||
|
<string name="passkeys_auto_select_title">Automatický výběr</string>
|
||||||
|
<string name="passkeys_auto_select_summary">Automaticky vybrat, pokud existuje pouze jeden záznam a aplikace je otevřená, pouze pokud je žádající aplikace kompatibilní</string>
|
||||||
|
<string name="passkeys_backup_eligibility_title">Možnost zálohy</string>
|
||||||
|
<string name="passkeys_backup_eligibility_summary">Při vytváření určit, zda je povoleno zálohovat zdroj ověřovacích údajů veřejného klíče</string>
|
||||||
|
<string name="passkeys_backup_state_title">Stav zálohy</string>
|
||||||
|
<string name="passkeys_backup_state_summary">Uvést, že přihlašovací údaje jsou zálohovány a chráněny proti ztrátě jednoho zařízení</string>
|
||||||
|
<string name="credential_provider_service_subtitle">Přístupové klíče, poskytovatel automatického vyplnění</string>
|
||||||
|
<string name="passkey">Přístupový klíč</string>
|
||||||
|
<string name="passkey_service_name">Poskytovatel údajů KeePassDX</string>
|
||||||
|
<string name="passkey_creation_description">Uložit přístupový klíč do nového záznamu</string>
|
||||||
|
<string name="passkey_update_description">Aktualizovat přístupový klíč v %1$s</string>
|
||||||
|
<string name="passkey_selection_username">Nenalezeny žádné přístupové klíče</string>
|
||||||
|
<string name="passkey_selection_description">Vyberte existující přístupový klíč</string>
|
||||||
|
<string name="passkey_database_username">Databáze KeePassDX</string>
|
||||||
|
<string name="passkey_locked_database_description">Vyberte k odemčení</string>
|
||||||
|
<string name="passkey_username">Uživatelské jméno přístupového klíče</string>
|
||||||
|
<string name="passkey_private_key">Soukromý klíč přístupového klíče</string>
|
||||||
|
<string name="passkey_credential_id">ID údaje přístupového klíče</string>
|
||||||
|
<string name="passkey_user_handle">Uživatelská adresa přístupového klíče</string>
|
||||||
|
<string name="passkey_relying_party">Strana předávající přístupový klíč</string>
|
||||||
|
<string name="passkey_backup_eligibility">Způsobilost přístupového klíče k zálohování</string>
|
||||||
|
<string name="passkey_backup_state">Stav zálohy přístupového klíče</string>
|
||||||
|
<string name="error_passkey_result">Nepodařilo se vrátit přístupový klíč</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -89,8 +89,6 @@
|
|||||||
<string name="list_size_summary">Tekststørrelse i elementlisten</string>
|
<string name="list_size_summary">Tekststørrelse i elementlisten</string>
|
||||||
<string name="loading_database">Indlæser database…</string>
|
<string name="loading_database">Indlæser database…</string>
|
||||||
<string name="lowercase">Små bogstaver</string>
|
<string name="lowercase">Små bogstaver</string>
|
||||||
<string name="hide_password_title">Skjul adgangskoder</string>
|
|
||||||
<string name="hide_password_summary">Skjul adgangskoder (***) som standard</string>
|
|
||||||
<string name="about">Om</string>
|
<string name="about">Om</string>
|
||||||
<string name="menu_change_key_settings">Skift hovednøgle</string>
|
<string name="menu_change_key_settings">Skift hovednøgle</string>
|
||||||
<string name="settings">Indstillinger</string>
|
<string name="settings">Indstillinger</string>
|
||||||
@@ -98,7 +96,6 @@
|
|||||||
<string name="menu_delete">Slet</string>
|
<string name="menu_delete">Slet</string>
|
||||||
<string name="menu_donate">Donér</string>
|
<string name="menu_donate">Donér</string>
|
||||||
<string name="menu_edit">Rediger</string>
|
<string name="menu_edit">Rediger</string>
|
||||||
<string name="menu_hide_password">Skjul adgangskode</string>
|
|
||||||
<string name="menu_lock">Lås database</string>
|
<string name="menu_lock">Lås database</string>
|
||||||
<string name="menu_open">Åbn</string>
|
<string name="menu_open">Åbn</string>
|
||||||
<string name="menu_search">Søg</string>
|
<string name="menu_search">Søg</string>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
Translations from David Ramiro
|
Translations from David Ramiro
|
||||||
--><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
--><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||||
<string name="contact">Kontakt</string>
|
<string name="contact">Kontakt</string>
|
||||||
<string name="contribution">Beiträge</string>
|
<string name="contribution">Beitrag</string>
|
||||||
<string name="feedback">Feedback</string>
|
<string name="feedback">Feedback</string>
|
||||||
<string name="homepage">Webseite</string>
|
<string name="homepage">Webseite</string>
|
||||||
<string name="about_description">Android-Implementierung des Passwortmanagers KeePass.</string>
|
<string name="about_description">Android-Implementierung des Passwortmanagers KeePass.</string>
|
||||||
@@ -99,8 +99,6 @@
|
|||||||
<string name="list_size_summary">Schriftgröße der Listenelemente</string>
|
<string name="list_size_summary">Schriftgröße der Listenelemente</string>
|
||||||
<string name="loading_database">Datenbank wird geladen …</string>
|
<string name="loading_database">Datenbank wird geladen …</string>
|
||||||
<string name="lowercase">Kleinbuchstaben</string>
|
<string name="lowercase">Kleinbuchstaben</string>
|
||||||
<string name="hide_password_title">Passwörter verbergen</string>
|
|
||||||
<string name="hide_password_summary">Passwörter mit (***) maskieren</string>
|
|
||||||
<string name="about">Über</string>
|
<string name="about">Über</string>
|
||||||
<string name="menu_change_key_settings">Hauptschlüssel ändern</string>
|
<string name="menu_change_key_settings">Hauptschlüssel ändern</string>
|
||||||
<string name="settings">Einstellungen</string>
|
<string name="settings">Einstellungen</string>
|
||||||
@@ -108,7 +106,6 @@
|
|||||||
<string name="menu_delete">Löschen</string>
|
<string name="menu_delete">Löschen</string>
|
||||||
<string name="menu_donate">Spenden</string>
|
<string name="menu_donate">Spenden</string>
|
||||||
<string name="menu_edit">Bearbeiten</string>
|
<string name="menu_edit">Bearbeiten</string>
|
||||||
<string name="menu_hide_password">Passwort verbergen</string>
|
|
||||||
<string name="menu_lock">Datenbank sperren</string>
|
<string name="menu_lock">Datenbank sperren</string>
|
||||||
<string name="menu_open">Öffnen</string>
|
<string name="menu_open">Öffnen</string>
|
||||||
<string name="menu_search">Suche</string>
|
<string name="menu_search">Suche</string>
|
||||||
@@ -153,7 +150,7 @@
|
|||||||
<string name="menu_appearance_settings">Erscheinungsbild</string>
|
<string name="menu_appearance_settings">Erscheinungsbild</string>
|
||||||
<string name="password_size_title">Generierte Passwortlänge</string>
|
<string name="password_size_title">Generierte Passwortlänge</string>
|
||||||
<string name="password_size_summary">Legt die Standardlänge des generierten Passworts fest</string>
|
<string name="password_size_summary">Legt die Standardlänge des generierten Passworts fest</string>
|
||||||
<string name="clipboard_notifications_title">Kopier-Benachrichtigung</string>
|
<string name="clipboard_notifications_title">Kopierbenachrichtigung</string>
|
||||||
<string name="clipboard_notifications_summary">Benachrichtigung anzeigen, um beim Betrachten eines Eintrags Felder kopieren zu können</string>
|
<string name="clipboard_notifications_summary">Benachrichtigung anzeigen, um beim Betrachten eines Eintrags Felder kopieren zu können</string>
|
||||||
<string name="lock_database_screen_off_title">Bildschirmsperre</string>
|
<string name="lock_database_screen_off_title">Bildschirmsperre</string>
|
||||||
<string name="lock_database_screen_off_summary">Datenbank wenige Sekunden nach Bildschirmabschaltung sperren</string>
|
<string name="lock_database_screen_off_summary">Datenbank wenige Sekunden nach Bildschirmabschaltung sperren</string>
|
||||||
@@ -197,7 +194,7 @@
|
|||||||
<string name="autofill">Automatisches Ausfüllen</string>
|
<string name="autofill">Automatisches Ausfüllen</string>
|
||||||
<string name="autofill_sign_in_prompt">Mit KeePassDX anmelden</string>
|
<string name="autofill_sign_in_prompt">Mit KeePassDX anmelden</string>
|
||||||
<string name="set_credential_provider_service_title">Standard-Autofill-Service festlegen</string>
|
<string name="set_credential_provider_service_title">Standard-Autofill-Service festlegen</string>
|
||||||
<string name="autofill_explanation_summary">Automatisches Ausfüllen aktivieren, um Formulare in anderen Apps schnell auszufüllen</string>
|
<string name="autofill_explanation_summary">Automatisches Ausfüllen konfigurieren, um Formulare in anderen Apps schnell 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="clipboard">Zwischenablage</string>
|
||||||
<string name="biometric_delete_all_key_title">Verschlüsselungsschlüssel löschen</string>
|
<string name="biometric_delete_all_key_title">Verschlüsselungsschlüssel löschen</string>
|
||||||
@@ -223,8 +220,8 @@
|
|||||||
<string name="reset_education_screens_summary">Alle Hilfsinfos nochmal anzeigen</string>
|
<string name="reset_education_screens_summary">Alle Hilfsinfos nochmal anzeigen</string>
|
||||||
<string name="reset_education_screens_text">Hilfeanzeige zurückgesetzt</string>
|
<string name="reset_education_screens_text">Hilfeanzeige zurückgesetzt</string>
|
||||||
<string name="education_create_database_title">Datenbankdatei erstellen</string>
|
<string name="education_create_database_title">Datenbankdatei erstellen</string>
|
||||||
<string name="education_create_database_summary">Erste Datei zur Passwortverwaltung erstellen.</string>
|
<string name="education_create_database_summary">Eine erste Datei zur Passwortverwaltung erstellen.</string>
|
||||||
<string name="education_select_database_title">Existierende Datenbank öffnen</string>
|
<string name="education_select_database_title">Vorhandene Datenbank öffnen</string>
|
||||||
<string name="education_select_database_summary">Öffnet über den Dateimanager eine früher erstellte Datenbankdatei, um sie weiterzuverwenden.</string>
|
<string name="education_select_database_summary">Öffnet über den Dateimanager eine früher erstellte Datenbankdatei, um sie weiterzuverwenden.</string>
|
||||||
<string name="education_new_node_title">Datenbankelemente hinzufügen</string>
|
<string name="education_new_node_title">Datenbankelemente hinzufügen</string>
|
||||||
<string name="education_new_node_summary">Einträge helfen, digitale Konten zu verwalten.
|
<string name="education_new_node_summary">Einträge helfen, digitale Konten zu verwalten.
|
||||||
@@ -257,7 +254,7 @@
|
|||||||
<string name="html_text_dev_feature_buy_pro">Durch den Kauf der <strong>Pro-Version</strong>,</string>
|
<string name="html_text_dev_feature_buy_pro">Durch den Kauf der <strong>Pro-Version</strong>,</string>
|
||||||
<string name="html_text_dev_feature_contibute">Durch deinen <strong>Beitrag</strong>,</string>
|
<string name="html_text_dev_feature_contibute">Durch deinen <strong>Beitrag</strong>,</string>
|
||||||
<string name="html_text_dev_feature_encourage">ermutigst du die Entwickler, <strong>neue Funktionen</strong> einzuführen und gemäß deinen Anmerkungen <strong>Fehler zu beheben</strong>.</string>
|
<string name="html_text_dev_feature_encourage">ermutigst du die Entwickler, <strong>neue Funktionen</strong> einzuführen und gemäß deinen Anmerkungen <strong>Fehler zu beheben</strong>.</string>
|
||||||
<string name="html_text_dev_feature_thanks">Vielen Dank für deine Unterstützung.</string>
|
<string name="html_text_dev_feature_thanks">Vielen Dank für die Unterstützung.</string>
|
||||||
<string name="html_text_dev_feature_work_hard">Wir bemühen uns, diese Funktion bald zu veröffentlichen.</string>
|
<string name="html_text_dev_feature_work_hard">Wir bemühen uns, diese Funktion bald zu veröffentlichen.</string>
|
||||||
<string name="html_text_dev_feature_upgrade">Denke daran, die App durch die Installation neuer Versionen auf dem aktuellsten Stand zu halten.</string>
|
<string name="html_text_dev_feature_upgrade">Denke daran, die App durch die Installation neuer Versionen auf dem aktuellsten Stand zu halten.</string>
|
||||||
<string name="download">Download</string>
|
<string name="download">Download</string>
|
||||||
@@ -312,7 +309,7 @@
|
|||||||
<string name="hide_broken_locations_title">Defekte Datenbankverknüpfungen ausblenden</string>
|
<string name="hide_broken_locations_title">Defekte Datenbankverknüpfungen ausblenden</string>
|
||||||
<string name="hide_broken_locations_summary">Defekte Verknüpfungen in der Liste der zuletzt verwendeten Datenbanken ausblenden</string>
|
<string name="hide_broken_locations_summary">Defekte 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 die Taste „Zurück“ gedrückt wird</string>
|
<string name="lock_database_back_root_summary">„Zurück“ drücken, um die Datenbank zu sperren, wenn man sich auf dem Hauptbildschirm der Datenbank befindet</string>
|
||||||
<string name="clear_clipboard_notification_title">Beim Schließen löschen</string>
|
<string name="clear_clipboard_notification_title">Beim Schließen löschen</string>
|
||||||
<string name="recycle_bin">Papierkorb</string>
|
<string name="recycle_bin">Papierkorb</string>
|
||||||
<string name="keyboard_selection_entry_title">Eintragsauswahl</string>
|
<string name="keyboard_selection_entry_title">Eintragsauswahl</string>
|
||||||
@@ -498,7 +495,7 @@
|
|||||||
<string name="keyboard_save_search_info_title">Geteilte Informationen speichern</string>
|
<string name="keyboard_save_search_info_title">Geteilte Informationen speichern</string>
|
||||||
<string name="warning_empty_recycle_bin">Alle Knoten unwiderruflich aus dem Papierkorb löschen\?</string>
|
<string name="warning_empty_recycle_bin">Alle Knoten unwiderruflich aus dem Papierkorb löschen\?</string>
|
||||||
<string name="error_field_name_already_exists">Der Feldname existiert bereits.</string>
|
<string name="error_field_name_already_exists">Der Feldname existiert bereits.</string>
|
||||||
<string name="device_unlock_prompt_store_credential_message">Du musst dich weiterhin an deinen Hauptschlüssel erinnern, wenn du die Geräteentsperrung verwendest.</string>
|
<string name="device_unlock_prompt_store_credential_message">Falls die Geräteentsperrung verwendet wird, ist es immer noch notwendig, sich die Hauptzugangsdaten für den Tresor zu merken.</string>
|
||||||
<string name="menu_keystore_remove_key">Schlüssel für Geräteentsperrung löschen</string>
|
<string name="menu_keystore_remove_key">Schlüssel für Geräteentsperrung löschen</string>
|
||||||
<string name="device_unlock_prompt_store_credential_title">Verknüpfung mit Geräteentsperrung</string>
|
<string name="device_unlock_prompt_store_credential_title">Verknüpfung mit Geräteentsperrung</string>
|
||||||
<string name="education_device_unlock_summary">Eigenes Passwort mit Biometrie- oder Geräteanmeldedaten verknüpfen, um die Datenbank schnell zu entsperren.</string>
|
<string name="education_device_unlock_summary">Eigenes Passwort mit Biometrie- oder Geräteanmeldedaten verknüpfen, um die Datenbank schnell zu entsperren.</string>
|
||||||
@@ -508,7 +505,7 @@
|
|||||||
<string name="temp_device_unlock_timeout_title">Ablauf der Geräteentsperrung</string>
|
<string name="temp_device_unlock_timeout_title">Ablauf der Geräteentsperrung</string>
|
||||||
<string name="temp_device_unlock_enable_summary">Bei Nutzung der Geräteentsperrung keine verschlüsselten Inhalte speichern</string>
|
<string name="temp_device_unlock_enable_summary">Bei Nutzung der Geräteentsperrung keine verschlüsselten Inhalte speichern</string>
|
||||||
<string name="temp_device_unlock_enable_title">Zeitlich begrenzte Geräteentsperrung</string>
|
<string name="temp_device_unlock_enable_title">Zeitlich begrenzte Geräteentsperrung</string>
|
||||||
<string name="device_credential_unlock_enable_summary">Ermöglicht das Öffnen der Datenbank mit deinen Geräteanmeldedaten</string>
|
<string name="device_credential_unlock_enable_summary">Ermöglicht das Öffnen der Datenbank mit den persönlichen Geräteanmeldedaten</string>
|
||||||
<string name="device_unlock_tap_delete">Drücken, um alle Geräteentsperrschlüssel zu löschen</string>
|
<string name="device_unlock_tap_delete">Drücken, um alle Geräteentsperrschlüssel zu löschen</string>
|
||||||
<string name="content">Inhalt</string>
|
<string name="content">Inhalt</string>
|
||||||
<string name="device_unlock_prompt_extract_credential_title">Datenbank mit Geräteentsperrdaten öffnen</string>
|
<string name="device_unlock_prompt_extract_credential_title">Datenbank mit Geräteentsperrdaten öffnen</string>
|
||||||
@@ -536,7 +533,7 @@
|
|||||||
<string name="error_upload_file">Beim Hochladen der Datei ist ein Fehler aufgetreten.</string>
|
<string name="error_upload_file">Beim Hochladen der Datei ist ein Fehler aufgetreten.</string>
|
||||||
<string name="import_app_properties_title">App-Einstellungen importieren</string>
|
<string name="import_app_properties_title">App-Einstellungen importieren</string>
|
||||||
<string name="error_start_database_action">Beim Ausführen einer Aktion in der Datenbank ist ein Fehler aufgetreten.</string>
|
<string name="error_start_database_action">Beim Ausführen einer Aktion in der Datenbank ist ein Fehler aufgetreten.</string>
|
||||||
<string name="error_otp_type">Der vorhandene OTP-Typ wird von diesem Formular nicht erkannt, seine Validierung kann Token möglicherweise nicht mehr korrekt erzeugen.</string>
|
<string name="error_otp_type">Der vorhandene OTP-Typ wird von diesem Formular nicht erkannt, und seine Validierung kann Token möglicherweise nicht mehr korrekt erzeugen.</string>
|
||||||
<string name="content_description_otp_information">Informationen zu Einmalpasswörtern</string>
|
<string name="content_description_otp_information">Informationen zu Einmalpasswörtern</string>
|
||||||
<string name="warning_database_revoked">Auf die Datei kann nicht zugegriffen werden. Bitte die Datenbank schließen und von ihrem Speicherort aus erneut öffnen.</string>
|
<string name="warning_database_revoked">Auf die Datei kann nicht zugegriffen werden. Bitte die Datenbank schließen und von ihrem Speicherort aus erneut öffnen.</string>
|
||||||
<string name="error_export_app_properties">Fehler beim Exportieren der App-Einstellungen.</string>
|
<string name="error_export_app_properties">Fehler beim Exportieren der App-Einstellungen.</string>
|
||||||
@@ -682,7 +679,7 @@
|
|||||||
<string name="ask">Fragen</string>
|
<string name="ask">Fragen</string>
|
||||||
<string name="later">Später</string>
|
<string name="later">Später</string>
|
||||||
<string name="unlock_and_link_biometric">Geräteentsperrverknüpfung</string>
|
<string name="unlock_and_link_biometric">Geräteentsperrverknüpfung</string>
|
||||||
<string name="menu_appearance_settings_summary">Design, Farben, Attribute</string>
|
<string name="menu_appearance_settings_summary">Design, Farben, Symbole, Schriftarten, Attribute</string>
|
||||||
<string name="warning_database_notification_permission">Die Benachrichtigungsberechtigung ermöglicht es, den Status der Datenbank anzuzeigen und sie mit einer leicht zugänglichen Taste zu sperren.
|
<string name="warning_database_notification_permission">Die Benachrichtigungsberechtigung ermöglicht es, den Status der Datenbank anzuzeigen und sie mit einer leicht zugänglichen Taste zu sperren.
|
||||||
\n
|
\n
|
||||||
\nWird diese Berechtigung nicht aktiviert, ist die im Hintergrund geöffnete Datenbank nicht sichtbar, wenn eine Anwendung im Vordergrund läuft.</string>
|
\nWird diese Berechtigung nicht aktiviert, ist die im Hintergrund geöffnete Datenbank nicht sichtbar, wenn eine Anwendung im Vordergrund läuft.</string>
|
||||||
@@ -704,4 +701,45 @@
|
|||||||
<string name="recursive_number_entries_title">Rekursive Anzahl der Einträge</string>
|
<string name="recursive_number_entries_title">Rekursive Anzahl der Einträge</string>
|
||||||
<string name="generate_keyfile">Schlüsseldatei generieren</string>
|
<string name="generate_keyfile">Schlüsseldatei generieren</string>
|
||||||
<string name="error_otp_secret_length">Geheimschlüssel muss mindestens %1$d Zeichen lang sein.</string>
|
<string name="error_otp_secret_length">Geheimschlüssel muss mindestens %1$d Zeichen lang sein.</string>
|
||||||
|
<string name="entry_application_id">App-ID</string>
|
||||||
|
<string name="warning_overwrite_data_title">Bestehende Daten überschreiben?</string>
|
||||||
|
<string name="warning_overwrite_data_description">Diese Aktion ersetzt die bestehenden Daten im Eintrag. Die alten Daten können wiederhergestellt werden, wenn der Verlauf aktiviert ist.</string>
|
||||||
|
<string name="passkeys">Passkeys</string>
|
||||||
|
<string name="passkeys_preference_title">Passkeys-Einstellungen</string>
|
||||||
|
<string name="passkeys_close_database_title">Datenbank schließen</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">Vertrauliche Apps</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">Signatur fehlt</string>
|
||||||
|
<string name="passkeys_auto_select_title">Automatische Auswahl</string>
|
||||||
|
<string name="passkeys_backup_eligibility_title">Backup-Erlaubnis</string>
|
||||||
|
<string name="passkeys_backup_state_title">Backup-Status</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">App nicht erkannt</string>
|
||||||
|
<string name="passkey">Passkey</string>
|
||||||
|
<string name="passkey_selection_username">Kein Passkey gefunden</string>
|
||||||
|
<string name="passkey_creation_description">Passkey in neuem Eintrag speichern</string>
|
||||||
|
<string name="passkey_update_description">Passkey in %1$s aktualisieren</string>
|
||||||
|
<string name="passkey_selection_description">Vorhandenen Passkey auswählen</string>
|
||||||
|
<string name="passkey_database_username">KeePassDX-Datenbank</string>
|
||||||
|
<string name="passkey_username">Passkey-Benutzername</string>
|
||||||
|
<string name="passkey_backup_state">Passkey-Backup-Status</string>
|
||||||
|
<string name="passkey_backup_eligibility">Passkey-Backup-Erlaubnis</string>
|
||||||
|
<string name="passkeys_close_database_summary">Datenbank nach der Passwortauswahl schließen</string>
|
||||||
|
<string name="credential_provider">Anmeldeinformationsanbieter</string>
|
||||||
|
<string name="passkeys_explanation_summary">Passkeys für eine schnelle und sichere Anmeldung ohne Passwort konfigurieren</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">Browser in der benutzerdefinierten Liste privilegierter Apps verwalten</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">WARNUNG: Eine privilegierte App fungiert als Gateway, um die Herkunft einer Authentifizierung abzurufen. Zur Vermeidung von Sicherheitsproblemen ist ihre Legitimität sicherzustellen.</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">%1$s versucht, eine Passkey-Aktion auszuführen.\n\nZur Liste der privilegierten Apps hinzufügen?</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">WARNUNG: Der Passkey wurde von einem anderen Client erstellt oder die Signatur wurde gelöscht. Um Sicherheitsrisiken zu vermeiden, sollte die zu authentifizierende App zum selben Dienst gehören und legitimiert sein.\nWenn es sich bei der App um einen Browser handelt, dann die Signatur nicht zum Eintrag, sondern zur Liste der privilegierten Apps in den Einstellungen hinzufügen.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">%1$s wird nicht erkannt und versucht, sich mit einem vorhandenen Passkey zu authentifizieren.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">App-Signatur zum Passkey-Eintrag hinzufügen?</string>
|
||||||
|
<string name="passkeys_auto_select_summary">Automatische Auswahl, wenn nur ein Eintrag vorhanden und die Datenbank geöffnet ist, und nur, wenn die anfragende App kompatibel ist</string>
|
||||||
|
<string name="passkeys_backup_eligibility_summary">Bei der Erstellung festlegen, ob die Quelle für öffentliche Schlüssel-Anmeldeinformationen gesichert werden darf</string>
|
||||||
|
<string name="passkeys_backup_state_summary">Anzeigen, dass Anmeldedaten gesichert und gegen den Verlust eines einzelnen Geräts geschützt sind</string>
|
||||||
|
<string name="credential_provider_service_subtitle">Passkeys, Anbieter für das automatische Ausfüllen von Anmeldedaten</string>
|
||||||
|
<string name="passkey_service_name">KeePassDX-Anmeldeungsdatenanbieter</string>
|
||||||
|
<string name="passkey_locked_database_description">Zum Entsperren auswählen</string>
|
||||||
|
<string name="passkey_private_key">Passkey Privater Schlüssel</string>
|
||||||
|
<string name="passkey_credential_id">Passkey-Anmeldeinformation</string>
|
||||||
|
<string name="error_passkey_result">Passkey kann nicht zurückgegeben werden</string>
|
||||||
|
<string name="passkey_user_handle">Passkey-Benutzerkennung</string>
|
||||||
|
<string name="passkey_relying_party">Passkey-Vertrauensstelle</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -92,8 +92,6 @@
|
|||||||
<string name="list_size_summary">Μέγεθος κειμένου στη λίστα στοιχείων</string>
|
<string name="list_size_summary">Μέγεθος κειμένου στη λίστα στοιχείων</string>
|
||||||
<string name="loading_database">Φόρτωση βάσης δεδομένων…</string>
|
<string name="loading_database">Φόρτωση βάσης δεδομένων…</string>
|
||||||
<string name="lowercase">Μικρά</string>
|
<string name="lowercase">Μικρά</string>
|
||||||
<string name="hide_password_title">Απόκρυψη κωδικών πρόσβασης</string>
|
|
||||||
<string name="hide_password_summary">Μάσκα κωδικούς πρόσβασης (***) από προεπιλογή</string>
|
|
||||||
<string name="about">Σχετικά με</string>
|
<string name="about">Σχετικά με</string>
|
||||||
<string name="menu_change_key_settings">Αλλαγή Κύριου Κλειδιού</string>
|
<string name="menu_change_key_settings">Αλλαγή Κύριου Κλειδιού</string>
|
||||||
<string name="settings">Ρυθμίσεις</string>
|
<string name="settings">Ρυθμίσεις</string>
|
||||||
@@ -101,7 +99,6 @@
|
|||||||
<string name="menu_delete">Διαγραφή</string>
|
<string name="menu_delete">Διαγραφή</string>
|
||||||
<string name="menu_donate">Δωρεά</string>
|
<string name="menu_donate">Δωρεά</string>
|
||||||
<string name="menu_edit">Επεξεργασία</string>
|
<string name="menu_edit">Επεξεργασία</string>
|
||||||
<string name="menu_hide_password">Απόκρυψη κωδικού</string>
|
|
||||||
<string name="menu_lock">Κλείδωμα βάσης δεδομένων</string>
|
<string name="menu_lock">Κλείδωμα βάσης δεδομένων</string>
|
||||||
<string name="menu_open">Άνοιγμα</string>
|
<string name="menu_open">Άνοιγμα</string>
|
||||||
<string name="menu_search">Αναζήτηση</string>
|
<string name="menu_search">Αναζήτηση</string>
|
||||||
|
|||||||
@@ -219,8 +219,6 @@
|
|||||||
<string name="invalid_algorithm">Wrong algorithm.</string>
|
<string name="invalid_algorithm">Wrong algorithm.</string>
|
||||||
<string name="invalid_db_same_uuid">%1$s with the same UUID %2$s already exists.</string>
|
<string name="invalid_db_same_uuid">%1$s with the same UUID %2$s already exists.</string>
|
||||||
<string name="keyfile_is_empty">The keyfile is empty.</string>
|
<string name="keyfile_is_empty">The keyfile is empty.</string>
|
||||||
<string name="hide_password_title">Hide passwords</string>
|
|
||||||
<string name="list_entries_show_username_title">Show usernames</string>
|
|
||||||
<string name="error_otp_period">Period must be between %1$d and %2$d seconds.</string>
|
<string name="error_otp_period">Period must be between %1$d and %2$d seconds.</string>
|
||||||
<string name="error_string_type">This text does not match the requested item.</string>
|
<string name="error_string_type">This text does not match the requested item.</string>
|
||||||
<string name="error_registration_read_only">Saving a new item is not allowed in a read-only database.</string>
|
<string name="error_registration_read_only">Saving a new item is not allowed in a read-only database.</string>
|
||||||
@@ -231,7 +229,6 @@
|
|||||||
<string name="error_file_to_big">The file you are trying to upload is too big.</string>
|
<string name="error_file_to_big">The file you are trying to upload is too big.</string>
|
||||||
<string name="error_upload_file">An error occurred while uploading the file data.</string>
|
<string name="error_upload_file">An error occurred while uploading the file data.</string>
|
||||||
<string name="error_location_unknown">Database location is unknown, database action cannot be performed.</string>
|
<string name="error_location_unknown">Database location is unknown, database action cannot be performed.</string>
|
||||||
<string name="hide_password_summary">Mask passwords (***) by default</string>
|
|
||||||
<string name="list_entries_show_username_summary">Displays usernames in entry lists</string>
|
<string name="list_entries_show_username_summary">Displays usernames in entry lists</string>
|
||||||
<string name="list_groups_show_number_entries_title">Show number of entries</string>
|
<string name="list_groups_show_number_entries_title">Show number of entries</string>
|
||||||
<string name="show_otp_token_summary">Displays OTP tokens in the list of entries</string>
|
<string name="show_otp_token_summary">Displays OTP tokens in the list of entries</string>
|
||||||
|
|||||||
@@ -41,7 +41,6 @@
|
|||||||
<string name="menu_reload_database">Reŝargi datumbazon</string>
|
<string name="menu_reload_database">Reŝargi datumbazon</string>
|
||||||
<string name="menu_save_database">Konservi datumbazon</string>
|
<string name="menu_save_database">Konservi datumbazon</string>
|
||||||
<string name="menu_lock">Ŝlosi datumbazon</string>
|
<string name="menu_lock">Ŝlosi datumbazon</string>
|
||||||
<string name="menu_hide_password">Kaŝi pasvorton</string>
|
|
||||||
<string name="menu_cancel">Nuligi</string>
|
<string name="menu_cancel">Nuligi</string>
|
||||||
<string name="menu_delete">Forigi</string>
|
<string name="menu_delete">Forigi</string>
|
||||||
<string name="menu_paste">Algui</string>
|
<string name="menu_paste">Algui</string>
|
||||||
@@ -56,8 +55,6 @@
|
|||||||
<string name="settings">Agordoj</string>
|
<string name="settings">Agordoj</string>
|
||||||
<string name="copy_field">Kopio de %1$s</string>
|
<string name="copy_field">Kopio de %1$s</string>
|
||||||
<string name="about">Pri</string>
|
<string name="about">Pri</string>
|
||||||
<string name="hide_password_summary">Kaŝi pasvortojn sub (***) defaŭlte</string>
|
|
||||||
<string name="hide_password_title">Kaŝi pasvortojn</string>
|
|
||||||
<string name="loading_database">Datumbaza ŝarĝado…</string>
|
<string name="loading_database">Datumbaza ŝarĝado…</string>
|
||||||
<string name="creating_database">Datumbaza kreado…</string>
|
<string name="creating_database">Datumbaza kreado…</string>
|
||||||
<string name="list_entries_show_username_summary">Vidigi uzantnomojn en elementaj listoj</string>
|
<string name="list_entries_show_username_summary">Vidigi uzantnomojn en elementaj listoj</string>
|
||||||
|
|||||||
@@ -84,8 +84,6 @@
|
|||||||
<string name="list_size_summary">Tamaño del texto en la lista de elementos</string>
|
<string name="list_size_summary">Tamaño del texto en la lista de elementos</string>
|
||||||
<string name="loading_database">Cargando base de datos…</string>
|
<string name="loading_database">Cargando base de datos…</string>
|
||||||
<string name="lowercase">Minúsculas</string>
|
<string name="lowercase">Minúsculas</string>
|
||||||
<string name="hide_password_title">Ocultar contraseñas</string>
|
|
||||||
<string name="hide_password_summary">Oculta contraseñas (***) por defecto</string>
|
|
||||||
<string name="about">Acerca de</string>
|
<string name="about">Acerca de</string>
|
||||||
<string name="menu_change_key_settings">Cambiar contraseña maestra</string>
|
<string name="menu_change_key_settings">Cambiar contraseña maestra</string>
|
||||||
<string name="settings">Configuración</string>
|
<string name="settings">Configuración</string>
|
||||||
@@ -93,7 +91,6 @@
|
|||||||
<string name="menu_delete">Eliminar</string>
|
<string name="menu_delete">Eliminar</string>
|
||||||
<string name="menu_donate">Donar</string>
|
<string name="menu_donate">Donar</string>
|
||||||
<string name="menu_edit">Editar</string>
|
<string name="menu_edit">Editar</string>
|
||||||
<string name="menu_hide_password">Ocultar contraseña</string>
|
|
||||||
<string name="menu_lock">Bloquear la base de datos</string>
|
<string name="menu_lock">Bloquear la base de datos</string>
|
||||||
<string name="menu_open">Abrir</string>
|
<string name="menu_open">Abrir</string>
|
||||||
<string name="menu_search">Buscar</string>
|
<string name="menu_search">Buscar</string>
|
||||||
@@ -165,7 +162,7 @@
|
|||||||
<string name="warning_no_encryption_key">¿Continuar sin clave de cifrado\?</string>
|
<string name="warning_no_encryption_key">¿Continuar sin clave de cifrado\?</string>
|
||||||
<string name="encrypted_value_stored">Contraseña cifrada almacenada</string>
|
<string name="encrypted_value_stored">Contraseña cifrada almacenada</string>
|
||||||
<string name="database_history">Historial</string>
|
<string name="database_history">Historial</string>
|
||||||
<string name="autofill_explanation_summary">Habilite el servicio para rellenar formularios fácilmente desde otras aplicaciones</string>
|
<string name="autofill_explanation_summary">Configura el autocompletado para rellenar rápidamente formularios en otras aplicaciones</string>
|
||||||
<string name="unavailable">No disponible</string>
|
<string name="unavailable">No disponible</string>
|
||||||
<string name="menu_appearance_settings">Apariencia</string>
|
<string name="menu_appearance_settings">Apariencia</string>
|
||||||
<string name="general">General</string>
|
<string name="general">General</string>
|
||||||
@@ -195,7 +192,7 @@
|
|||||||
<string name="assign_master_key">Asignar una clave maestra</string>
|
<string name="assign_master_key">Asignar una clave maestra</string>
|
||||||
<string name="create_keepass_file">Crear una nueva bóveda</string>
|
<string name="create_keepass_file">Crear una nueva bóveda</string>
|
||||||
<string name="recycle_bin_title">Uso de la papelera de reciclaje</string>
|
<string name="recycle_bin_title">Uso de la papelera de reciclaje</string>
|
||||||
<string name="recycle_bin_summary">Mueve los grupos y las entradas al grupo \"Papelera de reciclaje\" antes de eliminarlos</string>
|
<string name="recycle_bin_summary">Mueve los grupos y los apuntes al grupo \"Papelera de reciclaje\" antes de eliminarlos</string>
|
||||||
<string name="monospace_font_fields_enable_title">Tipografía del campo</string>
|
<string name="monospace_font_fields_enable_title">Tipografía del campo</string>
|
||||||
<string name="monospace_font_fields_enable_summary">Cambia la tipografía usada en los campos para una mejor visibilidad del carácter</string>
|
<string name="monospace_font_fields_enable_summary">Cambia la tipografía usada en los campos para una mejor visibilidad del carácter</string>
|
||||||
<string name="allow_copy_password_title">Portapapeles de confianza</string>
|
<string name="allow_copy_password_title">Portapapeles de confianza</string>
|
||||||
@@ -256,7 +253,7 @@
|
|||||||
<string name="error_load_database">No se ha podido cargar la base de datos.</string>
|
<string name="error_load_database">No se ha podido cargar la base de datos.</string>
|
||||||
<string name="error_load_database_KDF_memory">No se ha podido cargar la clave. Intente reducir el \"Uso de memoria\" del KDF.</string>
|
<string name="error_load_database_KDF_memory">No se ha podido cargar la clave. Intente reducir el \"Uso de memoria\" del KDF.</string>
|
||||||
<string name="list_entries_show_username_title">Mostrar nombres de usuario</string>
|
<string name="list_entries_show_username_title">Mostrar nombres de usuario</string>
|
||||||
<string name="list_entries_show_username_summary">Muestra los nombres de usuario en las listas de entrada</string>
|
<string name="list_entries_show_username_summary">Muestra los nombres de usuario en las listas de apuntes</string>
|
||||||
<string name="menu_copy">Copiar</string>
|
<string name="menu_copy">Copiar</string>
|
||||||
<string name="menu_move">Mover</string>
|
<string name="menu_move">Mover</string>
|
||||||
<string name="menu_paste">Pegar</string>
|
<string name="menu_paste">Pegar</string>
|
||||||
@@ -274,12 +271,12 @@
|
|||||||
<string name="keyboard_name">Magikeyboard</string>
|
<string name="keyboard_name">Magikeyboard</string>
|
||||||
<string name="keyboard_label">Magikeyboard (KeePassDX)</string>
|
<string name="keyboard_label">Magikeyboard (KeePassDX)</string>
|
||||||
<string name="keyboard_setting_label">Configuración de Magikeyboard</string>
|
<string name="keyboard_setting_label">Configuración de Magikeyboard</string>
|
||||||
<string name="keyboard_entry_category">Entrada</string>
|
<string name="keyboard_entry_category">Apunte</string>
|
||||||
<string name="keyboard_entry_timeout_title">Tiempo límite</string>
|
<string name="keyboard_entry_timeout_title">Tiempo límite</string>
|
||||||
<string name="keyboard_entry_timeout_summary">Tiempo límite para vaciar la entrada del teclado</string>
|
<string name="keyboard_entry_timeout_summary">Tiempo límite para vaciar el apunte del teclado</string>
|
||||||
<string name="keyboard_notification_entry_title">Información sobre notificación</string>
|
<string name="keyboard_notification_entry_title">Información sobre notificación</string>
|
||||||
<string name="keyboard_notification_entry_summary">Muestra una notificación cuando esté disponible un apunte</string>
|
<string name="keyboard_notification_entry_summary">Muestra una notificación cuando esté disponible un apunte</string>
|
||||||
<string name="keyboard_notification_entry_content_title_text">Entrada</string>
|
<string name="keyboard_notification_entry_content_title_text">Apunte</string>
|
||||||
<string name="keyboard_notification_entry_content_title">%1$s disponible en Magikeyboard</string>
|
<string name="keyboard_notification_entry_content_title">%1$s disponible en Magikeyboard</string>
|
||||||
<string name="keyboard_notification_entry_content_text">%1$s</string>
|
<string name="keyboard_notification_entry_content_text">%1$s</string>
|
||||||
<string name="keyboard_notification_entry_clear_close_title">Vaciar al cerrar</string>
|
<string name="keyboard_notification_entry_clear_close_title">Vaciar al cerrar</string>
|
||||||
@@ -291,11 +288,11 @@
|
|||||||
<string name="keyboard_key_sound_title">Sonar al pulsar las teclas</string>
|
<string name="keyboard_key_sound_title">Sonar al pulsar las teclas</string>
|
||||||
<string name="selection_mode">Modo de selección</string>
|
<string name="selection_mode">Modo de selección</string>
|
||||||
<string name="do_not_kill_app">No cierre la aplicación…</string>
|
<string name="do_not_kill_app">No cierre la aplicación…</string>
|
||||||
<string name="lock_database_back_root_summary">Bloquear la base de datos cuando el usuario pulse el botón atrás en la pantalla inicial</string>
|
<string name="lock_database_back_root_summary">Pulse \'Atrás\' para bloquear la base de datos si se encuentra en la pantalla raíz de la base de datos</string>
|
||||||
<string name="clear_clipboard_notification_title">Vaciar al cerrar</string>
|
<string name="clear_clipboard_notification_title">Vaciar al cerrar</string>
|
||||||
<string name="clear_clipboard_notification_summary">Bloquea la base de datos cuando caduque la duración del portapapeles o se cierre la notificación tras empezar a utilizarla</string>
|
<string name="clear_clipboard_notification_summary">Bloquea la base de datos cuando caduque la duración del portapapeles o se cierre la notificación tras empezar a utilizarla</string>
|
||||||
<string name="recycle_bin">Papelera de reciclaje</string>
|
<string name="recycle_bin">Papelera de reciclaje</string>
|
||||||
<string name="keyboard_selection_entry_title">Selección de entrada</string>
|
<string name="keyboard_selection_entry_title">Selección de apuntes</string>
|
||||||
<string name="keyboard_selection_entry_summary">Al visualizar un apunte en KeePassDX, rellena Magikeyboard con ese apunte</string>
|
<string name="keyboard_selection_entry_summary">Al visualizar un apunte en KeePassDX, rellena Magikeyboard con ese apunte</string>
|
||||||
<string name="delete_entered_password_title">Eliminar contraseña</string>
|
<string name="delete_entered_password_title">Eliminar contraseña</string>
|
||||||
<string name="delete_entered_password_summary">Elimina la contraseña introducida tras un intento de conexión a una base de datos</string>
|
<string name="delete_entered_password_summary">Elimina la contraseña introducida tras un intento de conexión a una base de datos</string>
|
||||||
@@ -315,8 +312,8 @@
|
|||||||
<string name="entry_UUID">UUID</string>
|
<string name="entry_UUID">UUID</string>
|
||||||
<string name="error_move_entry_here">No puede mover un apunte aquí.</string>
|
<string name="error_move_entry_here">No puede mover un apunte aquí.</string>
|
||||||
<string name="error_copy_entry_here">No puede copiar un apunte aquí.</string>
|
<string name="error_copy_entry_here">No puede copiar un apunte aquí.</string>
|
||||||
<string name="list_groups_show_number_entries_title">Mostrar número de entradas</string>
|
<string name="list_groups_show_number_entries_title">Mostrar número de apunte</string>
|
||||||
<string name="list_groups_show_number_entries_summary">Muestra el número de entradas de un grupo</string>
|
<string name="list_groups_show_number_entries_summary">Muestra el número de apuntes de un grupo</string>
|
||||||
<string name="content_description_background">Fondo</string>
|
<string name="content_description_background">Fondo</string>
|
||||||
<string name="content_description_update_from_list">Actualizar</string>
|
<string name="content_description_update_from_list">Actualizar</string>
|
||||||
<string name="content_description_keyboard_close_fields">Cerrar campos</string>
|
<string name="content_description_keyboard_close_fields">Cerrar campos</string>
|
||||||
@@ -498,7 +495,7 @@
|
|||||||
<string name="keyboard_auto_go_action_title">Acción automática de la tecla</string>
|
<string name="keyboard_auto_go_action_title">Acción automática de la tecla</string>
|
||||||
<string name="keyboard_save_search_info_summary">Intente guardar información compartida cuando realice una selección manual de apunte para utilizar más fácil en el futuro</string>
|
<string name="keyboard_save_search_info_summary">Intente guardar información compartida cuando realice una selección manual de apunte para utilizar más fácil en el futuro</string>
|
||||||
<string name="keyboard_save_search_info_title">Guardar información compartida</string>
|
<string name="keyboard_save_search_info_title">Guardar información compartida</string>
|
||||||
<string name="show_uuid_summary">Muestra el UUID vinculado a una entrada o a un grupo</string>
|
<string name="show_uuid_summary">Muestra el UUID vinculado a un apuntes o a un grupo</string>
|
||||||
<string name="show_uuid_title">Mostrar UUID</string>
|
<string name="show_uuid_title">Mostrar UUID</string>
|
||||||
<string name="error_rebuild_list">No se puede reconstruir correctamente la lista.</string>
|
<string name="error_rebuild_list">No se puede reconstruir correctamente la lista.</string>
|
||||||
<string name="error_database_uri_null">No se puede recuperar la URI de la base de datos.</string>
|
<string name="error_database_uri_null">No se puede recuperar la URI de la base de datos.</string>
|
||||||
@@ -513,7 +510,7 @@
|
|||||||
<string name="warning_database_info_changed_options">Fusionar los datos, sobrescribir las modificaciones externas guardando la base de datos o recargarla con los últimos cambios.</string>
|
<string name="warning_database_info_changed_options">Fusionar los datos, sobrescribir las modificaciones externas guardando la base de datos o recargarla con los últimos cambios.</string>
|
||||||
<string name="warning_database_info_changed">La información contenida en su archivo de base de datos ha sido modificada fuera de la aplicación.</string>
|
<string name="warning_database_info_changed">La información contenida en su archivo de base de datos ha sido modificada fuera de la aplicación.</string>
|
||||||
<string name="menu_reload_database">Recargar datos</string>
|
<string name="menu_reload_database">Recargar datos</string>
|
||||||
<string name="error_otp_type">El tipo de OTP existente no es reconocido por este formulario, es posible que su validación ya no genere correctamente el código.</string>
|
<string name="error_otp_type">El tipo de OTP existente no es reconocido por este formulario, y es posible que su validación ya no genere correctamente el token.</string>
|
||||||
<string name="download_canceled">¡Cancelado!</string>
|
<string name="download_canceled">¡Cancelado!</string>
|
||||||
<string name="error_duplicate_file">Los datos del archivo ya existen.</string>
|
<string name="error_duplicate_file">Los datos del archivo ya existen.</string>
|
||||||
<string name="error_upload_file">Se ha producido un error al cargar los datos del archivo.</string>
|
<string name="error_upload_file">Se ha producido un error al cargar los datos del archivo.</string>
|
||||||
@@ -539,7 +536,7 @@
|
|||||||
<string name="error_word_reserved">Esta palabra está reservada y no se puede usar.</string>
|
<string name="error_word_reserved">Esta palabra está reservada y no se puede usar.</string>
|
||||||
<string name="templates">Plantillas</string>
|
<string name="templates">Plantillas</string>
|
||||||
<string name="templates_group_uuid_title">Grupo de plantillas</string>
|
<string name="templates_group_uuid_title">Grupo de plantillas</string>
|
||||||
<string name="templates_group_enable_summary">Utilice plantillas dinámicas para completar los campos de una entrada</string>
|
<string name="templates_group_enable_summary">Utilice plantillas dinámicas para completar los campos de un apunte</string>
|
||||||
<string name="templates_group_enable_title">Uso de plantillas</string>
|
<string name="templates_group_enable_title">Uso de plantillas</string>
|
||||||
<string name="version">Versión</string>
|
<string name="version">Versión</string>
|
||||||
<string name="template">Plantilla</string>
|
<string name="template">Plantilla</string>
|
||||||
@@ -571,12 +568,12 @@
|
|||||||
<string name="ssid">SSID</string>
|
<string name="ssid">SSID</string>
|
||||||
<string name="personal_identification_number">PIN</string>
|
<string name="personal_identification_number">PIN</string>
|
||||||
<string name="card_verification_value">CVV</string>
|
<string name="card_verification_value">CVV</string>
|
||||||
<string name="show_otp_token_summary">Muestra los códigos OTP en la lista de entradas</string>
|
<string name="show_otp_token_summary">Muestra los códigos OTP en la lista de apuntes</string>
|
||||||
<string name="show_otp_token_title">Mostrar código OTP</string>
|
<string name="show_otp_token_title">Mostrar código OTP</string>
|
||||||
<string name="menu_external_icon">Icono externo</string>
|
<string name="menu_external_icon">Icono externo</string>
|
||||||
<string name="autofill_manual_selection_summary">Muestra la opción para permitir al usuario seleccionar la entrada de la base de datos</string>
|
<string name="autofill_manual_selection_summary">Muestra la opción para permitir al usuario seleccionar el apunte de la base de datos</string>
|
||||||
<string name="autofill_manual_selection_title">Selección manual</string>
|
<string name="autofill_manual_selection_title">Selección manual</string>
|
||||||
<string name="autofill_select_entry">Seleccionar entrada…</string>
|
<string name="autofill_select_entry">Seleccionar apunte…</string>
|
||||||
<string name="hint_icon_name">Nombre del icono</string>
|
<string name="hint_icon_name">Nombre del icono</string>
|
||||||
<string name="warning_exact_alarm">No ha permitido que la app use una alarma exacta. Como resultado, las funciones que requieren un temporizador no se harán con una hora exacta.</string>
|
<string name="warning_exact_alarm">No ha permitido que la app use una alarma exacta. Como resultado, las funciones que requieren un temporizador no se harán con una hora exacta.</string>
|
||||||
<string name="permission">Permiso</string>
|
<string name="permission">Permiso</string>
|
||||||
@@ -587,7 +584,7 @@
|
|||||||
<string name="warning_database_info_reloaded">La recarga de la base de datos borrará los datos modificados localmente.</string>
|
<string name="warning_database_info_reloaded">La recarga de la base de datos borrará los datos modificados localmente.</string>
|
||||||
<string name="warning_keyfile_integrity">El hash del archivo no está garantizado porque Android puede cambiar sus datos sobre la marcha. Cambia la extensión del archivo a .bin para una integridad correcta.</string>
|
<string name="warning_keyfile_integrity">El hash del archivo no está garantizado porque Android puede cambiar sus datos sobre la marcha. Cambia la extensión del archivo a .bin para una integridad correcta.</string>
|
||||||
<string name="enable_keep_screen_on_title">Mantener pantalla encendida</string>
|
<string name="enable_keep_screen_on_title">Mantener pantalla encendida</string>
|
||||||
<string name="enable_keep_screen_on_summary">Mantiene la pantalla encendida cuando se visualiza la entrada</string>
|
<string name="enable_keep_screen_on_summary">Mantiene la pantalla encendida cuando se visualiza el apunte</string>
|
||||||
<string name="enable_screenshot_mode_title">Modo captura de pantalla</string>
|
<string name="enable_screenshot_mode_title">Modo captura de pantalla</string>
|
||||||
<string name="enable_screenshot_mode_summary">Permite que aplicaciones de terceros graben o tomen pantallazos de la aplicación</string>
|
<string name="enable_screenshot_mode_summary">Permite que aplicaciones de terceros graben o tomen pantallazos de la aplicación</string>
|
||||||
<string name="show_entry_colors_summary">Muestra los colores de primer y segundo plano de una entrada</string>
|
<string name="show_entry_colors_summary">Muestra los colores de primer y segundo plano de una entrada</string>
|
||||||
@@ -666,7 +663,7 @@
|
|||||||
<string name="ask">Preguntar</string>
|
<string name="ask">Preguntar</string>
|
||||||
<string name="configure">Configurar</string>
|
<string name="configure">Configurar</string>
|
||||||
<string name="unlock_and_link_biometric">Enlace de desbloqueo del dispositivo</string>
|
<string name="unlock_and_link_biometric">Enlace de desbloqueo del dispositivo</string>
|
||||||
<string name="menu_appearance_settings_summary">Temas, colores, atributos</string>
|
<string name="menu_appearance_settings_summary">Temas, colores, iconos, fuentes, atributos</string>
|
||||||
<string name="unlock">Desbloquear</string>
|
<string name="unlock">Desbloquear</string>
|
||||||
<string name="education_validate_entry_title">Validar la entrada</string>
|
<string name="education_validate_entry_title">Validar la entrada</string>
|
||||||
<string name="education_validate_entry_summary">No olvide validar tu inicio de sesión y guardar tu base de datos.\n\nSi se activa un bloqueo automático y olvidas que estabas haciendo una modificación, corres el riesgo de perder tus datos.</string>
|
<string name="education_validate_entry_summary">No olvide validar tu inicio de sesión y guardar tu base de datos.\n\nSi se activa un bloqueo automático y olvidas que estabas haciendo una modificación, corres el riesgo de perder tus datos.</string>
|
||||||
@@ -683,11 +680,52 @@
|
|||||||
<string name="style_name_dark">Oscuro</string>
|
<string name="style_name_dark">Oscuro</string>
|
||||||
<string name="warning_database_info_changed_options_read_only">Recargue la base de datos con los últimos cambios.</string>
|
<string name="warning_database_info_changed_options_read_only">Recargue la base de datos con los últimos cambios.</string>
|
||||||
<string name="nodes">Nodos</string>
|
<string name="nodes">Nodos</string>
|
||||||
<string name="recursive_number_entries_summary">Calcula recursivamente el número de entradas en un grupo</string>
|
<string name="recursive_number_entries_summary">Calcula recursivamente el número de apuntes en un grupo</string>
|
||||||
<string name="warning_large_keyfile">No se recomienda agregar un archivo de clave grande , esto puede impedir que se abra la base de datos.</string>
|
<string name="warning_large_keyfile">No se recomienda agregar un archivo de clave grande , esto puede impedir que se abra la base de datos.</string>
|
||||||
<string name="hide_templates_title">Ocultar plantillas</string>
|
<string name="hide_templates_title">Ocultar plantillas</string>
|
||||||
<string name="generate_keyfile">Generar archivo de claves</string>
|
<string name="generate_keyfile">Generar archivo de claves</string>
|
||||||
<string name="recursive_number_entries_title">Número recursivo de entradas</string>
|
<string name="recursive_number_entries_title">Número recursivo de apuntes</string>
|
||||||
<string name="hide_templates_summary">Las plantillas no se muestran</string>
|
<string name="hide_templates_summary">Las plantillas no se muestran</string>
|
||||||
<string name="error_otp_secret_length">La clave secreta debe tener al menos %1$d caracteres.</string>
|
<string name="error_otp_secret_length">La clave secreta debe tener al menos %1$d caracteres.</string>
|
||||||
|
<string name="entry_application_id">ID aplicación</string>
|
||||||
|
<string name="warning_overwrite_data_title">¿Desea sobrescribir los datos existentes?</string>
|
||||||
|
<string name="warning_overwrite_data_description">Esta acción sustituirá los datos existentes en el apuntes. Si el historial está habilitado, podrá recuperar los datos antiguos.</string>
|
||||||
|
<string name="credential_provider">Proveedor de credenciales</string>
|
||||||
|
<string name="passkeys_explanation_summary">Configure las claves de acceso para un inicio de sesión rápido y seguro sin contraseña</string>
|
||||||
|
<string name="passkeys">Claves de acceso</string>
|
||||||
|
<string name="passkeys_preference_title">Configuración de claves de acceso</string>
|
||||||
|
<string name="passkeys_close_database_title">Cerrar base de datos</string>
|
||||||
|
<string name="passkeys_close_database_summary">Cerrar la base de datos después de seleccionar una contraseña</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">Administrar navegadores en la lista personalizada de aplicaciones privilegiadas</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">ADVERTENCIA: Una aplicación privilegiada actúa como puerta de enlace para recuperar el origen de una autenticación. Asegúrese de su legitimidad para evitar problemas de seguridad.</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">Aplicaciones privilegiadas</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">Aplicación no reconocida</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">%1$s intenta realizar una acción de claves de acceso.\n\n¿Añadirlo a la lista de aplicaciones con privilegios?</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">Falta la firma</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">ADVERTENCIA: La clave de acceso se ha creado desde otro cliente o se ha eliminado la firma. Asegúrese de que la aplicación que desea autenticar forma parte del mismo servicio y es legítima para evitar problemas de seguridad.\nSi la aplicación es un navegador, no añada su firma al apunte, sino a la lista de aplicaciones privilegiadas en la configuración.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">%1$s no se reconoce e intenta autenticarse con una clave de acceso existente.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">¿Añadir firma de la aplicación al apunte de la clave de acceso?</string>
|
||||||
|
<string name="passkeys_auto_select_title">Selección automática</string>
|
||||||
|
<string name="passkeys_auto_select_summary">Seleccionar automáticamente si solo hay un apunte y la base de datos está abierta, solo si la aplicación solicitante es compatible</string>
|
||||||
|
<string name="passkeys_backup_eligibility_title">Requisitos para la copia de seguridad</string>
|
||||||
|
<string name="passkeys_backup_eligibility_summary">Determinar en el momento de la creación si se permite realizar copias de seguridad de la fuente de credenciales de clave pública</string>
|
||||||
|
<string name="passkeys_backup_state_title">Estado de copia de seguridad</string>
|
||||||
|
<string name="passkeys_backup_state_summary">Indique que las credenciales están respaldadas y protegidas contra la pérdida de un solo dispositivo</string>
|
||||||
|
<string name="credential_provider_service_subtitle">Claves de acceso, proveedor de credenciales de autocompletar</string>
|
||||||
|
<string name="passkey">Claves de acceso</string>
|
||||||
|
<string name="passkey_service_name">Proveedor de credenciales KeePassDX</string>
|
||||||
|
<string name="passkey_creation_description">Guardar clave de acceso en nuevo apunte</string>
|
||||||
|
<string name="passkey_update_description">Actualizar clave de acceso en %1$s</string>
|
||||||
|
<string name="passkey_selection_username">No se ha encontrado ninguna clave de acceso</string>
|
||||||
|
<string name="passkey_selection_description">Seleccionar una clave de acceso existente</string>
|
||||||
|
<string name="passkey_database_username">Base de datos KeePassDX</string>
|
||||||
|
<string name="passkey_locked_database_description">Seleccionar para desbloquear</string>
|
||||||
|
<string name="passkey_username">Nombre de usuario de clave de acceso</string>
|
||||||
|
<string name="passkey_private_key">Clave privada clave de acceso</string>
|
||||||
|
<string name="passkey_credential_id">Identificador de credencial de clave de acceso</string>
|
||||||
|
<string name="passkey_user_handle">Identificador de usuario de clave de acceso</string>
|
||||||
|
<string name="passkey_relying_party">Parte que confía en la clave de acceso</string>
|
||||||
|
<string name="passkey_backup_eligibility">Requisitos para la copia de seguridad de la clave de acceso</string>
|
||||||
|
<string name="passkey_backup_state">Estado de copia de seguridad de la clave de acceso</string>
|
||||||
|
<string name="error_passkey_result">No se puede retornar la clave de acceso</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -197,8 +197,6 @@
|
|||||||
<string name="hint_keyfile">Võtmefail</string>
|
<string name="hint_keyfile">Võtmefail</string>
|
||||||
<string name="hint_length">Pikkus</string>
|
<string name="hint_length">Pikkus</string>
|
||||||
<string name="password">Salasõna</string>
|
<string name="password">Salasõna</string>
|
||||||
<string name="hide_password_title">Peida salasõnad</string>
|
|
||||||
<string name="hide_password_summary">Vakimisi peida salasõnad (***) maski taha</string>
|
|
||||||
<string name="colorize_password_title">Värvi salasõnad</string>
|
<string name="colorize_password_title">Värvi salasõnad</string>
|
||||||
<string name="error_registration_read_only">Uue kirje salvestamine pole võimalik andmebaasis, milles on vaid lugemisõigus.</string>
|
<string name="error_registration_read_only">Uue kirje salvestamine pole võimalik andmebaasis, milles on vaid lugemisõigus.</string>
|
||||||
<string name="error_database_uri_null">Andmebaasi ühtset ressursiidentifikaatorit ei õnnestu laadida.</string>
|
<string name="error_database_uri_null">Andmebaasi ühtset ressursiidentifikaatorit ei õnnestu laadida.</string>
|
||||||
@@ -213,7 +211,6 @@
|
|||||||
<string name="list_groups_show_number_entries_title">Näita kirjete arvu</string>
|
<string name="list_groups_show_number_entries_title">Näita kirjete arvu</string>
|
||||||
<string name="show_uuid_title">Näita UUID\'d</string>
|
<string name="show_uuid_title">Näita UUID\'d</string>
|
||||||
<string name="show_uuid_summary">Näita kirje või grupiga seotud UUID\'d</string>
|
<string name="show_uuid_summary">Näita kirje või grupiga seotud UUID\'d</string>
|
||||||
<string name="menu_hide_password">Peida salasõna</string>
|
|
||||||
<string name="menu_showpass">Näita salasõna</string>
|
<string name="menu_showpass">Näita salasõna</string>
|
||||||
<string name="error_cancel_by_user">Katkestatud kasutaja poolt.</string>
|
<string name="error_cancel_by_user">Katkestatud kasutaja poolt.</string>
|
||||||
<string name="error_driver_required">%1$s draiver on vajalik.</string>
|
<string name="error_driver_required">%1$s draiver on vajalik.</string>
|
||||||
@@ -403,7 +400,7 @@
|
|||||||
<string name="database_history">Ajalugu</string>
|
<string name="database_history">Ajalugu</string>
|
||||||
<string name="properties">Omadused</string>
|
<string name="properties">Omadused</string>
|
||||||
<string name="menu_appearance_settings">Välimus</string>
|
<string name="menu_appearance_settings">Välimus</string>
|
||||||
<string name="menu_appearance_settings_summary">Välimus, värvid, omadused</string>
|
<string name="menu_appearance_settings_summary">Välimus, värvid, ikoonid, kirjatüübid, omadused</string>
|
||||||
<string name="html_text_dev_feature_upgrade">Ära unusta paigaldada viimaseid versioone ja tagada, et rakendus on alati uuendatud.</string>
|
<string name="html_text_dev_feature_upgrade">Ära unusta paigaldada viimaseid versioone ja tagada, et rakendus on alati uuendatud.</string>
|
||||||
<string name="at_least_one_char">Vähemalt üks tähemärk igast</string>
|
<string name="at_least_one_char">Vähemalt üks tähemärk igast</string>
|
||||||
<string name="exclude_ambiguous_chars">Välista mitmetähenduslikud tähemärgid</string>
|
<string name="exclude_ambiguous_chars">Välista mitmetähenduslikud tähemärgid</string>
|
||||||
@@ -452,7 +449,7 @@
|
|||||||
<string name="lock_database_screen_off_summary">Mõni sekund peale ekraani väljalülitumist lukusta andmebaas</string>
|
<string name="lock_database_screen_off_summary">Mõni sekund peale ekraani väljalülitumist lukusta andmebaas</string>
|
||||||
<string name="lock_database_back_root_title">Vajuta nuppu „Tagasi“</string>
|
<string name="lock_database_back_root_title">Vajuta nuppu „Tagasi“</string>
|
||||||
<string name="lock_database_show_button_title">Näita lukustuse nuppu</string>
|
<string name="lock_database_show_button_title">Näita lukustuse nuppu</string>
|
||||||
<string name="lock_database_back_root_summary">Lukusta andmebaas peale juurkaustas „Tagasi“ nupu klõpsimist</string>
|
<string name="lock_database_back_root_summary">Kui sa pole andmebaasi juurkasutas, siis lukusta andmebaas peale „Tagasi“ nupu klõpsimist</string>
|
||||||
<string name="allow_copy_password_title">Lõikelaua usaldamine</string>
|
<string name="allow_copy_password_title">Lõikelaua usaldamine</string>
|
||||||
<string name="allow_copy_password_summary">Luba sisestatud salasõna ja kaitstud väljade kopeerimise lõikelauale</string>
|
<string name="allow_copy_password_summary">Luba sisestatud salasõna ja kaitstud väljade kopeerimise lõikelauale</string>
|
||||||
<string name="education_unlock_title">Eemalda oma andmebaasi lukustus</string>
|
<string name="education_unlock_title">Eemalda oma andmebaasi lukustus</string>
|
||||||
@@ -470,7 +467,7 @@
|
|||||||
<string name="allow_copy_password_warning">Hoiatus: süsteemiülene lõikelaud on kõikide rakenduste kasutuses. Kui sa kopeerid sinna delikaatseid andmeid, siis muu tarkvara võib seda seal näha. Kui sa kasutad KDE Connecti või muud lõikelaua jagamise teenust, siis sõltuvalt seadistustest võivad need delikaatsed andmed olla nähtavad ka muudes seadmetes.</string>
|
<string name="allow_copy_password_warning">Hoiatus: süsteemiülene lõikelaud on kõikide rakenduste kasutuses. Kui sa kopeerid sinna delikaatseid andmeid, siis muu tarkvara võib seda seal näha. Kui sa kasutad KDE Connecti või muud lõikelaua jagamise teenust, siis sõltuvalt seadistustest võivad need delikaatsed andmed olla nähtavad ka muudes seadmetes.</string>
|
||||||
<string name="warning_database_revoked">Failihaldur on blokeerinud ligipääsu failile. Sulge andmebaas ja ava ta uuesti oma asukohast.</string>
|
<string name="warning_database_revoked">Failihaldur on blokeerinud ligipääsu failile. Sulge andmebaas ja ava ta uuesti oma asukohast.</string>
|
||||||
<string name="autofill_sign_in_prompt">Logi sisse KeePassDX abil</string>
|
<string name="autofill_sign_in_prompt">Logi sisse KeePassDX abil</string>
|
||||||
<string name="autofill_explanation_summary">Täitmaks andmevorme teistes rakenduses, luba automaattäite teenus</string>
|
<string name="autofill_explanation_summary">Täitmaks andmevorme teistes rakenduses, seadista automaattäite teenus</string>
|
||||||
<string name="autofill_select_entry">Vali kirje…</string>
|
<string name="autofill_select_entry">Vali kirje…</string>
|
||||||
<string name="set_credential_provider_service_title">Vali vaikimisi kasutatav automaattäite teenus</string>
|
<string name="set_credential_provider_service_title">Vali vaikimisi kasutatav automaattäite teenus</string>
|
||||||
<string name="autofill_preference_title">Automaattäite teenuse seadistused</string>
|
<string name="autofill_preference_title">Automaattäite teenuse seadistused</string>
|
||||||
@@ -662,4 +659,45 @@
|
|||||||
<string name="keyboard_previous_fill_in_summary">Peale tegevust „Automaatne võtmetoiming“ lülita automaatselt tagasi eelmisele klahvistikule</string>
|
<string name="keyboard_previous_fill_in_summary">Peale tegevust „Automaatne võtmetoiming“ lülita automaatselt tagasi eelmisele klahvistikule</string>
|
||||||
<string name="education_read_only_summary">Saad juhtida sessioonil kasutatavat avamisviisi.\n\n„Kirjutuskaitstud“ tagab, et juhuslike muudatustega ei läheks andmeid kaotsi.\n„Muudetav“ võimaldab sul lisada, kustutada või muuta kõiki andmebaasi kirjeid.</string>
|
<string name="education_read_only_summary">Saad juhtida sessioonil kasutatavat avamisviisi.\n\n„Kirjutuskaitstud“ tagab, et juhuslike muudatustega ei läheks andmeid kaotsi.\n„Muudetav“ võimaldab sul lisada, kustutada või muuta kõiki andmebaasi kirjeid.</string>
|
||||||
<string name="error_otp_secret_length">Salavõti peab olema vähemalt %1$d tähemärki pikk.</string>
|
<string name="error_otp_secret_length">Salavõti peab olema vähemalt %1$d tähemärki pikk.</string>
|
||||||
|
<string name="entry_application_id">Rakenduse tunnus</string>
|
||||||
|
<string name="passkeys_close_database_title">Sulge andmebaas</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">Rakendust ei õnnestu tuvastada</string>
|
||||||
|
<string name="warning_overwrite_data_title">Kas soovid olemasolevad andmed üle kirjutada?</string>
|
||||||
|
<string name="warning_overwrite_data_description">Selle toiminguga asendad kirje olemasolevad andmed, aga kui ajaloo logimine on kasutusel, siis saad vanu kirjeid ka hiljem näha.</string>
|
||||||
|
<string name="credential_provider">kasutajanime/salasõna automaattäite teenus</string>
|
||||||
|
<string name="passkeys">WebAuthn pääsuvõtmed</string>
|
||||||
|
<string name="passkeys_explanation_summary">Seadista WebAuthn pääsuvõtmed, mis võimaldavad kiiret ja turvalist salasõnadeta ligipääsu</string>
|
||||||
|
<string name="passkeys_preference_title">Pääsuvõtmete seadistused</string>
|
||||||
|
<string name="passkeys_close_database_summary">Peale pääsuvõtme valimist sulge andmebaas</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">Eesõigustega rakendused</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">Halda brausereid sinu loodud eesõigustega rakenduste loendis</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">HOIATUS: Eesõigustega rakendus toimib lüüsina autentimise algallikaga suhtlemisel. Turvaprobleemide vältimiseks palun taga, et kasutad õiget rakendust.</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">%1$s kaset pääsuvõtmega autentimiseks.\n\nKas lisame ta eesõigustega rakenduste loendisse?</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">Allkiri on puudu</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">HOAITUS: See pääsuluba on loodud teise kliendi poolt või on allkiri kustutatud. Turvaprobleemide vältimiseks palun kontrolli rakendus, milles soovi end tuvastada on sama teenuse osa ning sisuliselt õige.\n\nKui rakendus on veebibrauser, siis ära lisa allkirja kirje juurde vaid seadistustes leiduva eesõigustega rakenduste loendisse.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">%1$s on antud kontekstis tundmatu ja mittetunnustatud ning ta proovib autentimist olemasoleva pääsuvõtmega.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">Kas soovid lisada rakenduse allkirja pääsuvõtme kirjele?</string>
|
||||||
|
<string name="passkeys_auto_select_title">Automaatne valik</string>
|
||||||
|
<string name="passkeys_auto_select_summary">Vali automaatselt vaid siis, kui vaid üks kirje ja andmebaas on avatud ning vaid siis, kui rakendus ühildub</string>
|
||||||
|
<string name="passkeys_backup_eligibility_title">Varunduse kõlblikkus</string>
|
||||||
|
<string name="passkeys_backup_eligibility_summary">Otsusta loomise hetkel, kas avaliku võtme allikat on lubatud varundada</string>
|
||||||
|
<string name="passkeys_backup_state_title">Varunduse olek</string>
|
||||||
|
<string name="passkeys_backup_state_summary">Anna märku, kas autentimisandmed on varundatud ja kaitstud üksiku seadme kaotsimineku puhul</string>
|
||||||
|
<string name="credential_provider_service_subtitle">Pääsuvõtmed - automaattäite teenusepakkuja</string>
|
||||||
|
<string name="passkey">WebAuthn pääsuvõti</string>
|
||||||
|
<string name="passkey_service_name">KeePassDX autentimisteenusepakkuja</string>
|
||||||
|
<string name="passkey_creation_description">Salvesta pääsuvõti uue kirjena</string>
|
||||||
|
<string name="passkey_update_description">Uuenda pääsuvõtit: %1$s</string>
|
||||||
|
<string name="passkey_selection_username">Pääsuvõtit ei leidu</string>
|
||||||
|
<string name="passkey_selection_description">Vali olemasolev pääsuvõti</string>
|
||||||
|
<string name="passkey_database_username">KeePassDX-i andmebaas</string>
|
||||||
|
<string name="passkey_locked_database_description">Vali lukustuse eemaldamiseks</string>
|
||||||
|
<string name="passkey_username">Pääsuvõtme kasutajanimi</string>
|
||||||
|
<string name="passkey_private_key">Pääsuvõtme privaatvõti</string>
|
||||||
|
<string name="passkey_credential_id">Pääsuvõtme autentimisühiku tunnus</string>
|
||||||
|
<string name="passkey_user_handle">Pääsuvõtme kasutaja võrgutunnus</string>
|
||||||
|
<string name="passkey_relying_party">Pääsuvõtit edastav osapool</string>
|
||||||
|
<string name="passkey_backup_eligibility">Pääsuvõtme kõlblikkus varundamiseks</string>
|
||||||
|
<string name="passkey_backup_state">Pääsuvõtme varunduse olek</string>
|
||||||
|
<string name="error_passkey_result">Pääsuvõtme väljastamine vastuseks ei õnnestu</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -93,8 +93,6 @@
|
|||||||
<string name="list_size_summary">Testuaren tamaina taldearen listan</string>
|
<string name="list_size_summary">Testuaren tamaina taldearen listan</string>
|
||||||
<string name="loading_database">Datubasea kargatzen…</string>
|
<string name="loading_database">Datubasea kargatzen…</string>
|
||||||
<string name="lowercase">minuskulak</string>
|
<string name="lowercase">minuskulak</string>
|
||||||
<string name="hide_password_title">Pasahitza estali</string>
|
|
||||||
<string name="hide_password_summary">Pasahitza estali modu lehenetsian</string>
|
|
||||||
<string name="about">Honi buruz</string>
|
<string name="about">Honi buruz</string>
|
||||||
<string name="menu_change_key_settings">Gako nagusia aldatu</string>
|
<string name="menu_change_key_settings">Gako nagusia aldatu</string>
|
||||||
<string name="settings">Ezarpenak</string>
|
<string name="settings">Ezarpenak</string>
|
||||||
@@ -102,7 +100,6 @@
|
|||||||
<string name="menu_delete">Ezabatu</string>
|
<string name="menu_delete">Ezabatu</string>
|
||||||
<string name="menu_donate">Dirua eman</string>
|
<string name="menu_donate">Dirua eman</string>
|
||||||
<string name="menu_edit">Editatu</string>
|
<string name="menu_edit">Editatu</string>
|
||||||
<string name="menu_hide_password">Pasahitza ezkutatu</string>
|
|
||||||
<string name="menu_lock">Datu-basea blokeatu</string>
|
<string name="menu_lock">Datu-basea blokeatu</string>
|
||||||
<string name="menu_open">Ireki</string>
|
<string name="menu_open">Ireki</string>
|
||||||
<string name="menu_search">Bilatu</string>
|
<string name="menu_search">Bilatu</string>
|
||||||
|
|||||||
@@ -79,7 +79,6 @@
|
|||||||
<string name="menu_open">باز</string>
|
<string name="menu_open">باز</string>
|
||||||
<string name="menu_save_database">ذخیره پایگاه داده</string>
|
<string name="menu_save_database">ذخیره پایگاه داده</string>
|
||||||
<string name="menu_lock">بانک اطلاعاتی قفل</string>
|
<string name="menu_lock">بانک اطلاعاتی قفل</string>
|
||||||
<string name="menu_hide_password">پنهان کردن رمز عبور</string>
|
|
||||||
<string name="menu_cancel">لغو</string>
|
<string name="menu_cancel">لغو</string>
|
||||||
<string name="menu_delete">حذف</string>
|
<string name="menu_delete">حذف</string>
|
||||||
<string name="menu_paste">جاگذاری</string>
|
<string name="menu_paste">جاگذاری</string>
|
||||||
@@ -97,8 +96,6 @@
|
|||||||
<string name="copy_field">کپی %1$s</string>
|
<string name="copy_field">کپی %1$s</string>
|
||||||
<string name="menu_change_key_settings">کلید اصلی را تغییر دهید</string>
|
<string name="menu_change_key_settings">کلید اصلی را تغییر دهید</string>
|
||||||
<string name="about">در باره</string>
|
<string name="about">در باره</string>
|
||||||
<string name="hide_password_summary">رمزهای عبور ماسک (***) به طور پیش فرض</string>
|
|
||||||
<string name="hide_password_title">پنهان کردن رمزهای عبور</string>
|
|
||||||
<string name="lowercase">ترجمه</string>
|
<string name="lowercase">ترجمه</string>
|
||||||
<string name="loading_database">پایگاه داده بارگذاری…</string>
|
<string name="loading_database">پایگاه داده بارگذاری…</string>
|
||||||
<string name="creating_database">ایجاد پایگاه داده…</string>
|
<string name="creating_database">ایجاد پایگاه داده…</string>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
--><resources>
|
--><resources>
|
||||||
<string name="feedback">Palaute</string>
|
<string name="feedback">Palaute</string>
|
||||||
<string name="homepage">Kotisivu</string>
|
<string name="homepage">Kotisivu</string>
|
||||||
<string name="about_description">KeePass-salasanahallintaohjelman Android-toteutus</string>
|
<string name="about_description">KeePass-salasanahallintaohjelman Android-toteutus.</string>
|
||||||
<string name="accept">Hyväksy</string>
|
<string name="accept">Hyväksy</string>
|
||||||
<string name="add_entry">Lisää uusi salasanatietue</string>
|
<string name="add_entry">Lisää uusi salasanatietue</string>
|
||||||
<string name="add_group">Lisää ryhmä</string>
|
<string name="add_group">Lisää ryhmä</string>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<string name="application">Sovellus</string>
|
<string name="application">Sovellus</string>
|
||||||
<string name="menu_app_settings">Ohjelman asetukset</string>
|
<string name="menu_app_settings">Ohjelman asetukset</string>
|
||||||
<string name="brackets">Hakasulkeet</string>
|
<string name="brackets">Hakasulkeet</string>
|
||||||
<string name="file_manager_install_description">Tietokantojen avaamista, luomista ja tallentamista varten tarvitaan tiedostonhallintaohjelma, joka tukee ACTION_CREATE_DOCUMENT ja ACTION_OPEN_DOCUMENT Intent-toimintoja.</string>
|
<string name="file_manager_install_description">Tietokantojen avaamista, luomista ja tallentamista varten tarvitaan tiedostonhallintaohjelma, joka tukee ACTION_CREATE_DOCUMENT ja ACTION_OPEN_DOCUMENT intent-toimintoja.</string>
|
||||||
<string name="clipboard_cleared">Leikepöytä tyhjennetty</string>
|
<string name="clipboard_cleared">Leikepöytä tyhjennetty</string>
|
||||||
<string name="clipboard_error_title">Leikepöytävirhe</string>
|
<string name="clipboard_error_title">Leikepöytävirhe</string>
|
||||||
<string name="clipboard_error">Jotkin laitteet eivät anna sovellusten käyttää leikepöytää.</string>
|
<string name="clipboard_error">Jotkin laitteet eivät anna sovellusten käyttää leikepöytää.</string>
|
||||||
@@ -92,8 +92,6 @@
|
|||||||
<string name="list_size_summary">Tekstin koko ryhmälistauksessa</string>
|
<string name="list_size_summary">Tekstin koko ryhmälistauksessa</string>
|
||||||
<string name="loading_database">Ladataan tietokantaa…</string>
|
<string name="loading_database">Ladataan tietokantaa…</string>
|
||||||
<string name="lowercase">pienet kirjaimet</string>
|
<string name="lowercase">pienet kirjaimet</string>
|
||||||
<string name="hide_password_title">Piilota salasana</string>
|
|
||||||
<string name="hide_password_summary">Piilota salasanat oletuksena</string>
|
|
||||||
<string name="about">Tietoa</string>
|
<string name="about">Tietoa</string>
|
||||||
<string name="menu_change_key_settings">Vaihda pääsalasanaa</string>
|
<string name="menu_change_key_settings">Vaihda pääsalasanaa</string>
|
||||||
<string name="settings">Asetukset</string>
|
<string name="settings">Asetukset</string>
|
||||||
@@ -101,7 +99,6 @@
|
|||||||
<string name="menu_delete">Poista</string>
|
<string name="menu_delete">Poista</string>
|
||||||
<string name="menu_donate">Lahjoita</string>
|
<string name="menu_donate">Lahjoita</string>
|
||||||
<string name="menu_edit">Muokkaa</string>
|
<string name="menu_edit">Muokkaa</string>
|
||||||
<string name="menu_hide_password">Piilota salasana</string>
|
|
||||||
<string name="menu_lock">Lukitse tietokanta</string>
|
<string name="menu_lock">Lukitse tietokanta</string>
|
||||||
<string name="menu_open">Avaa</string>
|
<string name="menu_open">Avaa</string>
|
||||||
<string name="menu_search">Etsi</string>
|
<string name="menu_search">Etsi</string>
|
||||||
@@ -145,7 +142,7 @@
|
|||||||
<string name="edit_entry">Muokkaa merkintää</string>
|
<string name="edit_entry">Muokkaa merkintää</string>
|
||||||
<string name="error_disallow_no_credentials">Ainakin yksi pääsytieto tulee olla asetettuna.</string>
|
<string name="error_disallow_no_credentials">Ainakin yksi pääsytieto tulee olla asetettuna.</string>
|
||||||
<string name="error_load_database_KDF_memory">Avainta ei pystytty lataamaan. Kokeile vähentää KDF ”Muistin käyttöä”.</string>
|
<string name="error_load_database_KDF_memory">Avainta ei pystytty lataamaan. Kokeile vähentää KDF ”Muistin käyttöä”.</string>
|
||||||
<string name="error_load_database">Tietokantaa ei pystytty avaamaan.</string>
|
<string name="error_load_database">Tietokantaa ei pystytty lataamaan.</string>
|
||||||
<string name="error_invalid_OTP">Virheellinen OTP salaisuus.</string>
|
<string name="error_invalid_OTP">Virheellinen OTP salaisuus.</string>
|
||||||
<string name="entry_otp">OTP</string>
|
<string name="entry_otp">OTP</string>
|
||||||
<string name="otp_algorithm">Algoritmi</string>
|
<string name="otp_algorithm">Algoritmi</string>
|
||||||
@@ -173,7 +170,7 @@
|
|||||||
<string name="content_description_repeat_toggle_password_visibility">Toista salasanan näkyvyyden vaihto</string>
|
<string name="content_description_repeat_toggle_password_visibility">Toista salasanan näkyvyyden vaihto</string>
|
||||||
<string name="content_description_keyfile_checkbox">Avaintiedoston valintaruutu</string>
|
<string name="content_description_keyfile_checkbox">Avaintiedoston valintaruutu</string>
|
||||||
<string name="content_description_password_checkbox">Salasanan valintaruutu</string>
|
<string name="content_description_password_checkbox">Salasanan valintaruutu</string>
|
||||||
<string name="content_description_file_information">Tietoja tiedostosta</string>
|
<string name="content_description_file_information">Tiedoston tiedot</string>
|
||||||
<string name="content_description_add_item">Lisää esine</string>
|
<string name="content_description_add_item">Lisää esine</string>
|
||||||
<string name="content_description_add_group">Lisää ryhmä</string>
|
<string name="content_description_add_group">Lisää ryhmä</string>
|
||||||
<string name="content_description_add_entry">Lisää kohta</string>
|
<string name="content_description_add_entry">Lisää kohta</string>
|
||||||
@@ -192,7 +189,7 @@
|
|||||||
<string name="menu_appearance_settings">Ulkonäkö</string>
|
<string name="menu_appearance_settings">Ulkonäkö</string>
|
||||||
<string name="database_history">Historia</string>
|
<string name="database_history">Historia</string>
|
||||||
<string name="biometric">Biometrinen</string>
|
<string name="biometric">Biometrinen</string>
|
||||||
<string name="unavailable">Tässä salasanatietokannassa ei ole vielä pääsytietoja.</string>
|
<string name="unavailable">Ei saatavilla</string>
|
||||||
<string name="keystore_not_accessible">Avainsäilöä ei ole kunnolla alustettu.</string>
|
<string name="keystore_not_accessible">Avainsäilöä ei ole kunnolla alustettu.</string>
|
||||||
<string name="build_label">Koontiversio %1$s</string>
|
<string name="build_label">Koontiversio %1$s</string>
|
||||||
<string name="warning_database_link_revoked">Tiedostoon pääsy evätty</string>
|
<string name="warning_database_link_revoked">Tiedostoon pääsy evätty</string>
|
||||||
@@ -235,7 +232,7 @@
|
|||||||
<string name="memory_usage_explanation">Avaimen johtamisfunktion käyttämän muistin määrä.</string>
|
<string name="memory_usage_explanation">Avaimen johtamisfunktion käyttämän muistin määrä.</string>
|
||||||
<string name="memory_usage">Muistin käyttö</string>
|
<string name="memory_usage">Muistin käyttö</string>
|
||||||
<string name="kdf_explanation">Pääavain muunnetaan käyttäen satunnaista suolattua avaimen johtamisfunktiota, jotta salausalgoritmin avain voidaan generoida.</string>
|
<string name="kdf_explanation">Pääavain muunnetaan käyttäen satunnaista suolattua avaimen johtamisfunktiota, jotta salausalgoritmin avain voidaan generoida.</string>
|
||||||
<string name="encryption_explanation">Salasanatietokannan salausalgoritmi, jota käytetään kaikelle datalle.</string>
|
<string name="encryption_explanation">Salasanatietokannan salausalgoritmi, jota käytetään kaikelle datalle</string>
|
||||||
<string name="password_size_title">Generoidun salasanan pituus</string>
|
<string name="password_size_title">Generoidun salasanan pituus</string>
|
||||||
<string name="set_credential_provider_service_title">Aseta oletus automaattiselle täytölle</string>
|
<string name="set_credential_provider_service_title">Aseta oletus automaattiselle täytölle</string>
|
||||||
<string name="autofill_sign_in_prompt">Kirjaudu sisään KeePassDX:llä</string>
|
<string name="autofill_sign_in_prompt">Kirjaudu sisään KeePassDX:llä</string>
|
||||||
@@ -257,10 +254,10 @@
|
|||||||
<string name="sort_menu">Järjestä</string>
|
<string name="sort_menu">Järjestä</string>
|
||||||
<string name="parallelism_explanation">Avaimen johtamisfunktion käyttämä rinnakkaisuuden aste (eli säikeiden määrä).</string>
|
<string name="parallelism_explanation">Avaimen johtamisfunktion käyttämä rinnakkaisuuden aste (eli säikeiden määrä).</string>
|
||||||
<string name="show_recent_files_title">Näytä viimeaikaiset tiedostot</string>
|
<string name="show_recent_files_title">Näytä viimeaikaiset tiedostot</string>
|
||||||
<string name="remember_keyfile_locations_summary">Muista salasanatietokantojen avaintiedostojen sijainti</string>
|
<string name="remember_keyfile_locations_summary">Tallentaa avaintiedostojen tiedostosijannin</string>
|
||||||
<string name="remember_keyfile_locations_title">Tallenna avaintiedostojen sijainti</string>
|
<string name="remember_keyfile_locations_title">Muista avaintiedostojen tiedostosijainnin</string>
|
||||||
<string name="remember_database_locations_summary">Muista salasanatietokantojen sijainti</string>
|
<string name="remember_database_locations_summary">Tallentaa tietokantojen tiedostosijainnit</string>
|
||||||
<string name="remember_database_locations_title">Tallenna salasatietokantojen sijainti</string>
|
<string name="remember_database_locations_title">Muista tietokantojen sijainnit</string>
|
||||||
<string name="selection_mode">Valintatila</string>
|
<string name="selection_mode">Valintatila</string>
|
||||||
<string name="contains_duplicate_uuid_procedure">Ratkaise ongelma generoimalla uudet UUID:t kaksoiskappaleilla jatkaaksesi\?</string>
|
<string name="contains_duplicate_uuid_procedure">Ratkaise ongelma generoimalla uudet UUID:t kaksoiskappaleilla jatkaaksesi\?</string>
|
||||||
<string name="contains_duplicate_uuid">Tietokanta sisältää päällekkäisiä UUID-tunnuksia.</string>
|
<string name="contains_duplicate_uuid">Tietokanta sisältää päällekkäisiä UUID-tunnuksia.</string>
|
||||||
@@ -286,9 +283,9 @@
|
|||||||
<string name="menu_form_filling_settings">Lomakkeen täyttö</string>
|
<string name="menu_form_filling_settings">Lomakkeen täyttö</string>
|
||||||
<string name="copy_field">Kopio %1$s:sta</string>
|
<string name="copy_field">Kopio %1$s:sta</string>
|
||||||
<string name="creating_database">Luodaan tietokantaa…</string>
|
<string name="creating_database">Luodaan tietokantaa…</string>
|
||||||
<string name="list_groups_show_number_entries_summary">Näytä tietueiden määrä ryhmässä</string>
|
<string name="list_groups_show_number_entries_summary">Näyttää tietueiden määrän ryhmässä</string>
|
||||||
<string name="list_groups_show_number_entries_title">Näytä tietueiden määrä</string>
|
<string name="list_groups_show_number_entries_title">Näytä tietueiden määrä</string>
|
||||||
<string name="list_entries_show_username_summary">Näytä käyttäjänimet tietuelistoissa</string>
|
<string name="list_entries_show_username_summary">Näyttää käyttäjänimet tietuelistoissa</string>
|
||||||
<string name="list_entries_show_username_title">Näytä käyttäjänimet</string>
|
<string name="list_entries_show_username_title">Näytä käyttäjänimet</string>
|
||||||
<string name="invalid_db_same_uuid">%1$s samalla UUID:lla %2$s on jo olemassa.</string>
|
<string name="invalid_db_same_uuid">%1$s samalla UUID:lla %2$s on jo olemassa.</string>
|
||||||
<string name="file_not_found_content">Tiedostoa ei löytynyt. Yritä avata se uudelleen tiedostoselaimessasi.</string>
|
<string name="file_not_found_content">Tiedostoa ei löytynyt. Yritä avata se uudelleen tiedostoselaimessasi.</string>
|
||||||
@@ -367,7 +364,7 @@
|
|||||||
<string name="public_key">Julkinen avain</string>
|
<string name="public_key">Julkinen avain</string>
|
||||||
<string name="private_key">Salainen avain</string>
|
<string name="private_key">Salainen avain</string>
|
||||||
<string name="error_word_reserved">Tämä sana on varattu eikä sitä voi käyttää.</string>
|
<string name="error_word_reserved">Tämä sana on varattu eikä sitä voi käyttää.</string>
|
||||||
<string name="error_otp_type">Olemassaolevaa OTP-tyyppiä ei ole tunnistettu tällä lomakkeella, ja sen validointi ei välttämättä enää tuota oikein tokenia.</string>
|
<string name="error_otp_type">Olemassaolevaa OTP-tyyppiä ei ole tunnistettu tällä lomakkeella, ja sen validointi ei välttämättä enää tuota tokenia oikein.</string>
|
||||||
<string name="show_uuid_summary">Näyttää tietueen tai ryhmän UUID-tunnisteen</string>
|
<string name="show_uuid_summary">Näyttää tietueen tai ryhmän UUID-tunnisteen</string>
|
||||||
<string name="generate_keyfile">Luo avaintiedosto</string>
|
<string name="generate_keyfile">Luo avaintiedosto</string>
|
||||||
<string name="nodes">Solmut</string>
|
<string name="nodes">Solmut</string>
|
||||||
@@ -388,16 +385,16 @@
|
|||||||
<string name="error_database_uri_null">Tietokannan URI-osoitetta ei voi palauttaa.</string>
|
<string name="error_database_uri_null">Tietokannan URI-osoitetta ei voi palauttaa.</string>
|
||||||
<string name="error_file_to_big">Lataamasi tiedosto on liian suuri.</string>
|
<string name="error_file_to_big">Lataamasi tiedosto on liian suuri.</string>
|
||||||
<string name="error_remove_file">Virhe poistaessa tiedoston tietoja.</string>
|
<string name="error_remove_file">Virhe poistaessa tiedoston tietoja.</string>
|
||||||
<string name="error_challenge_already_requested">Haaste on jo pyydetty</string>
|
<string name="error_challenge_already_requested">Haaste on jo pyydetty.</string>
|
||||||
<string name="error_response_already_provided">Vastaus on jo annettu.</string>
|
<string name="error_response_already_provided">Vastaus on jo annettu.</string>
|
||||||
<string name="error_no_response_from_challenge">Vastausta haasteeseen ei voitu saada.</string>
|
<string name="error_no_response_from_challenge">Vastausta haasteeseen ei voitu saada.</string>
|
||||||
<string name="error_unable_merge_database_kdb">Ei voitu yhdistää kdb-tietokantatiedostoon</string>
|
<string name="error_unable_merge_database_kdb">Ei voitu yhdistää kdb-tietokantatiedostoon.</string>
|
||||||
<string name="error_location_unknown">Tietokannan sijainti ei ole tiedossa, tietokannan toimintaa ei voida suorittaa.</string>
|
<string name="error_location_unknown">Tietokannan sijainti ei ole tiedossa, tietokannan toimintaa ei voida suorittaa.</string>
|
||||||
<string name="error_hardware_key_unsupported">Laitteistoavainta ei tueta.</string>
|
<string name="error_hardware_key_unsupported">Laitteistoavainta ei tueta.</string>
|
||||||
<string name="error_empty_key">Avain ei voi olla tyhjä.</string>
|
<string name="error_empty_key">Avain ei voi olla tyhjä.</string>
|
||||||
<string name="corrupted_file">Korruptoitunut tiedosto.</string>
|
<string name="corrupted_file">Korruptoitunut tiedosto.</string>
|
||||||
<string name="passphrase">Salalause</string>
|
<string name="passphrase">Salalause</string>
|
||||||
<string name="error_registration_read_only">Uuden tietueen tallentaminen ei ole sallittua, kun tietokanta on vain lukutilassa</string>
|
<string name="error_registration_read_only">Uuden tietueen tallentaminen ei ole sallittua, kun tietokanta on Vain luku -tilassa.</string>
|
||||||
<string name="bank_name">Pankin nimi</string>
|
<string name="bank_name">Pankin nimi</string>
|
||||||
<string name="warning_permanently_delete_nodes">Poista pysyvästi valitut solmut?</string>
|
<string name="warning_permanently_delete_nodes">Poista pysyvästi valitut solmut?</string>
|
||||||
<string name="warning_database_info_changed">Tietokantatiedoston sisältämää tietoa on muutettu sovelluksen ulkopuolella.</string>
|
<string name="warning_database_info_changed">Tietokantatiedoston sisältämää tietoa on muutettu sovelluksen ulkopuolella.</string>
|
||||||
@@ -416,10 +413,10 @@
|
|||||||
<string name="menu_security_settings_summary">Salaus, avaimen johtamisfunktio</string>
|
<string name="menu_security_settings_summary">Salaus, avaimen johtamisfunktio</string>
|
||||||
<string name="master_key_settings_summary">Muutos, uudistus</string>
|
<string name="master_key_settings_summary">Muutos, uudistus</string>
|
||||||
<string name="remember_hardware_key_title">Muista laitteistoavaimet</string>
|
<string name="remember_hardware_key_title">Muista laitteistoavaimet</string>
|
||||||
<string name="remember_hardware_key_summary">Seuraa käytettyjä laitteistoavaimia</string>
|
<string name="remember_hardware_key_summary">Tallenna käytetyt laitteistoavaimet</string>
|
||||||
<string name="import_app_properties_title">Tuo sovellusasetukset</string>
|
<string name="import_app_properties_title">Tuo sovellusasetukset</string>
|
||||||
<string name="import_app_properties_summary">Valitse tiedosto tuodaksesi sovellusasetukset</string>
|
<string name="import_app_properties_summary">Valitse tiedosto tuodaksesi sovellusasetukset</string>
|
||||||
<string name="error_import_app_properties">Virhe sovellusasetuksia tuodessa</string>
|
<string name="error_import_app_properties">Virhe sovellusasetuksia tuodessa.</string>
|
||||||
<string name="filter">Suodatin</string>
|
<string name="filter">Suodatin</string>
|
||||||
<string name="warning_database_already_opened">Sulje avoinna oleva tietokanta ennen uuden avaamista</string>
|
<string name="warning_database_already_opened">Sulje avoinna oleva tietokanta ennen uuden avaamista</string>
|
||||||
<string name="warning_empty_recycle_bin">Poista pysyvästi kaikki solmut roskakorista?</string>
|
<string name="warning_empty_recycle_bin">Poista pysyvästi kaikki solmut roskakorista?</string>
|
||||||
@@ -435,6 +432,52 @@
|
|||||||
<string name="export_app_properties_title">Vie sovellusasetukset</string>
|
<string name="export_app_properties_title">Vie sovellusasetukset</string>
|
||||||
<string name="description_app_properties">KeePassDX ominaisuudet sovellusasetusten hallintaan</string>
|
<string name="description_app_properties">KeePassDX ominaisuudet sovellusasetusten hallintaan</string>
|
||||||
<string name="success_import_app_properties">Sovellusasetukset tuotu</string>
|
<string name="success_import_app_properties">Sovellusasetukset tuotu</string>
|
||||||
<string name="error_export_app_properties">Virhe sovellusasetuksia viedessä</string>
|
<string name="error_export_app_properties">Virhe sovellusasetuksia viedessä.</string>
|
||||||
<string name="success_export_app_properties">Sovellusasetukset tuotu</string>
|
<string name="success_export_app_properties">Sovellusasetukset tuotu</string>
|
||||||
|
<string name="error_otp_secret_length">Secret key on oltava vähintään %1$d merkkiä.</string>
|
||||||
|
<string name="warning_database_info_changed_options">Yhdistä tiedot, korvaa ulkoiset muutokset tallentamalla tietokanta tai lataa se uudelleen uusimmilla muutoksilla.</string>
|
||||||
|
<string name="warning_database_info_changed_options_read_only">Lataa tietokanta uudelleen uusimmilla muutoksilla.</string>
|
||||||
|
<string name="warning_database_info_reloaded">Tietokannan uudelleenlataaminen poistaa paikallisesti muokatut tiedot.</string>
|
||||||
|
<string name="warning_database_revoked">Tiedostonhallintaohjelma on peruuttanut tiedoston käyttöoikeuden. Sulje tietokanta ja avaa se uudelleen sen sijainnista.</string>
|
||||||
|
<string name="warning_exact_alarm">Et ole antanut sovellukselle lupaa käyttää tarkkaa hälytystä. Tämän seurauksena ajastinta vaativat toiminnot eivät toimi tarkalla ajalla.</string>
|
||||||
|
<string name="warning_keyfile_integrity">Tiedoston tiivisteen eheys ei ole taattu, koska Android voi muuttaa sen tietoja lennossa. Vaihda tiedostopääte .bin:ksi oikean eheyden varmistamiseksi.</string>
|
||||||
|
<string name="warning_copy_permission">Ilmoitusoikeus tarvitaan leikepöydän ilmoitusominaisuuden käyttämiseen.</string>
|
||||||
|
<string name="later">Myöhemmin</string>
|
||||||
|
<string name="ask">Kysy</string>
|
||||||
|
<string name="merge_success">Yhdistäminen suoritettu onnistuneesti</string>
|
||||||
|
<string name="permission">Lupa</string>
|
||||||
|
<string name="configure">Määritä</string>
|
||||||
|
<string name="biometric_security_update_required">Biometrinen turvallisuuspäivitys vaaditaan.</string>
|
||||||
|
<string name="unlock_and_link_biometric">Laitteen lukituksen avauslinkki</string>
|
||||||
|
<string name="device_unlock_prompt_store_credential_title">Linkki laitteen lukituksen avaamiseen</string>
|
||||||
|
<string name="device_unlock_prompt_store_credential_message">Sinun on edelleen muistettava pääkäyttäjätunnuksesi, jos käytät laitteen lukituksen avaamisen tunnistusta.</string>
|
||||||
|
<string name="device_unlock_prompt_extract_credential_title">Laitteen lukituksen tunnistus</string>
|
||||||
|
<string name="device_unlock_prompt_extract_credential_message">Hae tietokannan tunnistetiedot laitteen lukituksen avaustiedoilla</string>
|
||||||
|
<string name="device_unlock_invalid_key">Laitteen lukituksen avausavainta ei voi lukea. Poista se ja toista lukituksen tunnistamismenettely.</string>
|
||||||
|
<string name="device_unlock_not_recognized">Laitteen lukituksen avaustunnistetta ei tunnistettu</string>
|
||||||
|
<string name="device_unlock_prompt_not_initialized">Laitteen lukituksen avauskehotuksen alustaminen epäonnistui.</string>
|
||||||
|
<string name="credential_before_click_device_unlock_button">Kirjoita salasana ja napsauta sitten tätä painiketta.</string>
|
||||||
|
<string name="properties">Ominaisuudet</string>
|
||||||
|
<string name="menu_appearance_settings_summary">Teemat, värit, kuvakkeet, fontit, attribuutit</string>
|
||||||
|
<string name="device_credential">Laitteen tunnistetiedot</string>
|
||||||
|
<string name="entry_application_id">Sovelluksen ID</string>
|
||||||
|
<string name="warning_overwrite_data_title">Tallenna olemassaolevan datan päälle?</string>
|
||||||
|
<string name="warning_overwrite_data_description">Tämä toiminto korvaa tietueessa jo olemassa olevan datan, mutta voit palauttaa vanhan datan jos historia on otettu käyttöön.</string>
|
||||||
|
<string name="configure_biometric">Biometrisiä tunnistetietoja tai laitteen lukitusta ei ole otettu käyttöön.</string>
|
||||||
|
<string name="credential_provider">Tunnistetietojen tarjoaja</string>
|
||||||
|
<string name="passkeys">Pääsyavaimet</string>
|
||||||
|
<string name="passkeys_explanation_summary">Ota pääsyavaimet käyttöön nopeaan ja turvalliseen salasanattomaan kirjautumiseen</string>
|
||||||
|
<string name="passkeys_preference_title">Pääsyavainten asetukset</string>
|
||||||
|
<string name="passkeys_close_database_title">Sulje tietokanta</string>
|
||||||
|
<string name="passkeys_close_database_summary">Sulje tietokanta pääsyavaimen valitsemisen jälkeen</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">Luotetut sovellukset</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">Hallinnoi selaimia luotettujen sovellustesi listassa</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">VAROITUS: Luotettu sovellus toimii porttina tunnistautumisen alkuperän selvittämiseen. Varmista sen aitous välttääksesi tietoturvaongelmia.</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">Sovellusta ei tunnistettu</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">%1$s yrittää pääsyavaintoimintoa.\n\nLisätäänkö se luotettujen sovellusten listaan?</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">Allekirjoitus puuttuu</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">VAROITUS: Pääsyavain joko luotiin toisessa ohjelmassa tai sen allekirjoitus on poistettu. Varmista, että sovellus jonka haluat todentaa on osana samaa palvelua, ja että se on aito tietoturvaongelmien välttämiseksi.\nJos sovellus on selain, älä lisää sen allekirjoitusta tietueeseen, vaan lisää se asetuksissa luotettujen sovellusten listaan.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">%1$s on tuntematon ja se yrittää tunnistautua jo olemassaolevalla pääsyavaimella.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">Lisätäänkö sovelluksen allekirjoitus pääsyavaimen tietueeseen?</string>
|
||||||
|
<string name="passkeys_auto_select_title">Automaattinen valinta</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -218,8 +218,6 @@
|
|||||||
<string name="invalid_db_sig">Hindi makilala ang format ng database.</string>
|
<string name="invalid_db_sig">Hindi makilala ang format ng database.</string>
|
||||||
<string name="keyfile_is_empty">Walang laman ang keyfile.</string>
|
<string name="keyfile_is_empty">Walang laman ang keyfile.</string>
|
||||||
<string name="length">Haba</string>
|
<string name="length">Haba</string>
|
||||||
<string name="hide_password_title">Itago ang mga password</string>
|
|
||||||
<string name="hide_password_summary">I-mask ang mga password (***) bilang default</string>
|
|
||||||
<string name="colorize_password_title">Kulayan ang mga password</string>
|
<string name="colorize_password_title">Kulayan ang mga password</string>
|
||||||
<string name="colorize_password_summary">Kulayan ang mga password character ayon sa uri</string>
|
<string name="colorize_password_summary">Kulayan ang mga password character ayon sa uri</string>
|
||||||
<string name="list_entries_show_username_title">Ipakita ang mga username</string>
|
<string name="list_entries_show_username_title">Ipakita ang mga username</string>
|
||||||
@@ -258,7 +256,6 @@
|
|||||||
<string name="menu_paste">I-paste</string>
|
<string name="menu_paste">I-paste</string>
|
||||||
<string name="menu_delete">Burahin</string>
|
<string name="menu_delete">Burahin</string>
|
||||||
<string name="menu_cancel">Kanselahin</string>
|
<string name="menu_cancel">Kanselahin</string>
|
||||||
<string name="menu_hide_password">Itago ang password</string>
|
|
||||||
<string name="menu_lock">I-lock ang database</string>
|
<string name="menu_lock">I-lock ang database</string>
|
||||||
<string name="menu_save_database">I-save ang data</string>
|
<string name="menu_save_database">I-save ang data</string>
|
||||||
<string name="menu_merge_database">I-merge ang data</string>
|
<string name="menu_merge_database">I-merge ang data</string>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
--><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
--><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||||
<string name="feedback">Commentaires</string>
|
<string name="feedback">Commentaires</string>
|
||||||
<string name="homepage">Page d’accueil</string>
|
<string name="homepage">Page d’accueil</string>
|
||||||
<string name="about_description">Implémentation Android du gestionnaire de mots de passe KeePass</string>
|
<string name="about_description">Implémentation Android du gestionnaire de mots de passe KeePass.</string>
|
||||||
<string name="accept">Accepter</string>
|
<string name="accept">Accepter</string>
|
||||||
<string name="add_entry">Ajouter une entrée</string>
|
<string name="add_entry">Ajouter une entrée</string>
|
||||||
<string name="add_group">Ajouter un groupe</string>
|
<string name="add_group">Ajouter un groupe</string>
|
||||||
@@ -98,8 +98,6 @@
|
|||||||
<string name="list_size_summary">Taille du texte dans les éléments de liste</string>
|
<string name="list_size_summary">Taille du texte dans les éléments de liste</string>
|
||||||
<string name="loading_database">Chargement de la base de données…</string>
|
<string name="loading_database">Chargement de la base de données…</string>
|
||||||
<string name="lowercase">Minuscules</string>
|
<string name="lowercase">Minuscules</string>
|
||||||
<string name="hide_password_title">Masquer les mots de passe</string>
|
|
||||||
<string name="hide_password_summary">Masque les mots de passe (***) par défaut</string>
|
|
||||||
<string name="about">À propos</string>
|
<string name="about">À propos</string>
|
||||||
<string name="menu_change_key_settings">Modifier la clé principale</string>
|
<string name="menu_change_key_settings">Modifier la clé principale</string>
|
||||||
<string name="copy_field">%1$s copié</string>
|
<string name="copy_field">%1$s copié</string>
|
||||||
@@ -108,7 +106,6 @@
|
|||||||
<string name="menu_delete">Supprimer</string>
|
<string name="menu_delete">Supprimer</string>
|
||||||
<string name="menu_donate">Faire un don</string>
|
<string name="menu_donate">Faire un don</string>
|
||||||
<string name="menu_edit">Modifier</string>
|
<string name="menu_edit">Modifier</string>
|
||||||
<string name="menu_hide_password">Masquer le mot de passe</string>
|
|
||||||
<string name="menu_lock">Verrouiller la base de données</string>
|
<string name="menu_lock">Verrouiller la base de données</string>
|
||||||
<string name="menu_open">Ouvrir</string>
|
<string name="menu_open">Ouvrir</string>
|
||||||
<string name="menu_search">Rechercher</string>
|
<string name="menu_search">Rechercher</string>
|
||||||
@@ -165,7 +162,7 @@
|
|||||||
<string name="autofill">Remplissage automatique</string>
|
<string name="autofill">Remplissage automatique</string>
|
||||||
<string name="autofill_sign_in_prompt">Se connecter avec KeePassDX</string>
|
<string name="autofill_sign_in_prompt">Se connecter avec KeePassDX</string>
|
||||||
<string name="set_credential_provider_service_title">Définir le service de remplissage automatique par défaut</string>
|
<string name="set_credential_provider_service_title">Définir le service de remplissage automatique par défaut</string>
|
||||||
<string name="autofill_explanation_summary">Activer le remplissage automatique pour remplir rapidement des formulaires dans d’autres applications</string>
|
<string name="autofill_explanation_summary">Configurer le remplissage automatique pour remplir rapidement des formulaires dans d’autres applications</string>
|
||||||
<string name="autofill_select_entry">Sélectionner une entrée…</string>
|
<string name="autofill_select_entry">Sélectionner une entrée…</string>
|
||||||
<string name="password_size_title">Taille du mot de passe généré</string>
|
<string name="password_size_title">Taille du mot de passe généré</string>
|
||||||
<string name="password_size_summary">Défini la taille par défaut des mots de passe générés</string>
|
<string name="password_size_summary">Défini la taille par défaut des mots de passe générés</string>
|
||||||
@@ -319,7 +316,7 @@
|
|||||||
<string name="selection_mode">Mode sélection</string>
|
<string name="selection_mode">Mode sélection</string>
|
||||||
<string name="do_not_kill_app">Ne pas fermer l\'application…</string>
|
<string name="do_not_kill_app">Ne pas fermer l\'application…</string>
|
||||||
<string name="lock_database_back_root_title">Appuyer sur \"Retour\" pour verrouiller</string>
|
<string name="lock_database_back_root_title">Appuyer sur \"Retour\" pour verrouiller</string>
|
||||||
<string name="lock_database_back_root_summary">Verrouille la base de données lorsque l’utilisateur clique sur le bouton retour de l’écran racine</string>
|
<string name="lock_database_back_root_summary">Appuyer sur « Retour » pour verrouiller la base de données si vous vous trouvez sur l\'écran racine de la base de données</string>
|
||||||
<string name="clear_clipboard_notification_title">Suppression à la fermeture</string>
|
<string name="clear_clipboard_notification_title">Suppression à la fermeture</string>
|
||||||
<string name="clear_clipboard_notification_summary">Verrouille la base de données lorsque la durée du presse-papier expire ou que la notification est fermée après avoir commencé à l’utiliser</string>
|
<string name="clear_clipboard_notification_summary">Verrouille la base de données lorsque la durée du presse-papier expire ou que la notification est fermée après avoir commencé à l’utiliser</string>
|
||||||
<string name="recycle_bin">Corbeille</string>
|
<string name="recycle_bin">Corbeille</string>
|
||||||
@@ -539,7 +536,7 @@
|
|||||||
<string name="unit_mebibyte">Mébioctets</string>
|
<string name="unit_mebibyte">Mébioctets</string>
|
||||||
<string name="unit_kibibyte">Kibioctets</string>
|
<string name="unit_kibibyte">Kibioctets</string>
|
||||||
<string name="unit_byte">Octets</string>
|
<string name="unit_byte">Octets</string>
|
||||||
<string name="error_otp_type">Le type OTP existant n\'est pas reconnu par ce formulaire, sa validation peut ne plus générer correctement le jeton.</string>
|
<string name="error_otp_type">Le type OTP existant n\'est pas reconnu par ce formulaire, et sa validation peut ne plus générer correctement le jeton.</string>
|
||||||
<string name="download_canceled">Annulé !</string>
|
<string name="download_canceled">Annulé !</string>
|
||||||
<string name="icon_section_custom">Customisé</string>
|
<string name="icon_section_custom">Customisé</string>
|
||||||
<string name="icon_section_standard">Standard</string>
|
<string name="icon_section_standard">Standard</string>
|
||||||
@@ -702,4 +699,45 @@
|
|||||||
<string name="hide_templates_title">Cacher les gabarits</string>
|
<string name="hide_templates_title">Cacher les gabarits</string>
|
||||||
<string name="hide_templates_summary">Les gabarits ne sont pas affichés</string>
|
<string name="hide_templates_summary">Les gabarits ne sont pas affichés</string>
|
||||||
<string name="error_otp_secret_length">La clé secrète doit avoir ai moins %1$d caractères.</string>
|
<string name="error_otp_secret_length">La clé secrète doit avoir ai moins %1$d caractères.</string>
|
||||||
|
<string name="entry_application_id">ID d\'application</string>
|
||||||
|
<string name="warning_overwrite_data_title">Écraser les données existantes ?</string>
|
||||||
|
<string name="warning_overwrite_data_description">Cette action va remplacer la donnée existante de cette entrée, vous pourrez récupérer l\'ancienne donnée si l\'historique est activé.</string>
|
||||||
|
<string name="passkeys">Clé d\'accès</string>
|
||||||
|
<string name="passkeys_explanation_summary">Configurer des clés d\'accès pour une connexion rapide et sécurisée, sans mot de passe</string>
|
||||||
|
<string name="passkeys_preference_title">Paramètres des clés d\'accès</string>
|
||||||
|
<string name="passkeys_close_database_title">Fermer la base de données</string>
|
||||||
|
<string name="credential_provider">Fournisseur d\'authentification</string>
|
||||||
|
<string name="passkeys_close_database_summary">Fermer la base de données après la sélection d\'un mot de passe</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">Applications privilégiées</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">Gérer les navigateurs dans la liste personnalisée des applications privilégiées</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">AVERTISSEMENT : une application privilégiée sert de passerelle pour récupérer l\'origine d\'une authentification. Assurez-vous de sa légitimité afin d\'éviter tout problème de sécurité.</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">Application non reconnue</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">%1$s tente d\'effectuer une action Passkey.\n\nL\'ajouter à la liste des applications privilégiées ?</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">Signature manquante</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">AVERTISSEMENT : la clé d\'accès a été créée à partir d\'un autre client ou la signature a été supprimée. Assurez-vous que l\'application que vous souhaitez authentifier fait partie du même service et qu\'elle est légitime afin d\'éviter tout problème de sécurité.\nSi l\'application est un navigateur, n\'ajoutez pas sa signature à l\'entrée, mais à la liste des applications privilégiées dans les paramètres.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">%1$s n\'est pas reconnu et tente de s\'authentifier avec une clé d\'accès existante.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">Ajouter la signature d\'application à l\'entrée contenant la clé d\'accès ?</string>
|
||||||
|
<string name="passkeys_auto_select_title">Sélection automatique</string>
|
||||||
|
<string name="passkeys_auto_select_summary">Sélection automatique s\'il n\'y a qu\'une seule entrée et que la base de données est ouverte, uniquement si l\'application requérante est compatible</string>
|
||||||
|
<string name="passkeys_backup_eligibility_title">Admissibilité à la sauvegarde</string>
|
||||||
|
<string name="passkeys_backup_eligibility_summary">Déterminer au moment de la création si la source d\'authentification de clé publique est autorisé à une copie de sauvegarde</string>
|
||||||
|
<string name="passkeys_backup_state_title">État de sauvegarde</string>
|
||||||
|
<string name="passkeys_backup_state_summary">Indiquer que les informations d\'authentification sont sauvegardées et protégées contre la perte d\'un seul appareil</string>
|
||||||
|
<string name="credential_provider_service_subtitle">Clés d\'accès, Remplissage automatique fournisseur d\'authentification</string>
|
||||||
|
<string name="passkey">Clé d\'accès</string>
|
||||||
|
<string name="passkey_service_name">KeePassDX Fournisseur d\'Authentification</string>
|
||||||
|
<string name="passkey_creation_description">Enregistrer la clé d\'accès dans la nouvelle entrée</string>
|
||||||
|
<string name="passkey_update_description">Mettre à jour la clé d\'accès dans %1$s</string>
|
||||||
|
<string name="passkey_selection_username">Aucune clé d\'accès trouvée</string>
|
||||||
|
<string name="passkey_selection_description">Sélectionner une clé d\'accès existante</string>
|
||||||
|
<string name="passkey_database_username">Base de données KeePassDX</string>
|
||||||
|
<string name="passkey_locked_database_description">Sélectionner pour déverrouiller</string>
|
||||||
|
<string name="passkey_username">Nom d\'Utilisateur de Clé d\'Accès</string>
|
||||||
|
<string name="passkey_private_key">Clé Privée de Clé d\'Accès</string>
|
||||||
|
<string name="passkey_credential_id">Identifiant d\'Authentification de Clé d\'Accès</string>
|
||||||
|
<string name="passkey_user_handle">Identifiant Utilisateur de Clé d\'Accès</string>
|
||||||
|
<string name="passkey_relying_party">Partie Utilisatrice de Clé d\'Accès</string>
|
||||||
|
<string name="passkey_backup_eligibility">Admissibilité à la Sauvegarde de Clé d\'Accès</string>
|
||||||
|
<string name="passkey_backup_state">État de la Sauvegarde de Clé d\'Accès</string>
|
||||||
|
<string name="error_passkey_result">Impossible de restituer la clé d\'accès</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -188,7 +188,6 @@
|
|||||||
<string name="corrupted_file">Ficheiro corrompido.</string>
|
<string name="corrupted_file">Ficheiro corrompido.</string>
|
||||||
<string name="menu_keystore_remove_key">Borrar a clave do desbloqueo avanzado</string>
|
<string name="menu_keystore_remove_key">Borrar a clave do desbloqueo avanzado</string>
|
||||||
<string name="loading_database">A carregar base de datos…</string>
|
<string name="loading_database">A carregar base de datos…</string>
|
||||||
<string name="hide_password_summary">Mascarar contrasinais (***) por predefinición</string>
|
|
||||||
<string name="list_size_summary">Tamaño do texto na lista de elementos</string>
|
<string name="list_size_summary">Tamaño do texto na lista de elementos</string>
|
||||||
<string name="creating_database">A crear a base de datos…</string>
|
<string name="creating_database">A crear a base de datos…</string>
|
||||||
<string name="menu_move">Mover</string>
|
<string name="menu_move">Mover</string>
|
||||||
@@ -209,7 +208,6 @@
|
|||||||
<string name="colorize_password_title">Colorear contrasinais</string>
|
<string name="colorize_password_title">Colorear contrasinais</string>
|
||||||
<string name="colorize_password_summary">Colorear caracteres dos contrasinais por tipo</string>
|
<string name="colorize_password_summary">Colorear caracteres dos contrasinais por tipo</string>
|
||||||
<string name="settings">Configuracións</string>
|
<string name="settings">Configuracións</string>
|
||||||
<string name="menu_hide_password">Ocultar contrasinal</string>
|
|
||||||
<string name="menu_lock">Bloquear base de datos</string>
|
<string name="menu_lock">Bloquear base de datos</string>
|
||||||
<string name="menu_save_database">Gardar datos</string>
|
<string name="menu_save_database">Gardar datos</string>
|
||||||
<string name="no_results">Sen resultados da procura</string>
|
<string name="no_results">Sen resultados da procura</string>
|
||||||
@@ -278,7 +276,6 @@
|
|||||||
<string name="list_entries_show_username_title">Mostrar nomes de usuario</string>
|
<string name="list_entries_show_username_title">Mostrar nomes de usuario</string>
|
||||||
<string name="hint_keyfile">Ficheiro clave</string>
|
<string name="hint_keyfile">Ficheiro clave</string>
|
||||||
<string name="hint_pass">Contrasinal</string>
|
<string name="hint_pass">Contrasinal</string>
|
||||||
<string name="hide_password_title">Ocultar contrasinais</string>
|
|
||||||
<string name="lowercase">Minúsculas</string>
|
<string name="lowercase">Minúsculas</string>
|
||||||
<string name="about">Sobre</string>
|
<string name="about">Sobre</string>
|
||||||
<string name="copy_field">Copia de %1$s</string>
|
<string name="copy_field">Copia de %1$s</string>
|
||||||
|
|||||||
@@ -113,7 +113,6 @@
|
|||||||
<string name="list_groups_show_number_entries_summary">Prikazuje broj unosa u grupi</string>
|
<string name="list_groups_show_number_entries_summary">Prikazuje broj unosa u grupi</string>
|
||||||
<string name="creating_database">Stvaranje baze podataka …</string>
|
<string name="creating_database">Stvaranje baze podataka …</string>
|
||||||
<string name="loading_database">Učitavanje baze podataka …</string>
|
<string name="loading_database">Učitavanje baze podataka …</string>
|
||||||
<string name="hide_password_title">Sakrij lozinke</string>
|
|
||||||
<string name="menu_change_key_settings">Promjeni glavni ključ</string>
|
<string name="menu_change_key_settings">Promjeni glavni ključ</string>
|
||||||
<string name="settings">Postavke</string>
|
<string name="settings">Postavke</string>
|
||||||
<string name="menu_app_settings">Postavke aplikacije</string>
|
<string name="menu_app_settings">Postavke aplikacije</string>
|
||||||
@@ -127,7 +126,6 @@
|
|||||||
<string name="menu_copy">Kopiraj</string>
|
<string name="menu_copy">Kopiraj</string>
|
||||||
<string name="menu_paste">Umetni</string>
|
<string name="menu_paste">Umetni</string>
|
||||||
<string name="menu_delete">Izbriši</string>
|
<string name="menu_delete">Izbriši</string>
|
||||||
<string name="menu_hide_password">Sakrij lozinku</string>
|
|
||||||
<string name="menu_lock">Zaključaj bazu podataka</string>
|
<string name="menu_lock">Zaključaj bazu podataka</string>
|
||||||
<string name="menu_save_database">Spremi podatke</string>
|
<string name="menu_save_database">Spremi podatke</string>
|
||||||
<string name="menu_open">Otvori</string>
|
<string name="menu_open">Otvori</string>
|
||||||
@@ -249,7 +247,6 @@
|
|||||||
<string name="list_size_title">Veličina elemenata popisa</string>
|
<string name="list_size_title">Veličina elemenata popisa</string>
|
||||||
<string name="list_size_summary">Veličina teksta u popisu elemenata</string>
|
<string name="list_size_summary">Veličina teksta u popisu elemenata</string>
|
||||||
<string name="lowercase">Mala slova</string>
|
<string name="lowercase">Mala slova</string>
|
||||||
<string name="hide_password_summary">Standardno sakrij lozinke (***)</string>
|
|
||||||
<string name="about">O aplikaciji</string>
|
<string name="about">O aplikaciji</string>
|
||||||
<string name="copy_field">Kopija od %1$s</string>
|
<string name="copy_field">Kopija od %1$s</string>
|
||||||
<string name="menu_move">Premjesti</string>
|
<string name="menu_move">Premjesti</string>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
--><resources>
|
--><resources>
|
||||||
<string name="feedback">Visszajelzés</string>
|
<string name="feedback">Visszajelzés</string>
|
||||||
<string name="homepage">Honlap</string>
|
<string name="homepage">Weboldal</string>
|
||||||
<string name="about_description">A KeePass jelszókezelő androidos megvalósítása.</string>
|
<string name="about_description">A KeePass jelszókezelő androidos megvalósítása.</string>
|
||||||
<string name="accept">Elfogadás</string>
|
<string name="accept">Elfogadás</string>
|
||||||
<string name="add_entry">Bejegyzés hozzáadása</string>
|
<string name="add_entry">Bejegyzés hozzáadása</string>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<string name="application">Alkalmazás</string>
|
<string name="application">Alkalmazás</string>
|
||||||
<string name="menu_app_settings">Alkalmazásbeállítások</string>
|
<string name="menu_app_settings">Alkalmazásbeállítások</string>
|
||||||
<string name="brackets">Zárójelek</string>
|
<string name="brackets">Zárójelek</string>
|
||||||
<string name="file_manager_install_description">Az adatbázisfájlok létrehozásához, megnyitásához és mentéséhez szükség van egy fájlkezelőre, amely képes fogadni az ACTION_CREATE_DOCUMENT és ACTION_OPEN_DOCUMENT Intenteket.</string>
|
<string name="file_manager_install_description">Az adatbázisfájlok létrehozásához, megnyitásához és mentéséhez szükség van egy fájlkezelőre, amely képes fogadni az ACTION_CREATE_DOCUMENT és ACTION_OPEN_DOCUMENT intenteket.</string>
|
||||||
<string name="clipboard_cleared">Vágólap törölve</string>
|
<string name="clipboard_cleared">Vágólap törölve</string>
|
||||||
<string name="clipboard_error_title">Vágólaphiba</string>
|
<string name="clipboard_error_title">Vágólaphiba</string>
|
||||||
<string name="clipboard_error">Egyes eszközök nem engedik, hogy az alkalmazások használják a vágólapot.</string>
|
<string name="clipboard_error">Egyes eszközök nem engedik, hogy az alkalmazások használják a vágólapot.</string>
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<string name="entry_password">Jelszó</string>
|
<string name="entry_password">Jelszó</string>
|
||||||
<string name="save">Mentés</string>
|
<string name="save">Mentés</string>
|
||||||
<string name="entry_title">Cím</string>
|
<string name="entry_title">Cím</string>
|
||||||
<string name="entry_url">URL</string>
|
<string name="entry_url">Webcím</string>
|
||||||
<string name="entry_user_name">Felhasználónév</string>
|
<string name="entry_user_name">Felhasználónév</string>
|
||||||
<string name="error_arc4">Az Arcfour adatfolyam-titkosítás nem támogatott.</string>
|
<string name="error_arc4">Az Arcfour adatfolyam-titkosítás nem támogatott.</string>
|
||||||
<string name="error_can_not_handle_uri">Ez az URI nem kezelhető a KeePassDX-ben.</string>
|
<string name="error_can_not_handle_uri">Ez az URI nem kezelhető a KeePassDX-ben.</string>
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
<string name="hint_length">Hossz</string>
|
<string name="hint_length">Hossz</string>
|
||||||
<string name="hint_pass">Jelszó</string>
|
<string name="hint_pass">Jelszó</string>
|
||||||
<string name="password">Jelszó</string>
|
<string name="password">Jelszó</string>
|
||||||
<string name="invalid_credentials">A hitelesítő adatok nem olvashatók.</string>
|
<string name="invalid_credentials">A hitelesítési adatok nem olvashatók.</string>
|
||||||
<string name="invalid_algorithm">Hibás algoritmus.</string>
|
<string name="invalid_algorithm">Hibás algoritmus.</string>
|
||||||
<string name="invalid_db_sig">Az adatbázis formátuma nem ismerhető fel.</string>
|
<string name="invalid_db_sig">Az adatbázis formátuma nem ismerhető fel.</string>
|
||||||
<string name="keyfile_is_empty">A kulcsfájl üres.</string>
|
<string name="keyfile_is_empty">A kulcsfájl üres.</string>
|
||||||
@@ -92,8 +92,6 @@
|
|||||||
<string name="list_size_summary">Szövegméret az elemlistában</string>
|
<string name="list_size_summary">Szövegméret az elemlistában</string>
|
||||||
<string name="loading_database">Adatbázis betöltése…</string>
|
<string name="loading_database">Adatbázis betöltése…</string>
|
||||||
<string name="lowercase">Kisbetűk</string>
|
<string name="lowercase">Kisbetűk</string>
|
||||||
<string name="hide_password_title">Jelszavak elrejtése</string>
|
|
||||||
<string name="hide_password_summary">Jelszavak alapértelmezett elrejtése (***)</string>
|
|
||||||
<string name="about">Névjegy</string>
|
<string name="about">Névjegy</string>
|
||||||
<string name="menu_change_key_settings">Mesterkulcs cseréje</string>
|
<string name="menu_change_key_settings">Mesterkulcs cseréje</string>
|
||||||
<string name="settings">Beállítások</string>
|
<string name="settings">Beállítások</string>
|
||||||
@@ -101,16 +99,15 @@
|
|||||||
<string name="menu_delete">Törlés</string>
|
<string name="menu_delete">Törlés</string>
|
||||||
<string name="menu_donate">Támogatás</string>
|
<string name="menu_donate">Támogatás</string>
|
||||||
<string name="menu_edit">Szerkesztés</string>
|
<string name="menu_edit">Szerkesztés</string>
|
||||||
<string name="menu_hide_password">Jelszó elrejtése</string>
|
|
||||||
<string name="menu_lock">Adatbázis zárolása</string>
|
<string name="menu_lock">Adatbázis zárolása</string>
|
||||||
<string name="menu_open">Megnyitás</string>
|
<string name="menu_open">Megnyitás</string>
|
||||||
<string name="menu_search">Keresés</string>
|
<string name="menu_search">Keresés</string>
|
||||||
<string name="menu_showpass">Jelszó megjelenítése</string>
|
<string name="menu_showpass">Jelszó megjelenítése</string>
|
||||||
<string name="menu_url">Ugrás az URL-re</string>
|
<string name="menu_url">Ugrás a webcímre</string>
|
||||||
<string name="minus">Mínusz</string>
|
<string name="minus">Mínusz</string>
|
||||||
<string name="never">Soha</string>
|
<string name="never">Soha</string>
|
||||||
<string name="no_results">Nincs találat</string>
|
<string name="no_results">Nincs találat</string>
|
||||||
<string name="no_url_handler">Telepítsen egy webböngészőt az URL megnyitásához.</string>
|
<string name="no_url_handler">Telepítsen egy webböngészőt a webcím megnyitásához.</string>
|
||||||
<string name="progress_create">Új adatbázis létrehozása…</string>
|
<string name="progress_create">Új adatbázis létrehozása…</string>
|
||||||
<string name="progress_title">Feldolgozás…</string>
|
<string name="progress_title">Feldolgozás…</string>
|
||||||
<string name="protection">Védelem</string>
|
<string name="protection">Védelem</string>
|
||||||
@@ -127,15 +124,13 @@
|
|||||||
<string name="search">Keresés</string>
|
<string name="search">Keresés</string>
|
||||||
<string name="underline">Aláhúzás</string>
|
<string name="underline">Aláhúzás</string>
|
||||||
<string name="unsupported_db_version">Nem támogatott adatbázis-verzió.</string>
|
<string name="unsupported_db_version">Nem támogatott adatbázis-verzió.</string>
|
||||||
<string name="uppercase">Nagybetűs</string>
|
<string name="uppercase">Nagybetűk</string>
|
||||||
<string name="warning">Figyelmeztetés</string>
|
<string name="warning">Figyelmeztetés</string>
|
||||||
<string name="warning_password_encoding">Kerülje a Latin-1 karakterkészlettől eltérő jelszókaraktereket az adatbázis-fájlban (a nem felismert karakterek mert ugyanarra a betűre lesznek alakítva).</string>
|
<string name="warning_password_encoding">Kerülje a Latin-1 karakterkészlettől eltérő jelszókaraktereket az adatbázis-fájlban (a nem felismert karakterek mert ugyanarra a betűre lesznek alakítva).</string>
|
||||||
<string name="version_label">Verzió: %1$s</string>
|
<string name="version_label">Verzió: %1$s</string>
|
||||||
<string name="encrypted_value_stored">Titkosított jelszó tárolva</string>
|
<string name="encrypted_value_stored">Titkosított jelszó tárolva</string>
|
||||||
<string name="unavailable">Nem érhető el</string>
|
<string name="unavailable">Nem érhető el</string>
|
||||||
<string name="education_unlock_summary">Adja meg a jelszót és/vagy a kulcsfájlt, hogy kinyithassa az adatbázist.
|
<string name="education_unlock_summary">Adja meg a jelszót és/vagy a kulcsfájlt, hogy kinyithassa az adatbázist. \n \nBiztonsági mentés létrehozása az adatbázisról minden egyes módosítás után.</string>
|
||||||
\n
|
|
||||||
\nKészítsen biztonsági mentést az adatbázisról minden egyes módosítás után.</string>
|
|
||||||
<string-array name="list_size_options">
|
<string-array name="list_size_options">
|
||||||
<item>Kicsi</item>
|
<item>Kicsi</item>
|
||||||
<item>Közepes</item>
|
<item>Közepes</item>
|
||||||
@@ -184,8 +179,8 @@
|
|||||||
<string name="general">Általános</string>
|
<string name="general">Általános</string>
|
||||||
<string name="autofill">Automatikus kitöltés</string>
|
<string name="autofill">Automatikus kitöltés</string>
|
||||||
<string name="autofill_sign_in_prompt">Bejelentkezés a KeePassDX-szel</string>
|
<string name="autofill_sign_in_prompt">Bejelentkezés a KeePassDX-szel</string>
|
||||||
<string name="set_credential_provider_service_title">Alapértelmezett automatikus kitöltési szolgáltatás beállítása</string>
|
<string name="set_credential_provider_service_title">Hitelesítő adatkezelő szolgáltatás</string>
|
||||||
<string name="autofill_explanation_summary">Automatikus kitöltés engedélyezése az űrlapok gyors kitöltéséhez más alkalmazásokban</string>
|
<string name="autofill_explanation_summary">Automatikus kitöltés az űrlapok gyors kitöltéséhez más alkalmazásokban</string>
|
||||||
<string name="password_size_title">Előállított jelszó mérete</string>
|
<string name="password_size_title">Előállított jelszó mérete</string>
|
||||||
<string name="password_size_summary">Beállítja az előállított jelszavak alapértelmezett méretét</string>
|
<string name="password_size_summary">Beállítja az előállított jelszavak alapértelmezett méretét</string>
|
||||||
<string name="list_password_generator_options_title">Jelszó karakterek</string>
|
<string name="list_password_generator_options_title">Jelszó karakterek</string>
|
||||||
@@ -193,7 +188,7 @@
|
|||||||
<string name="clipboard">Vágólap</string>
|
<string name="clipboard">Vágólap</string>
|
||||||
<string name="clipboard_notifications_title">Vágólap-értesítések</string>
|
<string name="clipboard_notifications_title">Vágólap-értesítések</string>
|
||||||
<string name="clipboard_notifications_summary">Vágólap-értesítések megjelenítése a bejegyzésmezők másolásakor</string>
|
<string name="clipboard_notifications_summary">Vágólap-értesítések megjelenítése a bejegyzésmezők másolásakor</string>
|
||||||
<string name="clipboard_warning">Ha a vágólap automatikus törlése meghiúsul, akkor törölje kézzel az előzményeket.</string>
|
<string name="clipboard_warning">Ha a vágólap automatikus törlése meghiúsul, akkor törölje saját kezűleg az előzményeket.</string>
|
||||||
<string name="lock">Zárolás</string>
|
<string name="lock">Zárolás</string>
|
||||||
<string name="lock_database_screen_off_title">Képernyőzár</string>
|
<string name="lock_database_screen_off_title">Képernyőzár</string>
|
||||||
<string name="lock_database_screen_off_summary">Az adatbázis zárolása néhány másodperc után, ha a képernyő kikapcsol</string>
|
<string name="lock_database_screen_off_summary">Az adatbázis zárolása néhány másodperc után, ha a képernyő kikapcsol</string>
|
||||||
@@ -217,7 +212,7 @@
|
|||||||
<string name="allow_copy_password_warning">Figyelmeztetés: A vágólapon osztozik az összes alkalmazás. Ha érzékeny adatokat másol, akkor a többi szoftver is hozzáférhet.</string>
|
<string name="allow_copy_password_warning">Figyelmeztetés: A vágólapon osztozik az összes alkalmazás. Ha érzékeny adatokat másol, akkor a többi szoftver is hozzáférhet.</string>
|
||||||
<string name="database_name_title">Adatbázis neve</string>
|
<string name="database_name_title">Adatbázis neve</string>
|
||||||
<string name="database_description_title">Adatbázis leírása</string>
|
<string name="database_description_title">Adatbázis leírása</string>
|
||||||
<string name="database_version_title">Adatbázis verzió</string>
|
<string name="database_version_title">Adatbázis-verzió</string>
|
||||||
<string name="text_appearance">Szöveg</string>
|
<string name="text_appearance">Szöveg</string>
|
||||||
<string name="application_appearance">Felhasználói felület</string>
|
<string name="application_appearance">Felhasználói felület</string>
|
||||||
<string name="other">Egyéb</string>
|
<string name="other">Egyéb</string>
|
||||||
@@ -243,7 +238,7 @@
|
|||||||
<string name="keyboard_key_vibrate_title">Rezgés gombnyomáskor</string>
|
<string name="keyboard_key_vibrate_title">Rezgés gombnyomáskor</string>
|
||||||
<string name="keyboard_key_sound_title">Hang gombnyomáskor</string>
|
<string name="keyboard_key_sound_title">Hang gombnyomáskor</string>
|
||||||
<string name="allow_no_password_title">Mesterkulcs elhagyásának engedélyezése</string>
|
<string name="allow_no_password_title">Mesterkulcs elhagyásának engedélyezése</string>
|
||||||
<string name="allow_no_password_summary">A „Megnyitás” gomb engedélyezése, ha nincsenek hitelesítő adatok kiválasztva</string>
|
<string name="allow_no_password_summary">A „Megnyitás” gomb engedélyezése, ha nincsenek hitelesítési adatok kiválasztva</string>
|
||||||
<string name="enable_education_screens_title">Oktatóképernyők</string>
|
<string name="enable_education_screens_title">Oktatóképernyők</string>
|
||||||
<string name="enable_education_screens_summary">Elemek kiemelése, hogy megtudja hogyan működik az alkalmazás</string>
|
<string name="enable_education_screens_summary">Elemek kiemelése, hogy megtudja hogyan működik az alkalmazás</string>
|
||||||
<string name="reset_education_screens_title">Oktatóképernyők visszaállítása</string>
|
<string name="reset_education_screens_title">Oktatóképernyők visszaállítása</string>
|
||||||
@@ -278,18 +273,18 @@
|
|||||||
<string name="education_lock_summary">Zárolja gyorsan az adatbázist, beállíthatja hogy az alkalmazás lezárja egy idő után, valamint ha a képernyőt kikapcsolja.</string>
|
<string name="education_lock_summary">Zárolja gyorsan az adatbázist, beállíthatja hogy az alkalmazás lezárja egy idő után, valamint ha a képernyőt kikapcsolja.</string>
|
||||||
<string name="education_sort_title">Elemek rendezése</string>
|
<string name="education_sort_title">Elemek rendezése</string>
|
||||||
<string name="education_sort_summary">Válasszon, hogy az elemek és csoportok hogyan legyenek rendezve.</string>
|
<string name="education_sort_summary">Válasszon, hogy az elemek és csoportok hogyan legyenek rendezve.</string>
|
||||||
<string name="education_donation_title">Közreműködés</string>
|
<string name="education_donation_title">Részvétel</string>
|
||||||
<string name="education_entry_edit_summary">Szerkessze a bejegyzése egyéni mezőit. Az adatok hivatkozhatóak a különböző mezők között.</string>
|
<string name="education_entry_edit_summary">Szerkessze a bejegyzése egyéni mezőit. Az adatok hivatkozhatóak a különböző mezők között.</string>
|
||||||
<string name="education_donation_summary">Segítsen a stabilitás és a biztonság növelésében, és az új funkciók hozzáadásában.</string>
|
<string name="education_donation_summary">Segítsen a stabilitás és a biztonság növelésében, és az új funkciók hozzáadásában.</string>
|
||||||
<string name="html_text_ad_free">Számos más jelszókezelő alkalmazással ellentétben, ez egy <strong>reklámmentes</strong>, <strong>copyleft licencelésű szabad szoftver</strong>, amely nem gyűjt személyes adatokat a kiszolgálókon, bármelyik verziót is használja.</string>
|
<string name="html_text_ad_free">Számos más jelszókezelő alkalmazással ellentétben, ez egy <strong>reklámmentes</strong>, <strong>copyleft licencelésű szabad szoftver</strong>, amely nem gyűjt személyes adatokat a kiszolgálókon, bármelyik verziót is használja.</string>
|
||||||
<string name="html_text_buy_pro">A pro verzió megvásárlásával hozzáférést kap ehhez a <strong>vizuális stílushoz</strong>, és segít a <strong>közösségi projektek megvalósulásában.</strong></string>
|
<string name="html_text_buy_pro">A pro verzió megvásárlásával hozzáférést kap ehhez a <strong>vizuális stílushoz</strong>, és segít a <strong>közösségi projektek megvalósulásában.</strong></string>
|
||||||
<string name="html_text_feature_generosity">Ez a <strong>vizuális stílus</strong> az Ön nagylelkűségének köszönhetően érhető el.</string>
|
<string name="html_text_feature_generosity">Ez a <strong>vizuális stílus</strong> az Ön nagylelkűségének köszönhetően érhető el.</string>
|
||||||
<string name="html_text_donation">Azzal, hogy <strong>hozzájárul</strong> a projekthez <i>(pénzzel, kóddal, fordítással)</i>, segít abban, hogy a projekt tovább éljen és gyarapodjon, továbbá Ön jogosulttá válik a <strong>téma</strong>feloldásának lehetősége.</string>
|
<string name="html_text_donation">Azzal, hogy <strong>hozzájárul</strong> a projekthez <i>(pénzzel, kóddal, fordítással)</i>, segít abban, hogy a projekt tovább éljen és gyarapodjon, továbbá Ön jogosulttá válik a <strong>téma</strong>feloldásának lehetősége.</string>
|
||||||
<string name="html_text_dev_feature">Ez a funkció <strong>fejlesztés alatt áll</strong>, és az Ön <strong>támogatására</strong> van szükség, hogy hamarosan elérhető legyen.</string>
|
<string name="html_text_dev_feature">Ez a funkció <strong>fejlesztés alatt áll</strong>, és az Ön <strong>közreműködésére</strong> van szükség, hogy hamarosan elérhető legyen.</string>
|
||||||
<string name="html_text_dev_feature_buy_pro">A <strong>pro</strong> verzió megvásárlásával,</string>
|
<string name="html_text_dev_feature_buy_pro">A <strong>pro</strong> verzió megvásárlásával,</string>
|
||||||
<string name="html_text_dev_feature_contibute">A <strong>támogatással</strong></string>
|
<string name="html_text_dev_feature_contibute">A <strong>közreműködésével</strong>,</string>
|
||||||
<string name="html_text_dev_feature_encourage">arra ösztönzi a fejlesztőket, hogy <strong>új funkciókat</strong> készítsenek, és <strong>hibákat javítsanak</strong> az észrevételei alapján.</string>
|
<string name="html_text_dev_feature_encourage">arra ösztönzi a fejlesztőket, hogy <strong>új funkciókat</strong> hozzanak létre, és <strong>hibákat javítsanak</strong> az észrevételei alapján.</string>
|
||||||
<string name="html_text_dev_feature_thanks">Köszönjük a támogatását.</string>
|
<string name="html_text_dev_feature_thanks">Köszönjük a közreműködését.</string>
|
||||||
<string name="html_text_dev_feature_work_hard">Keményen dolgozunk, hogy gyorsan kiadjuk ezt a funkciót.</string>
|
<string name="html_text_dev_feature_work_hard">Keményen dolgozunk, hogy gyorsan kiadjuk ezt a funkciót.</string>
|
||||||
<string name="html_text_dev_feature_upgrade">Ne felejtse naprakészen tartani az alkalmazást az új verziók telepítésével.</string>
|
<string name="html_text_dev_feature_upgrade">Ne felejtse naprakészen tartani az alkalmazást az új verziók telepítésével.</string>
|
||||||
<string name="download">Letöltés</string>
|
<string name="download">Letöltés</string>
|
||||||
@@ -315,7 +310,7 @@
|
|||||||
<string name="clear_clipboard_notification_title">Zárolás bezáráskor</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>
|
<string name="lock_database_show_button_summary">Megjeleníti a zárolás gombot a felhasználói felületen</string>
|
||||||
<string name="lock_database_show_button_title">Zárolás gomb megjelenítése</string>
|
<string name="lock_database_show_button_title">Zárolás gomb megjelenítése</string>
|
||||||
<string name="lock_database_back_root_summary">Az adatbázis zárolása, ha a felhasználó a vissza gombra kattint az indítóképernyőn</string>
|
<string name="lock_database_back_root_summary">Nyomja meg a „Vissza” gombot az adatbázis zárolásához, ha az adatbázis indítóképernyőjén tartózkodik</string>
|
||||||
<string name="autofill_preference_title">Automatikus kitöltés beállításai</string>
|
<string name="autofill_preference_title">Automatikus kitöltés beállításai</string>
|
||||||
<string name="warning_database_link_revoked">A fájlhoz történő hozzáférést visszavonta a fájlkezelő</string>
|
<string name="warning_database_link_revoked">A fájlhoz történő hozzáférést visszavonta a fájlkezelő</string>
|
||||||
<string name="warning_database_read_only">Fájlírási-hozzáférés megadása az adatbázis-változások mentéséhez</string>
|
<string name="warning_database_read_only">Fájlírási-hozzáférés megadása az adatbázis-változások mentéséhez</string>
|
||||||
@@ -330,7 +325,7 @@
|
|||||||
<string name="auto_focus_search_summary">Keresés kérése az adatbázis megnyitásakor</string>
|
<string name="auto_focus_search_summary">Keresés kérése az adatbázis megnyitásakor</string>
|
||||||
<string name="error_create_database">Az adatbázisfájl létrehozása sikertelen.</string>
|
<string name="error_create_database">Az adatbázisfájl létrehozása sikertelen.</string>
|
||||||
<string name="error_label_exists">Ez a címke már létezik.</string>
|
<string name="error_label_exists">Ez a címke már létezik.</string>
|
||||||
<string name="html_about_contribution">A <strong>szabadságunk megtartása</strong>, a <strong>hibák javítása</strong>, a <strong>funkciók hozzáadása</strong> és az <strong>állandó aktivitás</strong> érdekében számítunk a <strong>támogatására</strong>.</string>
|
<string name="html_about_contribution">A <strong>szabadságunk megtartása</strong>, a <strong>hibák javítása</strong>, a <strong>funkciók hozzáadása</strong> és az <strong>állandó aktivitás</strong> érdekében számítunk a <strong>közreműködésére</strong>.</string>
|
||||||
<string name="entry_add_attachment">Melléklet hozzáadása</string>
|
<string name="entry_add_attachment">Melléklet hozzáadása</string>
|
||||||
<string name="entry_add_field">Mező hozzáadása</string>
|
<string name="entry_add_field">Mező hozzáadása</string>
|
||||||
<string name="entry_password_generator">Jelszógenerátor</string>
|
<string name="entry_password_generator">Jelszógenerátor</string>
|
||||||
@@ -355,7 +350,7 @@
|
|||||||
<string name="enable_auto_save_database_summary">Az adatbázis mentése minden fontos művelet után („Módosítható” módban)</string>
|
<string name="enable_auto_save_database_summary">Az adatbázis mentése minden fontos művelet után („Módosítható” módban)</string>
|
||||||
<string name="enable_auto_save_database_title">Adatbázis automatikus mentése</string>
|
<string name="enable_auto_save_database_title">Adatbázis automatikus mentése</string>
|
||||||
<string name="recycle_bin_group_title">Kuka csoportja</string>
|
<string name="recycle_bin_group_title">Kuka csoportja</string>
|
||||||
<string name="keystore_not_accessible">Az kulcstár nincs helyesen előkészítve.</string>
|
<string name="keystore_not_accessible">A KeyStore nincs helyesen előkészítve.</string>
|
||||||
<string name="warning_permanently_delete_nodes">Biztos, hogy végleg törli a kiválasztott csomópontokat\?</string>
|
<string name="warning_permanently_delete_nodes">Biztos, hogy végleg törli a kiválasztott csomópontokat\?</string>
|
||||||
<string name="command_execution">Parancs végrehajtása…</string>
|
<string name="command_execution">Parancs végrehajtása…</string>
|
||||||
<string name="menu_empty_recycle_bin">Kuka ürítése</string>
|
<string name="menu_empty_recycle_bin">Kuka ürítése</string>
|
||||||
@@ -393,7 +388,7 @@
|
|||||||
<string name="error_otp_counter">A számlálónak %1$d és %2$d között kell lennie.</string>
|
<string name="error_otp_counter">A számlálónak %1$d és %2$d között kell lennie.</string>
|
||||||
<string name="error_otp_secret_key">A titkos kulcsnak Base32 formátumban kell lennie.</string>
|
<string name="error_otp_secret_key">A titkos kulcsnak Base32 formátumban kell lennie.</string>
|
||||||
<string name="error_copy_group_here">Csoport nem másolható ide.</string>
|
<string name="error_copy_group_here">Csoport nem másolható ide.</string>
|
||||||
<string name="error_disallow_no_credentials">Legalább egy hitelesíti módot be kell állítani.</string>
|
<string name="error_disallow_no_credentials">Legalább egy hitelesítési módot be kell állítani.</string>
|
||||||
<string name="error_invalid_OTP">Érvénytelen OTP titok.</string>
|
<string name="error_invalid_OTP">Érvénytelen OTP titok.</string>
|
||||||
<string name="entry_otp">OTP</string>
|
<string name="entry_otp">OTP</string>
|
||||||
<string name="otp_algorithm">Algoritmus</string>
|
<string name="otp_algorithm">Algoritmus</string>
|
||||||
@@ -421,7 +416,7 @@
|
|||||||
<string name="content_description_password_length">Jelszó hossza</string>
|
<string name="content_description_password_length">Jelszó hossza</string>
|
||||||
<string name="content_description_entry_icon">Bejegyzésikon</string>
|
<string name="content_description_entry_icon">Bejegyzésikon</string>
|
||||||
<string name="content_description_repeat_toggle_password_visibility">Jelszó láthatóságának átváltásának megismétlése</string>
|
<string name="content_description_repeat_toggle_password_visibility">Jelszó láthatóságának átváltásának megismétlése</string>
|
||||||
<string name="content_description_node_children">Csomópont gyermekei</string>
|
<string name="content_description_node_children">Alárendelt csomópontok</string>
|
||||||
<string name="delete_entered_password_summary">Törli a beírt jelszót az adatbázishoz való kapcsolódási kísérlet után</string>
|
<string name="delete_entered_password_summary">Törli a beírt jelszót az adatbázishoz való kapcsolódási kísérlet után</string>
|
||||||
<string name="delete_entered_password_title">Jelszó törlése</string>
|
<string name="delete_entered_password_title">Jelszó törlése</string>
|
||||||
<string name="keyboard_selection_entry_summary">A Mágikus billentyűzet kitöltése azzal a bejegyzéssel, amelyet megtekint a KeePassDX-ben</string>
|
<string name="keyboard_selection_entry_summary">A Mágikus billentyűzet kitöltése azzal a bejegyzéssel, amelyet megtekint a KeePassDX-ben</string>
|
||||||
@@ -430,7 +425,7 @@
|
|||||||
<string name="lock_database_back_root_title">Nyomja meg a „Vissza” gombot a zároláshoz</string>
|
<string name="lock_database_back_root_title">Nyomja meg a „Vissza” gombot a zároláshoz</string>
|
||||||
<string name="do_not_kill_app">Ne lője ki az alkalmazást…</string>
|
<string name="do_not_kill_app">Ne lője ki az alkalmazást…</string>
|
||||||
<string name="selection_mode">Kiválasztási mód</string>
|
<string name="selection_mode">Kiválasztási mód</string>
|
||||||
<string name="keyboard_previous_database_credentials_title">Adatbázis-hitelesítőadatok képernyője</string>
|
<string name="keyboard_previous_database_credentials_title">Adatbázis-hitelesítő képernyő</string>
|
||||||
<string name="keyboard_change">Billentyűzet váltása</string>
|
<string name="keyboard_change">Billentyűzet váltása</string>
|
||||||
<string name="keyboard_save_search_info_title">Megosztott információk mentése</string>
|
<string name="keyboard_save_search_info_title">Megosztott információk mentése</string>
|
||||||
<string name="notification">Értesítés</string>
|
<string name="notification">Értesítés</string>
|
||||||
@@ -438,7 +433,7 @@
|
|||||||
<string name="database_data_remove_unlinked_attachments_title">Nem összekapcsolt adatok eltávolítása</string>
|
<string name="database_data_remove_unlinked_attachments_title">Nem összekapcsolt adatok eltávolítása</string>
|
||||||
<string name="data">Adatok</string>
|
<string name="data">Adatok</string>
|
||||||
<string name="biometric_security_update_required">Biometrikus biztonsági frissítés szükséges.</string>
|
<string name="biometric_security_update_required">Biometrikus biztonsági frissítés szükséges.</string>
|
||||||
<string name="configure_biometric">Nincs biometrikus vagy eszközazonosító beállítva.</string>
|
<string name="configure_biometric">Nincs biometrikus vagy eszközhitelesítési adat beállítva.</string>
|
||||||
<string name="warning_empty_keyfile_explanation">A kulcsfájl tartalmának sosem szabad megváltoznia, és a legjobb esetben véletlenszerűen előállított adatokat kellene tartalmaznia.</string>
|
<string name="warning_empty_keyfile_explanation">A kulcsfájl tartalmának sosem szabad megváltoznia, és a legjobb esetben véletlenszerűen előállított adatokat kellene tartalmaznia.</string>
|
||||||
<string name="warning_empty_keyfile">Nem ajánlott, hogy üres kulcsfájl adjon hozzá.</string>
|
<string name="warning_empty_keyfile">Nem ajánlott, hogy üres kulcsfájl adjon hozzá.</string>
|
||||||
<string name="warning_sure_remove_data">Mindenképp törli ezeket az adatokat\?</string>
|
<string name="warning_sure_remove_data">Mindenképp törli ezeket az adatokat\?</string>
|
||||||
@@ -456,7 +451,7 @@
|
|||||||
<string name="subdomain_search_title">Aldomain keresés</string>
|
<string name="subdomain_search_title">Aldomain keresés</string>
|
||||||
<string name="error_registration_read_only">Az új elem mentése nem engedélyezett írásvédett adatbázisban.</string>
|
<string name="error_registration_read_only">Az új elem mentése nem engedélyezett írásvédett adatbázisban.</string>
|
||||||
<string name="error_string_type">A szöveg nem egyezik a kért elemmel.</string>
|
<string name="error_string_type">A szöveg nem egyezik a kért elemmel.</string>
|
||||||
<string name="content_description_credentials_information">Hitelesítő adatok információi</string>
|
<string name="content_description_credentials_information">Hitelesítési adatok információi</string>
|
||||||
<string name="content_description_add_item">Elem hozzáadása</string>
|
<string name="content_description_add_item">Elem hozzáadása</string>
|
||||||
<string name="export_app_properties_summary">Fájl létrehozása az alkalmazásbeállítások exportálásához</string>
|
<string name="export_app_properties_summary">Fájl létrehozása az alkalmazásbeállítások exportálásához</string>
|
||||||
<string name="export_app_properties_title">Alkalmazásbeállítások exportálása</string>
|
<string name="export_app_properties_title">Alkalmazásbeállítások exportálása</string>
|
||||||
@@ -473,7 +468,7 @@
|
|||||||
<string name="error_rebuild_list">A lista újbóli összeállítása sikertelen.</string>
|
<string name="error_rebuild_list">A lista újbóli összeállítása sikertelen.</string>
|
||||||
<string name="error_database_uri_null">Az adatbázis URI nem kérhető le.</string>
|
<string name="error_database_uri_null">Az adatbázis URI nem kérhető le.</string>
|
||||||
<string name="error_field_name_already_exists">A mezőnév már létezik.</string>
|
<string name="error_field_name_already_exists">A mezőnév már létezik.</string>
|
||||||
<string name="error_otp_type">A meglévő OTP típus nem ismert ebben a formában, a kiértékelése nem biztos, hogy helyes tokent fog előállítani.</string>
|
<string name="error_otp_type">A meglévő OTP típus nem felismerhető ebben az űrlapban, és érvényesítése már nem biztosítja a token helyes generálását.</string>
|
||||||
<string name="error_word_reserved">Ez egy foglalt szó, és nem használható.</string>
|
<string name="error_word_reserved">Ez egy foglalt szó, és nem használható.</string>
|
||||||
<string name="version">Verzió</string>
|
<string name="version">Verzió</string>
|
||||||
<string name="template">Sablon</string>
|
<string name="template">Sablon</string>
|
||||||
@@ -518,25 +513,25 @@
|
|||||||
<string name="autofill_select_entry">Válasszon bejegyzést…</string>
|
<string name="autofill_select_entry">Válasszon bejegyzést…</string>
|
||||||
<string name="warning_database_info_changed_options">Adatok egyesítése, majd a külső módosítások felülírása az adatbázis mentésével, vagy újratöltés a legfrissebb változtatásokkal.</string>
|
<string name="warning_database_info_changed_options">Adatok egyesítése, majd a külső módosítások felülírása az adatbázis mentésével, vagy újratöltés a legfrissebb változtatásokkal.</string>
|
||||||
<string name="device_unlock_prompt_store_credential_title">Hivatkozás az eszköz feloldásához</string>
|
<string name="device_unlock_prompt_store_credential_title">Hivatkozás az eszköz feloldásához</string>
|
||||||
<string name="device_unlock_prompt_store_credential_message">Továbbra is meg kell jegyeznie a széf fő jelszavát, ha az eszköz feloldásfelismerését használja.</string>
|
<string name="device_unlock_prompt_store_credential_message">Ha az eszköz feloldásának felismerését használja, akkor is meg kell jegyeznie a széf fő hitelesítési adatait.</string>
|
||||||
<string name="device_unlock_prompt_extract_credential_message">Adatbázis hitelesítő adatainak kinyerése az eszköz adatfeloldásával</string>
|
<string name="device_unlock_prompt_extract_credential_message">Adatbázis hitelesítési adatainak kinyerése az eszköz feloldási adatok alapján</string>
|
||||||
<string name="device_unlock_invalid_key">Az eszközfeloldási kulcs nem olvasható. Törölje, és ismételje meg a feloldásfelismerési folyamatot.</string>
|
<string name="device_unlock_invalid_key">Az eszközfeloldási kulcs nem olvasható. Törölje, és ismételje meg a feloldásfelismerési folyamatot.</string>
|
||||||
<string name="device_unlock_not_recognized">Az eszközfeloldási ujjlenyomat nem ismerhető fel</string>
|
<string name="device_unlock_not_recognized">Az eszközfeloldási ujjlenyomat nem ismerhető fel</string>
|
||||||
<string name="device_unlock_prompt_not_initialized">Az eszközfeloldási képernyő előkészítése sikertelen.</string>
|
<string name="device_unlock_prompt_not_initialized">Az eszközfeloldási képernyő előkészítése sikertelen.</string>
|
||||||
<string name="properties">Tulajdonságok</string>
|
<string name="properties">Tulajdonságok</string>
|
||||||
<string name="device_credential">Eszköz hitelesítő adatai</string>
|
<string name="device_credential">Eszköz hitelesítési adatai</string>
|
||||||
<string name="temp_device_unlock_enable_summary">Ne tároljon semmilyen titkosított tartalmat az eszközfeloldás használatához</string>
|
<string name="temp_device_unlock_enable_summary">Ne tároljon semmilyen titkosított tartalmat az eszközfeloldás használatához</string>
|
||||||
<string name="temp_device_unlock_timeout_title">Eszközfeloldás lejárati ideje</string>
|
<string name="temp_device_unlock_timeout_title">Eszközfeloldás lejárati ideje</string>
|
||||||
<string name="warning_database_revoked">A fájl elérését visszavonta a fájlkezelő, zárja be az adatbázist és nyissa meg újra a helyéről.</string>
|
<string name="warning_database_revoked">A fájl elérését visszavonta a fájlkezelő, zárja be az adatbázist és nyissa meg újra a helyéről.</string>
|
||||||
<string name="warning_exact_alarm">Nem engedélyezte az alkalmazásnak, hogy pontos riasztást használjon. Ezért az időzítőt használó funkciók nem a pontos időt fogják használni.</string>
|
<string name="warning_exact_alarm">Nem engedélyezte az alkalmazásnak, hogy pontos riasztást használjon. Ezért az időzítőt használó funkciók nem a pontos időt fogják használni.</string>
|
||||||
<string name="device_unlock_prompt_extract_credential_title">Eszköz feloldásfelismerése</string>
|
<string name="device_unlock_prompt_extract_credential_title">Eszköz feloldásának felismerése</string>
|
||||||
<string name="credential_before_click_device_unlock_button">Írja be a jelszót, majd kattintson erre a gombra.</string>
|
<string name="credential_before_click_device_unlock_button">Írja be a jelszót, majd kattintson erre a gombra.</string>
|
||||||
<string name="temp_device_unlock_enable_title">Ideiglenes eszközfeloldás</string>
|
<string name="temp_device_unlock_enable_title">Ideiglenes eszközfeloldás</string>
|
||||||
<string name="permission">Engedély</string>
|
<string name="permission">Engedély</string>
|
||||||
<string name="content">Tartalom</string>
|
<string name="content">Tartalom</string>
|
||||||
<string name="device_unlock_tap_delete">Koppintson az eszközfeloldási kulcsok törléséhez</string>
|
<string name="device_unlock_tap_delete">Koppintson az eszközfeloldási kulcsok törléséhez</string>
|
||||||
<string name="device_credential_unlock_enable_title">Eszköz hitelesítő adataival történő feloldás</string>
|
<string name="device_credential_unlock_enable_title">Eszköz hitelesítési adataival történő feloldás</string>
|
||||||
<string name="device_credential_unlock_enable_summary">Lehetővé teszi, hogy az eszköz hitelesítő adataival nyissa meg az adatbázist</string>
|
<string name="device_credential_unlock_enable_summary">Lehetővé teszi, hogy az eszköz hitelesítési adataival nyissa meg az adatbázist</string>
|
||||||
<string name="autofill_application_id_blocklist_title">Letiltott alkalmazások</string>
|
<string name="autofill_application_id_blocklist_title">Letiltott alkalmazások</string>
|
||||||
<string name="menu_merge_database">Adatok egyesítése</string>
|
<string name="menu_merge_database">Adatok egyesítése</string>
|
||||||
<string name="autofill_close_database_title">Adatbázis bezárása</string>
|
<string name="autofill_close_database_title">Adatbázis bezárása</string>
|
||||||
@@ -576,7 +571,7 @@
|
|||||||
<string name="show_uuid_summary">Megjeleníti a bejegyzéshez vagy csoporthoz tartozó UUID-t</string>
|
<string name="show_uuid_summary">Megjeleníti a bejegyzéshez vagy csoporthoz tartozó UUID-t</string>
|
||||||
<string name="show_otp_token_title">OTP token megjelenítése</string>
|
<string name="show_otp_token_title">OTP token megjelenítése</string>
|
||||||
<string name="device_unlock_delete_all_key_warning">Törli az összes, az eszközfeloldás-felismeréshez tartozó titkosítási kulcsot\?</string>
|
<string name="device_unlock_delete_all_key_warning">Törli az összes, az eszközfeloldás-felismeréshez tartozó titkosítási kulcsot\?</string>
|
||||||
<string name="keyboard_save_search_info_summary">A könnyebb jövőbeli használat érdekében próbálja menteni a megosztott információkat a kézi bejegyzéskiválasztásnál</string>
|
<string name="keyboard_save_search_info_summary">A könnyebb jövőbeli használat érdekében próbálja menteni a megosztott információkat a kézi bejegyzés kiválasztásánál</string>
|
||||||
<string name="custom_fields">Egyéni mezők</string>
|
<string name="custom_fields">Egyéni mezők</string>
|
||||||
<string name="back_to_previous_keyboard">Vissza az előző billentyűzethez</string>
|
<string name="back_to_previous_keyboard">Vissza az előző billentyűzethez</string>
|
||||||
<string name="upload_attachment">%1$s feltöltése</string>
|
<string name="upload_attachment">%1$s feltöltése</string>
|
||||||
@@ -587,10 +582,10 @@
|
|||||||
<string name="autofill_web_domain_blocklist_title">Letiltott webes domainek</string>
|
<string name="autofill_web_domain_blocklist_title">Letiltott webes domainek</string>
|
||||||
<string name="autofill_ask_to_save_data_title">Adatok mentésének kérése</string>
|
<string name="autofill_ask_to_save_data_title">Adatok mentésének kérése</string>
|
||||||
<string name="education_device_unlock_title">Eszközadatbázis feloldása</string>
|
<string name="education_device_unlock_title">Eszközadatbázis feloldása</string>
|
||||||
<string name="education_device_unlock_summary">A jelszó összekötése a leolvasott biometrikus adatokkal vagy eszköz-hitelesítőadatokkal, hogy gyorsan feloldhassa az adatbázist.</string>
|
<string name="education_device_unlock_summary">A jelszó összekapcsolása a leolvasott biometrikus adatokkal vagy eszközhitelesítő adatokkal, hogy gyorsan feloldhassa az adatbázist.</string>
|
||||||
<string name="show_otp_token_summary">Megjeleníti az OTP tokeneket a bejegyzések között</string>
|
<string name="show_otp_token_summary">Megjeleníti az OTP tokeneket a bejegyzések között</string>
|
||||||
<string name="education_add_attachment_title">Melléklet hozzáadása</string>
|
<string name="education_add_attachment_title">Melléklet hozzáadása</string>
|
||||||
<string name="keyboard_previous_database_credentials_summary">Automatikus visszaváltás az előző billentyűzetre az adatbázis-hitelesítőadatok képernyőn</string>
|
<string name="keyboard_previous_database_credentials_summary">Automatikus visszaváltás az előző billentyűzetre az adatbázis-hitelesítő adatok képernyőjén</string>
|
||||||
<string name="warning_database_info_reloaded">Az adatbázis újratöltése törli a helyileg módosított adatokat.</string>
|
<string name="warning_database_info_reloaded">Az adatbázis újratöltése törli a helyileg módosított adatokat.</string>
|
||||||
<string name="templates">Sablonok</string>
|
<string name="templates">Sablonok</string>
|
||||||
<string name="error_no_response_from_challenge">Nem kérhető le a válasz a kihívásból.</string>
|
<string name="error_no_response_from_challenge">Nem kérhető le a válasz a kihívásból.</string>
|
||||||
@@ -625,19 +620,19 @@
|
|||||||
<string name="navigation_drawer_close">A navigációs tálca bezárása</string>
|
<string name="navigation_drawer_close">A navigációs tálca bezárása</string>
|
||||||
<string name="html_about_privacy"><strong>Nincsenek felhasználói adatok lekérve</strong>, az alkalmazás nem kapcsolódik semmilyen külső kiszolgálóhoz, csak helyben működik, és teljes mértékben tiszteletben tartja a felhasználók adatvédelmét.</string>
|
<string name="html_about_privacy"><strong>Nincsenek felhasználói adatok lekérve</strong>, az alkalmazás nem kapcsolódik semmilyen külső kiszolgálóhoz, csak helyben működik, és teljes mértékben tiszteletben tartja a felhasználók adatvédelmét.</string>
|
||||||
<string name="enable_keep_screen_on_summary">Képernyő bekapcsolva tartása egy bejegyzés megtekintésekor vagy szerkesztésekor</string>
|
<string name="enable_keep_screen_on_summary">Képernyő bekapcsolva tartása egy bejegyzés megtekintésekor vagy szerkesztésekor</string>
|
||||||
<string name="title_case">Nagy Kezdőbetűs</string>
|
<string name="title_case">Nagy kezdőbetűs</string>
|
||||||
<string name="navigation_drawer_open">A navigációs tálca kinyitása</string>
|
<string name="navigation_drawer_open">A navigációs tálca kinyitása</string>
|
||||||
<string name="content_description_hardware_key_checkbox">Hardverkulcs jelölőnégyzete</string>
|
<string name="content_description_hardware_key_checkbox">Hardverkulcs jelölőnégyzete</string>
|
||||||
<string name="at_least_one_char">Legalább egy karakter mindegyikből</string>
|
<string name="at_least_one_char">Legalább egy karakter mindegyikből</string>
|
||||||
<string name="lower_case">kisbetűk</string>
|
<string name="lower_case">kisbetűk</string>
|
||||||
<string name="screenshot_mode_banner_text">Képernyőképmodul</string>
|
<string name="screenshot_mode_banner_text">Képernyőképmodul</string>
|
||||||
<string name="case_sensitive">Kis- és nagybetű eltérő</string>
|
<string name="case_sensitive">Kis- és nagybetűk megkülönböztetése</string>
|
||||||
<string name="error_location_unknown">Az adatbázis helye ismeretlen, az adatbázis-művelet nem hajtható végre.</string>
|
<string name="error_location_unknown">Az adatbázis helye ismeretlen, az adatbázis-művelet nem hajtható végre.</string>
|
||||||
<string name="content_description_passphrase_word_count">A jelmondat szószáma</string>
|
<string name="content_description_passphrase_word_count">A jelmondat szószáma</string>
|
||||||
<string name="regex">Reguláris kifejezés</string>
|
<string name="regex">Reguláris kifejezés</string>
|
||||||
<string name="show_entry_colors_title">Bejegyzésszínek</string>
|
<string name="show_entry_colors_title">Bejegyzésszínek</string>
|
||||||
<string name="passphrase">Jelmondat</string>
|
<string name="passphrase">Jelmondat</string>
|
||||||
<string name="menu_appearance_settings_summary">Témák, színek, attribútumok</string>
|
<string name="menu_appearance_settings_summary">Témák, színek, ikonok, betűkészletek, attribútumok</string>
|
||||||
<string name="expired">Lejárt</string>
|
<string name="expired">Lejárt</string>
|
||||||
<string name="error_response_already_provided">A válasz már megérkezett.</string>
|
<string name="error_response_already_provided">A válasz már megérkezett.</string>
|
||||||
<string name="colorize_password_summary">A jelszó karaktereinek színezése típus szerint</string>
|
<string name="colorize_password_summary">A jelszó karaktereinek színezése típus szerint</string>
|
||||||
@@ -652,7 +647,7 @@
|
|||||||
<string name="wireless">Wi-Fi</string>
|
<string name="wireless">Wi-Fi</string>
|
||||||
<string name="error_hardware_key_unsupported">A hardverkulcs nem támogatott.</string>
|
<string name="error_hardware_key_unsupported">A hardverkulcs nem támogatott.</string>
|
||||||
<string name="colorize_password_title">Jelszavak színezése</string>
|
<string name="colorize_password_title">Jelszavak színezése</string>
|
||||||
<string name="menu_device_unlock_settings_summary">Biometrikus adatok, eszköz-hitelesítőadatok</string>
|
<string name="menu_device_unlock_settings_summary">Biometrikus adatok, eszközhitelesítési adatok</string>
|
||||||
<string name="error_XML_malformed">Hibásan formázott XML.</string>
|
<string name="error_XML_malformed">Hibásan formázott XML.</string>
|
||||||
<string name="remember_hardware_key_summary">Követi a használt hardverkulcsokat</string>
|
<string name="remember_hardware_key_summary">Követi a használt hardverkulcsokat</string>
|
||||||
<string name="warning_database_already_opened">Egy adatbázis már nyitva van, előbb zárja be, hogy újat nyisson meg</string>
|
<string name="warning_database_already_opened">Egy adatbázis már nyitva van, előbb zárja be, hogy újat nyisson meg</string>
|
||||||
@@ -662,11 +657,7 @@
|
|||||||
<string name="warning_database_notification_permission">Az értesítési engedély lehetővé teszi az adatbázis állapotának megjelenítését, és az egyszerű ikonnal történő zárolást.
|
<string name="warning_database_notification_permission">Az értesítési engedély lehetővé teszi az adatbázis állapotának megjelenítését, és az egyszerű ikonnal történő zárolást.
|
||||||
\n
|
\n
|
||||||
\nHa nem aktiválja az engedélyt, akkor a háttérben nyitott adatbázis nem lesz látható, ha egy másik alkalmazás van előtérben.</string>
|
\nHa nem aktiválja az engedélyt, akkor a háttérben nyitott adatbázis nem lesz látható, ha egy másik alkalmazás van előtérben.</string>
|
||||||
<string name="device_unlock_keystore_warning">A funkció eltárolja a titkosított hitelesítőadatokat az eszköz biztonságos kulcstárában.
|
<string name="device_unlock_keystore_warning">A funkció eltárolja a titkosított hitelesítési adatokat az eszköz biztonságos KeyStore-jában. \n \nAz operációs rendszer natív API megvalósításától függően lehet, hogy nem lesz teljes értékű. \n \nEllenőrizze a KeyStore kompatibilitását és biztonságát az eszköz gyártójánál, valamint a használt ROM készítőjénél.</string>
|
||||||
\n
|
|
||||||
\nAz operációs rendszer natív API megvalósításától függően lehet, hogy nem lesz teljes értékű.
|
|
||||||
\n
|
|
||||||
\nEllenőrizze a kulcstár kompatibilitását és biztonságát az eszköz gyártójánál és a használt ROM készítőjénél.</string>
|
|
||||||
<string name="show_entry_colors_summary">Megjeleníti egy bejegyzés előtér- és háttérszínét</string>
|
<string name="show_entry_colors_summary">Megjeleníti egy bejegyzés előtér- és háttérszínét</string>
|
||||||
<string name="unlock_and_link_biometric">Eszközfeloldási hivatkozás</string>
|
<string name="unlock_and_link_biometric">Eszközfeloldási hivatkozás</string>
|
||||||
<string name="warning_copy_permission">Az értesítési engedély szükséges a vágólap-értesítési funkció használatához.</string>
|
<string name="warning_copy_permission">Az értesítési engedély szükséges a vágólap-értesítési funkció használatához.</string>
|
||||||
@@ -704,4 +695,45 @@
|
|||||||
<string name="generate_keyfile">Kulcsfájl előállítása</string>
|
<string name="generate_keyfile">Kulcsfájl előállítása</string>
|
||||||
<string name="hide_templates_title">Sablonok elrejtése</string>
|
<string name="hide_templates_title">Sablonok elrejtése</string>
|
||||||
<string name="error_otp_secret_length">A titkos kulcsnak legalább %1$d karakterből kell állnia.</string>
|
<string name="error_otp_secret_length">A titkos kulcsnak legalább %1$d karakterből kell állnia.</string>
|
||||||
|
<string name="entry_application_id">Alkalmazásazonosító</string>
|
||||||
|
<string name="warning_overwrite_data_title">Felülírja a meglévő adatokat?</string>
|
||||||
|
<string name="warning_overwrite_data_description">Ez a művelet felülírja a bejegyzésben található meglévő adatokat. Ha a előzmények funkció engedélyezve van, akkor a régi adatokat vissza lehet állítani.</string>
|
||||||
|
<string name="credential_provider">Hitelesítési adatok szolgáltatója</string>
|
||||||
|
<string name="passkeys">Hozzáférési kulcsok</string>
|
||||||
|
<string name="passkeys_explanation_summary">Hozzáférési kulcsok beállítása a gyors és biztonságos jelszó nélküli bejelentkezéshez</string>
|
||||||
|
<string name="passkeys_preference_title">Hozzáférési kulcsok beállításai</string>
|
||||||
|
<string name="passkeys_close_database_title">Adatbázis bezárása</string>
|
||||||
|
<string name="passkeys_close_database_summary">Adatbázis bezárása a hozzáférési kulcs kiválasztása után</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">Emelt jogosultságú alkalmazások</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">A böngészők kezelése az emelt jogosultságú alkalmazások egyéni listájában</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">FIGYELEM: Az emelt jogosultságú alkalmazás kapuként szolgál a hitelesítés eredetének lekéréséhez. Biztonsági okokból győződjön meg annak jogszerűségéről.</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">Az alkalmazás nem ismerhető fel</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">%1$s megkísérli végrehajtani a hozzáférési kulcsműveletet.\n\nHozzáadja az emelt jogosultságú alkalmazások listájához?</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">Aláírás hiányzik</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">FIGYELEM: A hozzáférési kulcsot egy másik kliens hozta létre, vagy az aláírás törölve lett. A biztonsági problémák elkerülése érdekében győződjön meg arról, hogy az azonosítani kívánt alkalmazás ugyanahhoz a szolgáltatáshoz tartozik, és jogszerű.\nHa az alkalmazás egy böngésző, ne adja hozzá az aláírását a bejegyzéshez, hanem a beállítások emelt jogosultságú alkalmazások listájához.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">%1$s nem ismerhető fel, és hitelesíteni próbál egy meglévő hozzáférési kulccsal.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">Alkalmazás-aláírás hozzáadása a hozzáférési kulcs bejegyzéséhez?</string>
|
||||||
|
<string name="passkeys_auto_select_title">Automatikus kiválasztás</string>
|
||||||
|
<string name="passkeys_auto_select_summary">Automatikus kiválasztás, ha csak egy bejegyzés szerepel, és az adatbázis nyitva van, csak akkor, ha a kérő alkalmazás kompatibilis</string>
|
||||||
|
<string name="passkeys_backup_eligibility_title">Biztonsági mentéshez való jogosultság</string>
|
||||||
|
<string name="passkeys_backup_eligibility_summary">A biztonsági mentés létrehozáskor határozza meg, hogy a nyilvános kulcs hitelesítési adatforrása jogosult-e visszaállítani a biztonsági mentést</string>
|
||||||
|
<string name="passkeys_backup_state_title">Biztonsági mentés állapota</string>
|
||||||
|
<string name="passkeys_backup_state_summary">Jelzi, hogy a hitelesítési adatokról biztonsági másolat lett létrehozva, és azok egyetlen eszköz elvesztése esetén is védve vannak</string>
|
||||||
|
<string name="credential_provider_service_subtitle">Biztonsági kulcsok, automatikus kitöltés hitelesítési adat szolgáltató</string>
|
||||||
|
<string name="passkey">Hozzáférési kulcs</string>
|
||||||
|
<string name="passkey_service_name">KeePassDX hitelesítési adatok szolgáltató</string>
|
||||||
|
<string name="passkey_creation_description">Hozzáférési kulcs mentése új bejegyzésben</string>
|
||||||
|
<string name="passkey_update_description">Hozzáférési kulcs frissítése a(z) %1$s fájlban</string>
|
||||||
|
<string name="passkey_selection_username">Nem található hozzáférési kulcs</string>
|
||||||
|
<string name="passkey_selection_description">Válasszon egy meglévő hozzáférési kulcsot</string>
|
||||||
|
<string name="passkey_database_username">KeePassDX adatbázis</string>
|
||||||
|
<string name="passkey_locked_database_description">Válassza a feloldáshoz</string>
|
||||||
|
<string name="passkey_username">Hozzáférési kulcs felhasználóneve</string>
|
||||||
|
<string name="passkey_private_key">Hozzáférési kulcs privát kulcsa</string>
|
||||||
|
<string name="passkey_credential_id">Hozzáférési kulcs hitelesítési azonosítója</string>
|
||||||
|
<string name="passkey_user_handle">Hozzáférési kulcs felhasználói kezelője</string>
|
||||||
|
<string name="passkey_relying_party">Hozzáférési kulcs megbízható fele</string>
|
||||||
|
<string name="passkey_backup_eligibility">Hozzáférési kulcs mentésének jogosultsága</string>
|
||||||
|
<string name="passkey_backup_state">Hozzáférési kulcs mentésének állapota</string>
|
||||||
|
<string name="error_passkey_result">A hozzáférési kulcs visszaadása nem lehetséges</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
<string name="menu_open">Buka</string>
|
<string name="menu_open">Buka</string>
|
||||||
<string name="menu_save_database">Simpan data</string>
|
<string name="menu_save_database">Simpan data</string>
|
||||||
<string name="menu_lock">Basis Data Terkunci</string>
|
<string name="menu_lock">Basis Data Terkunci</string>
|
||||||
<string name="menu_hide_password">Sembunyikan Kata Sandi</string>
|
|
||||||
<string name="menu_cancel">Batal</string>
|
<string name="menu_cancel">Batal</string>
|
||||||
<string name="menu_delete">Hapus</string>
|
<string name="menu_delete">Hapus</string>
|
||||||
<string name="menu_paste">Tempel</string>
|
<string name="menu_paste">Tempel</string>
|
||||||
@@ -32,8 +31,6 @@
|
|||||||
<string name="copy_field">Salinan dari %1$s</string>
|
<string name="copy_field">Salinan dari %1$s</string>
|
||||||
<string name="menu_change_key_settings">Ubah Kunci Utama</string>
|
<string name="menu_change_key_settings">Ubah Kunci Utama</string>
|
||||||
<string name="about">Tentang</string>
|
<string name="about">Tentang</string>
|
||||||
<string name="hide_password_summary">Secara otomatis tutupi kata sandi (***)</string>
|
|
||||||
<string name="hide_password_title">Sembunyikan Kata Sandi</string>
|
|
||||||
<string name="lowercase">Huruf Kecil</string>
|
<string name="lowercase">Huruf Kecil</string>
|
||||||
<string name="loading_database">Memuat basis data…</string>
|
<string name="loading_database">Memuat basis data…</string>
|
||||||
<string name="creating_database">Pembuatan basis data…</string>
|
<string name="creating_database">Pembuatan basis data…</string>
|
||||||
@@ -659,7 +656,7 @@
|
|||||||
<string name="education_validate_entry_summary">Ingat untuk memvalidasi entri Anda dan simpan basis data Anda.
|
<string name="education_validate_entry_summary">Ingat untuk memvalidasi entri Anda dan simpan basis data Anda.
|
||||||
\n
|
\n
|
||||||
\nJika penguncian otomatis diaktifkan dan Anda lupa bahwa Anda sedang membuat perubahan, Anda berisiko kehilangan data Anda.</string>
|
\nJika penguncian otomatis diaktifkan dan Anda lupa bahwa Anda sedang membuat perubahan, Anda berisiko kehilangan data Anda.</string>
|
||||||
<string name="menu_appearance_settings_summary">Tema, warna, atribut</string>
|
<string name="menu_appearance_settings_summary">Tema, warna, ikon, fon, atribut</string>
|
||||||
<string name="unlock">Buka kunci</string>
|
<string name="unlock">Buka kunci</string>
|
||||||
<string name="education_validate_entry_title">Validasi entri</string>
|
<string name="education_validate_entry_title">Validasi entri</string>
|
||||||
<string name="style_name_moon">Bulan</string>
|
<string name="style_name_moon">Bulan</string>
|
||||||
|
|||||||
@@ -94,8 +94,6 @@
|
|||||||
<string name="list_size_summary">Dimensione del testo nell\'elenco del gruppo</string>
|
<string name="list_size_summary">Dimensione del testo nell\'elenco del gruppo</string>
|
||||||
<string name="loading_database">Caricamento del database…</string>
|
<string name="loading_database">Caricamento del database…</string>
|
||||||
<string name="lowercase">Minuscole</string>
|
<string name="lowercase">Minuscole</string>
|
||||||
<string name="hide_password_title">Nascondi le password</string>
|
|
||||||
<string name="hide_password_summary">Maschera le password (***) in modo predefinito</string>
|
|
||||||
<string name="about">Informazioni</string>
|
<string name="about">Informazioni</string>
|
||||||
<string name="menu_change_key_settings">Modifica chiave principale</string>
|
<string name="menu_change_key_settings">Modifica chiave principale</string>
|
||||||
<string name="settings">Impostazioni</string>
|
<string name="settings">Impostazioni</string>
|
||||||
@@ -103,7 +101,6 @@
|
|||||||
<string name="menu_delete">Elimina</string>
|
<string name="menu_delete">Elimina</string>
|
||||||
<string name="menu_donate">Dona</string>
|
<string name="menu_donate">Dona</string>
|
||||||
<string name="menu_edit">Modifica</string>
|
<string name="menu_edit">Modifica</string>
|
||||||
<string name="menu_hide_password">Nascondi password</string>
|
|
||||||
<string name="menu_lock">Blocca database</string>
|
<string name="menu_lock">Blocca database</string>
|
||||||
<string name="menu_open">Apri</string>
|
<string name="menu_open">Apri</string>
|
||||||
<string name="menu_search">Cerca</string>
|
<string name="menu_search">Cerca</string>
|
||||||
@@ -180,7 +177,7 @@
|
|||||||
<string name="autofill">Autocompletamento</string>
|
<string name="autofill">Autocompletamento</string>
|
||||||
<string name="autofill_sign_in_prompt">Accedi con KeePassDX</string>
|
<string name="autofill_sign_in_prompt">Accedi con KeePassDX</string>
|
||||||
<string name="set_credential_provider_service_title">Servizio predefinito di autocompletamento</string>
|
<string name="set_credential_provider_service_title">Servizio predefinito di autocompletamento</string>
|
||||||
<string name="autofill_explanation_summary">Attiva l\'autocompletamento per riempire velocemente i campi in altre app</string>
|
<string name="autofill_explanation_summary">Configura l\'autocompletamento per riempire velocemente i campi in altre app</string>
|
||||||
<string name="password_size_title">Dimensione password generata</string>
|
<string name="password_size_title">Dimensione password generata</string>
|
||||||
<string name="password_size_summary">Imposta la dimensione predefinita delle password generate</string>
|
<string name="password_size_summary">Imposta la dimensione predefinita delle password generate</string>
|
||||||
<string name="list_password_generator_options_title">Caratteri password</string>
|
<string name="list_password_generator_options_title">Caratteri password</string>
|
||||||
@@ -304,7 +301,7 @@
|
|||||||
<string name="selection_mode">Modalità selezione</string>
|
<string name="selection_mode">Modalità selezione</string>
|
||||||
<string name="do_not_kill_app">Non terminare l\'app…</string>
|
<string name="do_not_kill_app">Non terminare l\'app…</string>
|
||||||
<string name="lock_database_back_root_title">Premere \'\'Indietro\'\' per bloccare</string>
|
<string name="lock_database_back_root_title">Premere \'\'Indietro\'\' per bloccare</string>
|
||||||
<string name="lock_database_back_root_summary">Blocca il database quando l\'utente preme il pulsante Indietro nella schermata principale</string>
|
<string name="lock_database_back_root_summary">Premi \'Indietro\' per bloccare il database se sei nella schermata principale del database</string>
|
||||||
<string name="clear_clipboard_notification_title">Pulisci alla chiusura</string>
|
<string name="clear_clipboard_notification_title">Pulisci alla chiusura</string>
|
||||||
<string name="clear_clipboard_notification_summary">Blocca il database quando scade la durata degli appunti o la notifica viene chiusa dopo che inizi ad usarlo</string>
|
<string name="clear_clipboard_notification_summary">Blocca il database quando scade la durata degli appunti o la notifica viene chiusa dopo che inizi ad usarlo</string>
|
||||||
<string name="recycle_bin">Cestino</string>
|
<string name="recycle_bin">Cestino</string>
|
||||||
@@ -522,7 +519,7 @@
|
|||||||
<string name="warning_database_info_changed_options">Unisci i dati, sovrascrivi le modifiche esterne salvando il database o ricaricalo con le ultime modifiche.</string>
|
<string name="warning_database_info_changed_options">Unisci i dati, sovrascrivi le modifiche esterne salvando il database o ricaricalo con le ultime modifiche.</string>
|
||||||
<string name="warning_database_info_changed">I dati nel tuo database sono stati modificati al di fuori di questa app.</string>
|
<string name="warning_database_info_changed">I dati nel tuo database sono stati modificati al di fuori di questa app.</string>
|
||||||
<string name="menu_reload_database">Ricarica dati</string>
|
<string name="menu_reload_database">Ricarica dati</string>
|
||||||
<string name="error_otp_type">Il tipo di OTP esistente non è riconosciuto da questo modulo, la sua convalida potrebbe non generare più correttamente il token.</string>
|
<string name="error_otp_type">Il tipo di OTP esistente non è riconosciuto da questo modulo e la sua convalida potrebbe non generare più il token correttamente.</string>
|
||||||
<string name="download_canceled">Annullato!</string>
|
<string name="download_canceled">Annullato!</string>
|
||||||
<string name="unit_gibibyte">GiB</string>
|
<string name="unit_gibibyte">GiB</string>
|
||||||
<string name="unit_mebibyte">MiB</string>
|
<string name="unit_mebibyte">MiB</string>
|
||||||
@@ -678,7 +675,7 @@
|
|||||||
<string name="ask">Chiedi</string>
|
<string name="ask">Chiedi</string>
|
||||||
<string name="configure">Configura</string>
|
<string name="configure">Configura</string>
|
||||||
<string name="unlock_and_link_biometric">Collegamento sblocco con dispositivo</string>
|
<string name="unlock_and_link_biometric">Collegamento sblocco con dispositivo</string>
|
||||||
<string name="menu_appearance_settings_summary">Temi, colori, attributi</string>
|
<string name="menu_appearance_settings_summary">Temi, colori, icone, caratteri, attributi</string>
|
||||||
<string name="unlock">Sblocca</string>
|
<string name="unlock">Sblocca</string>
|
||||||
<string name="education_validate_entry_title">Conferma la voce</string>
|
<string name="education_validate_entry_title">Conferma la voce</string>
|
||||||
<string name="education_validate_entry_summary">Ricordati di confermare la voce e salvare il tuo database.
|
<string name="education_validate_entry_summary">Ricordati di confermare la voce e salvare il tuo database.
|
||||||
@@ -707,4 +704,45 @@
|
|||||||
<string name="recursive_number_entries_summary">Calcola in modo ricorsivo il numero di voci in un gruppo</string>
|
<string name="recursive_number_entries_summary">Calcola in modo ricorsivo il numero di voci in un gruppo</string>
|
||||||
<string name="hide_templates_summary">I template non vengono mostrati</string>
|
<string name="hide_templates_summary">I template non vengono mostrati</string>
|
||||||
<string name="error_otp_secret_length">La chiave segreta deve essere lunga almeno %1$d caratteri.</string>
|
<string name="error_otp_secret_length">La chiave segreta deve essere lunga almeno %1$d caratteri.</string>
|
||||||
|
<string name="entry_application_id">ID app</string>
|
||||||
|
<string name="warning_overwrite_data_title">Sovrascrivere i dati esistenti?</string>
|
||||||
|
<string name="warning_overwrite_data_description">Questa azione sostituirà i dati esistenti nella voce, puoi recuperare i vecchi dati se la cronologia è attiva.</string>
|
||||||
|
<string name="credential_provider">Fornitore di credenziali</string>
|
||||||
|
<string name="passkeys">Passkey</string>
|
||||||
|
<string name="passkeys_explanation_summary">Configura una passkey per un accesso veloce e sicuro senza password</string>
|
||||||
|
<string name="passkeys_preference_title">Impostazioni passkey</string>
|
||||||
|
<string name="passkeys_close_database_title">Chiudi database</string>
|
||||||
|
<string name="passkeys_close_database_summary">Chiudi il database dopo la selezione di una passkey</string>
|
||||||
|
<string name="passkeys_privileged_apps_title">App privilegiate</string>
|
||||||
|
<string name="passkeys_privileged_apps_summary">Gestisci i browser nell\'elenco personalizzato di app privilegiate</string>
|
||||||
|
<string name="passkeys_privileged_apps_explanation">ATTENZIONE: Un\'app privilegiata funge da gateway per recuperare l\'origine di un\'autenticazione. Assicurati della sua legittimità per evitare problemi di sicurezza.</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_title">App non riconosciuta</string>
|
||||||
|
<string name="passkeys_privileged_apps_ask_message">%1$s tentativi di eseguire un\'azione passkey.\n\nAggiungerla alla lista delle app privilegiate?</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_title">Firma mancante</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_explanation">ATTENZIONE: la passkey è stata creata da un altro client o la firma è stata eliminata. Assicurati che l\'app che vuoi autenticare sia parte dello stesso servizio e che sia legittima per evitare problemi di sicurezza.\nSe l\'app è un browser, non aggiungere la sua firma alla voce, ma all\'elenco delle app privilegiate nelle impostazioni.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_message">%1$s non è riconosciuto e tenta di autenticarsi con un passkey esistente.</string>
|
||||||
|
<string name="passkeys_missing_signature_app_ask_question">Aggiungere la firma dell\'app alla voce della passkey?</string>
|
||||||
|
<string name="passkeys_auto_select_title">Selezione automatica</string>
|
||||||
|
<string name="passkeys_auto_select_summary">Selezionare automaticamente se una sola voce e il database è aperto, solo se l\'app richiesta è compatibile</string>
|
||||||
|
<string name="passkeys_backup_eligibility_title">Ammissibilità del backup</string>
|
||||||
|
<string name="passkeys_backup_eligibility_summary">Determina al momento della creazione se l\'origine delle credenziali della chiave pubblica sia consentita per il backup</string>
|
||||||
|
<string name="passkeys_backup_state_title">Stato del backup</string>
|
||||||
|
<string name="passkeys_backup_state_summary">Indica che le credenziali sono dietro backup e protette contro la perdita di un singolo dispositivo</string>
|
||||||
|
<string name="credential_provider_service_subtitle">Passkey, fornitore di autocompletamento credenziali</string>
|
||||||
|
<string name="passkey">Passkey</string>
|
||||||
|
<string name="passkey_service_name">Fornitore di credenziali KeePassDX</string>
|
||||||
|
<string name="passkey_creation_description">Salva la passkey in una nuova voce</string>
|
||||||
|
<string name="passkey_update_description">Aggiorna la passkey in %1$s</string>
|
||||||
|
<string name="passkey_selection_username">Nessuna passkey trovata</string>
|
||||||
|
<string name="passkey_selection_description">Seleziona una passkey esistente</string>
|
||||||
|
<string name="passkey_database_username">Database di KeePassDX</string>
|
||||||
|
<string name="passkey_locked_database_description">Seleziona per sbloccare</string>
|
||||||
|
<string name="passkey_username">Nome utente passkey</string>
|
||||||
|
<string name="passkey_private_key">Chiave privata passkey</string>
|
||||||
|
<string name="passkey_credential_id">ID credenziali passkey</string>
|
||||||
|
<string name="passkey_user_handle">Handle utente passkey</string>
|
||||||
|
<string name="passkey_relying_party">Parte affidabile passkey</string>
|
||||||
|
<string name="passkey_backup_eligibility">Ammissibilità backup passkey</string>
|
||||||
|
<string name="passkey_backup_state">Stato backup passkey</string>
|
||||||
|
<string name="error_passkey_result">Impossibile ritornare la passkey</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user