mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'feature/Entry_History' into develop
This commit is contained in:
@@ -21,6 +21,7 @@ package com.kunzisoft.keepass.activities
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
@@ -54,10 +55,13 @@ class EntryActivity : LockingHideActivity() {
|
||||
|
||||
private var collapsingToolbarLayout: CollapsingToolbarLayout? = null
|
||||
private var titleIconView: ImageView? = null
|
||||
private var historyView: View? = null
|
||||
private var entryContentsView: EntryContentsView? = null
|
||||
private var toolbar: Toolbar? = null
|
||||
|
||||
private var mEntry: EntryVersioned? = null
|
||||
private var mIsHistory: Boolean = false
|
||||
|
||||
private var mShowPassword: Boolean = false
|
||||
|
||||
private var clipboardHelper: ClipboardHelper? = null
|
||||
@@ -88,6 +92,12 @@ class EntryActivity : LockingHideActivity() {
|
||||
Log.e(TAG, "Unable to retrieve the entry key")
|
||||
}
|
||||
|
||||
val historyPosition = intent.getIntExtra(KEY_ENTRY_HISTORY_POSITION, -1)
|
||||
if (historyPosition >= 0) {
|
||||
mIsHistory = true
|
||||
mEntry = mEntry?.getHistory()?.get(historyPosition)
|
||||
}
|
||||
|
||||
if (mEntry == null) {
|
||||
Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show()
|
||||
finish()
|
||||
@@ -108,6 +118,7 @@ class EntryActivity : LockingHideActivity() {
|
||||
// Get views
|
||||
collapsingToolbarLayout = findViewById(R.id.toolbar_layout)
|
||||
titleIconView = findViewById(R.id.entry_icon)
|
||||
historyView = findViewById(R.id.history_container)
|
||||
entryContentsView = findViewById(R.id.entry_contents)
|
||||
entryContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this))
|
||||
|
||||
@@ -238,18 +249,11 @@ class EntryActivity : LockingHideActivity() {
|
||||
entryContentsView?.setHiddenPasswordStyle(!mShowPassword)
|
||||
|
||||
// Assign dates
|
||||
entry.creationTime.date?.let {
|
||||
entryContentsView?.assignCreationDate(it)
|
||||
}
|
||||
entry.lastModificationTime.date?.let {
|
||||
entryContentsView?.assignModificationDate(it)
|
||||
}
|
||||
entry.lastAccessTime.date?.let {
|
||||
entryContentsView?.assignLastAccessDate(it)
|
||||
}
|
||||
val expires = entry.expiryTime.date
|
||||
if (entry.isExpires && expires != null) {
|
||||
entryContentsView?.assignExpiresDate(expires)
|
||||
entryContentsView?.assignCreationDate(entry.creationTime)
|
||||
entryContentsView?.assignModificationDate(entry.lastModificationTime)
|
||||
entryContentsView?.assignLastAccessDate(entry.lastAccessTime)
|
||||
if (entry.isExpires) {
|
||||
entryContentsView?.assignExpiresDate(entry.expiryTime)
|
||||
} else {
|
||||
entryContentsView?.assignExpiresDate(getString(R.string.never))
|
||||
}
|
||||
@@ -257,6 +261,24 @@ class EntryActivity : LockingHideActivity() {
|
||||
// Assign special data
|
||||
entryContentsView?.assignUUID(entry.nodeId.id)
|
||||
|
||||
// Manage history
|
||||
historyView?.visibility = if (mIsHistory) View.VISIBLE else View.GONE
|
||||
if (mIsHistory) {
|
||||
val taColorAccent = theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
|
||||
collapsingToolbarLayout?.contentScrim = ColorDrawable(taColorAccent.getColor(0, Color.BLACK))
|
||||
taColorAccent.recycle()
|
||||
}
|
||||
val entryHistory = entry.getHistory()
|
||||
// isMainEntry = not an history
|
||||
val showHistoryView = entryHistory.isNotEmpty()
|
||||
entryContentsView?.showHistory(showHistoryView)
|
||||
if (showHistoryView) {
|
||||
entryContentsView?.assignHistory(entryHistory)
|
||||
entryContentsView?.onHistoryClick { historyItem, position ->
|
||||
launch(this, historyItem, true, position)
|
||||
}
|
||||
}
|
||||
|
||||
database.stopManageEntry(entry)
|
||||
}
|
||||
|
||||
@@ -412,13 +434,16 @@ class EntryActivity : LockingHideActivity() {
|
||||
companion object {
|
||||
private val TAG = EntryActivity::class.java.name
|
||||
|
||||
const val KEY_ENTRY = "entry"
|
||||
const val KEY_ENTRY = "KEY_ENTRY"
|
||||
const val KEY_ENTRY_HISTORY_POSITION = "KEY_ENTRY_HISTORY_POSITION"
|
||||
|
||||
fun launch(activity: Activity, pw: EntryVersioned, readOnly: Boolean) {
|
||||
fun launch(activity: Activity, entry: EntryVersioned, readOnly: Boolean, historyPosition: Int? = null) {
|
||||
if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) {
|
||||
val intent = Intent(activity, EntryActivity::class.java)
|
||||
intent.putExtra(KEY_ENTRY, pw.nodeId)
|
||||
intent.putExtra(KEY_ENTRY, entry.nodeId)
|
||||
ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly)
|
||||
if (historyPosition != null)
|
||||
intent.putExtra(KEY_ENTRY_HISTORY_POSITION, historyPosition)
|
||||
activity.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.kunzisoft.keepass.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.EntryVersioned
|
||||
|
||||
class EntryHistoryAdapter(val context: Context) : RecyclerView.Adapter<EntryHistoryAdapter.EntryHistoryViewHolder>() {
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
var entryHistoryList: MutableList<EntryVersioned> = ArrayList()
|
||||
var onItemClickListener: ((item: EntryVersioned, position: Int)->Unit)? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EntryHistoryViewHolder {
|
||||
return EntryHistoryViewHolder(inflater.inflate(R.layout.item_list_entry_history, parent, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: EntryHistoryViewHolder, position: Int) {
|
||||
val entryHistory = entryHistoryList[position]
|
||||
|
||||
holder.lastModifiedView.text = entryHistory.lastModificationTime.getDateTimeString(context.resources)
|
||||
holder.titleView.text = entryHistory.title
|
||||
holder.usernameView.text = entryHistory.username
|
||||
holder.urlView.text = entryHistory.url
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
onItemClickListener?.invoke(entryHistory, position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return entryHistoryList.size
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
entryHistoryList.clear()
|
||||
}
|
||||
|
||||
inner class EntryHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var lastModifiedView: TextView = itemView.findViewById(R.id.entry_history_last_modified)
|
||||
var titleView: TextView = itemView.findViewById(R.id.entry_history_title)
|
||||
var usernameView: TextView = itemView.findViewById(R.id.entry_history_username)
|
||||
var urlView: TextView = itemView.findViewById(R.id.entry_history_url)
|
||||
}
|
||||
}
|
||||
@@ -32,12 +32,10 @@ class AesKdf internal constructor() : KdfEngine() {
|
||||
|
||||
override val defaultParameters: KdfParameters
|
||||
get() {
|
||||
val p = KdfParameters(uuid)
|
||||
|
||||
p.setParamUUID()
|
||||
p.setUInt32(ParamRounds, DEFAULT_ROUNDS.toLong())
|
||||
|
||||
return p
|
||||
return KdfParameters(uuid).apply {
|
||||
setParamUUID()
|
||||
setUInt32(PARAM_ROUNDS, DEFAULT_ROUNDS.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
override val defaultKeyRounds: Long
|
||||
@@ -54,8 +52,8 @@ class AesKdf internal constructor() : KdfEngine() {
|
||||
@Throws(IOException::class)
|
||||
override fun transform(masterKey: ByteArray, p: KdfParameters): ByteArray {
|
||||
var currentMasterKey = masterKey
|
||||
val rounds = p.getUInt64(ParamRounds)
|
||||
var seed = p.getByteArray(ParamSeed)
|
||||
val rounds = p.getUInt64(PARAM_ROUNDS)
|
||||
var seed = p.getByteArray(PARAM_SEED)
|
||||
|
||||
if (currentMasterKey.size != 32) {
|
||||
currentMasterKey = CryptoUtil.hashSha256(currentMasterKey)
|
||||
@@ -75,15 +73,15 @@ class AesKdf internal constructor() : KdfEngine() {
|
||||
val seed = ByteArray(32)
|
||||
random.nextBytes(seed)
|
||||
|
||||
p.setByteArray(ParamSeed, seed)
|
||||
p.setByteArray(PARAM_SEED, seed)
|
||||
}
|
||||
|
||||
override fun getKeyRounds(p: KdfParameters): Long {
|
||||
return p.getUInt64(ParamRounds)
|
||||
return p.getUInt64(PARAM_ROUNDS)
|
||||
}
|
||||
|
||||
override fun setKeyRounds(p: KdfParameters, keyRounds: Long) {
|
||||
p.setUInt64(ParamRounds, keyRounds)
|
||||
p.setUInt64(PARAM_ROUNDS, keyRounds)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -91,9 +89,24 @@ class AesKdf internal constructor() : KdfEngine() {
|
||||
private const val DEFAULT_ROUNDS = 6000
|
||||
|
||||
val CIPHER_UUID: UUID = Types.bytestoUUID(
|
||||
byteArrayOf(0xC9.toByte(), 0xD9.toByte(), 0xF3.toByte(), 0x9A.toByte(), 0x62.toByte(), 0x8A.toByte(), 0x44.toByte(), 0x60.toByte(), 0xBF.toByte(), 0x74.toByte(), 0x0D.toByte(), 0x08.toByte(), 0xC1.toByte(), 0x8A.toByte(), 0x4F.toByte(), 0xEA.toByte()))
|
||||
byteArrayOf(0xC9.toByte(),
|
||||
0xD9.toByte(),
|
||||
0xF3.toByte(),
|
||||
0x9A.toByte(),
|
||||
0x62.toByte(),
|
||||
0x8A.toByte(),
|
||||
0x44.toByte(),
|
||||
0x60.toByte(),
|
||||
0xBF.toByte(),
|
||||
0x74.toByte(),
|
||||
0x0D.toByte(),
|
||||
0x08.toByte(),
|
||||
0xC1.toByte(),
|
||||
0x8A.toByte(),
|
||||
0x4F.toByte(),
|
||||
0xEA.toByte()))
|
||||
|
||||
const val ParamRounds = "R"
|
||||
const val ParamSeed = "S"
|
||||
const val PARAM_ROUNDS = "R"
|
||||
const val PARAM_SEED = "S"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,16 +33,16 @@ class Argon2Kdf internal constructor() : KdfEngine() {
|
||||
val p = KdfParameters(uuid)
|
||||
|
||||
p.setParamUUID()
|
||||
p.setUInt32(ParamParallelism, DefaultParallelism)
|
||||
p.setUInt64(ParamMemory, DefaultMemory)
|
||||
p.setUInt64(ParamIterations, DefaultIterations)
|
||||
p.setUInt32(ParamVersion, MaxVersion)
|
||||
p.setUInt32(PARAM_PARALLELISM, DEFAULT_PARALLELISM)
|
||||
p.setUInt64(PARAM_MEMORY, DEFAULT_MEMORY)
|
||||
p.setUInt64(PARAM_ITERATIONS, DEFAULT_ITERATIONS)
|
||||
p.setUInt32(PARAM_VERSION, MAX_VERSION)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
override val defaultKeyRounds: Long
|
||||
get() = DefaultIterations
|
||||
get() = DEFAULT_ITERATIONS
|
||||
|
||||
init {
|
||||
uuid = CIPHER_UUID
|
||||
@@ -55,13 +55,13 @@ class Argon2Kdf internal constructor() : KdfEngine() {
|
||||
@Throws(IOException::class)
|
||||
override fun transform(masterKey: ByteArray, p: KdfParameters): ByteArray {
|
||||
|
||||
val salt = p.getByteArray(ParamSalt)
|
||||
val parallelism = p.getUInt32(ParamParallelism).toInt()
|
||||
val memory = p.getUInt64(ParamMemory)
|
||||
val iterations = p.getUInt64(ParamIterations)
|
||||
val version = p.getUInt32(ParamVersion)
|
||||
val secretKey = p.getByteArray(ParamSecretKey)
|
||||
val assocData = p.getByteArray(ParamAssocData)
|
||||
val salt = p.getByteArray(PARAM_SALT)
|
||||
val parallelism = p.getUInt32(PARAM_PARALLELISM).toInt()
|
||||
val memory = p.getUInt64(PARAM_MEMORY)
|
||||
val iterations = p.getUInt64(PARAM_ITERATIONS)
|
||||
val version = p.getUInt32(PARAM_VERSION)
|
||||
val secretKey = p.getByteArray(PARAM_SECRET_KEY)
|
||||
val assocData = p.getByteArray(PARAM_ASSOC_DATA)
|
||||
|
||||
return Argon2Native.transformKey(masterKey, salt, parallelism, memory, iterations,
|
||||
secretKey, assocData, version)
|
||||
@@ -73,71 +73,102 @@ class Argon2Kdf internal constructor() : KdfEngine() {
|
||||
val salt = ByteArray(32)
|
||||
random.nextBytes(salt)
|
||||
|
||||
p.setByteArray(ParamSalt, salt)
|
||||
p.setByteArray(PARAM_SALT, salt)
|
||||
}
|
||||
|
||||
override fun getKeyRounds(p: KdfParameters): Long {
|
||||
return p.getUInt64(ParamIterations)
|
||||
return p.getUInt64(PARAM_ITERATIONS)
|
||||
}
|
||||
|
||||
override fun setKeyRounds(p: KdfParameters, keyRounds: Long) {
|
||||
p.setUInt64(ParamIterations, keyRounds)
|
||||
p.setUInt64(PARAM_ITERATIONS, keyRounds)
|
||||
}
|
||||
|
||||
override val minKeyRounds: Long
|
||||
get() = MIN_ITERATIONS
|
||||
|
||||
override val maxKeyRounds: Long
|
||||
get() = MAX_ITERATIONS
|
||||
|
||||
override fun getMemoryUsage(p: KdfParameters): Long {
|
||||
return p.getUInt64(ParamMemory)
|
||||
return p.getUInt64(PARAM_MEMORY)
|
||||
}
|
||||
|
||||
override fun setMemoryUsage(p: KdfParameters, memory: Long) {
|
||||
p.setUInt64(ParamMemory, memory)
|
||||
p.setUInt64(PARAM_MEMORY, memory)
|
||||
}
|
||||
|
||||
override fun getDefaultMemoryUsage(): Long {
|
||||
return DefaultMemory
|
||||
}
|
||||
override val defaultMemoryUsage: Long
|
||||
get() = DEFAULT_MEMORY
|
||||
|
||||
override val minMemoryUsage: Long
|
||||
get() = MIN_MEMORY
|
||||
|
||||
override val maxMemoryUsage: Long
|
||||
get() = MAX_MEMORY
|
||||
|
||||
override fun getParallelism(p: KdfParameters): Int {
|
||||
return p.getUInt32(ParamParallelism).toInt() // TODO Verify
|
||||
return p.getUInt32(PARAM_PARALLELISM).toInt() // TODO Verify
|
||||
}
|
||||
|
||||
override fun setParallelism(p: KdfParameters, parallelism: Int) {
|
||||
p.setUInt32(ParamParallelism, parallelism.toLong())
|
||||
p.setUInt32(PARAM_PARALLELISM, parallelism.toLong())
|
||||
}
|
||||
|
||||
override fun getDefaultParallelism(): Int {
|
||||
return DefaultParallelism.toInt() // TODO Verify
|
||||
}
|
||||
override val defaultParallelism: Int
|
||||
get() = DEFAULT_PARALLELISM.toInt()
|
||||
|
||||
override val minParallelism: Int
|
||||
get() = MIN_PARALLELISM
|
||||
|
||||
override val maxParallelism: Int
|
||||
get() = MAX_PARALLELISM
|
||||
|
||||
companion object {
|
||||
|
||||
val CIPHER_UUID: UUID = Types.bytestoUUID(
|
||||
byteArrayOf(0xEF.toByte(), 0x63.toByte(), 0x6D.toByte(), 0xDF.toByte(), 0x8C.toByte(), 0x29.toByte(), 0x44.toByte(), 0x4B.toByte(), 0x91.toByte(), 0xF7.toByte(), 0xA9.toByte(), 0xA4.toByte(), 0x03.toByte(), 0xE3.toByte(), 0x0A.toByte(), 0x0C.toByte()))
|
||||
byteArrayOf(0xEF.toByte(),
|
||||
0x63.toByte(),
|
||||
0x6D.toByte(),
|
||||
0xDF.toByte(),
|
||||
0x8C.toByte(),
|
||||
0x29.toByte(),
|
||||
0x44.toByte(),
|
||||
0x4B.toByte(),
|
||||
0x91.toByte(),
|
||||
0xF7.toByte(),
|
||||
0xA9.toByte(),
|
||||
0xA4.toByte(),
|
||||
0x03.toByte(),
|
||||
0xE3.toByte(),
|
||||
0x0A.toByte(),
|
||||
0x0C.toByte()))
|
||||
|
||||
private const val ParamSalt = "S" // byte[]
|
||||
private const val ParamParallelism = "P" // UInt32
|
||||
private const val ParamMemory = "M" // UInt64
|
||||
private const val ParamIterations = "I" // UInt64
|
||||
private const val ParamVersion = "V" // UInt32
|
||||
private const val ParamSecretKey = "K" // byte[]
|
||||
private const val ParamAssocData = "A" // byte[]
|
||||
private const val PARAM_SALT = "S" // byte[]
|
||||
private const val PARAM_PARALLELISM = "P" // UInt32
|
||||
private const val PARAM_MEMORY = "M" // UInt64
|
||||
private const val PARAM_ITERATIONS = "I" // UInt64
|
||||
private const val PARAM_VERSION = "V" // UInt32
|
||||
private const val PARAM_SECRET_KEY = "K" // byte[]
|
||||
private const val PARAM_ASSOC_DATA = "A" // byte[]
|
||||
|
||||
private const val MinVersion: Long = 0x10
|
||||
private const val MaxVersion: Long = 0x13
|
||||
private const val MIN_VERSION: Long = 0x10
|
||||
private const val MAX_VERSION: Long = 0x13
|
||||
|
||||
private const val MinSalt = 8
|
||||
private const val MaxSalt = Integer.MAX_VALUE
|
||||
private const val MIN_SALT = 8
|
||||
private const val MAX_SALT = Integer.MAX_VALUE
|
||||
|
||||
private const val MinIterations: Long = 1
|
||||
private const val MaxIterations = 4294967295L
|
||||
private const val MIN_ITERATIONS: Long = 1
|
||||
private const val MAX_ITERATIONS = 4294967295L
|
||||
|
||||
private const val MinMemory = (1024 * 8).toLong()
|
||||
private const val MaxMemory = Integer.MAX_VALUE.toLong()
|
||||
private const val MIN_MEMORY = (1024 * 8).toLong()
|
||||
private const val MAX_MEMORY = Integer.MAX_VALUE.toLong()
|
||||
|
||||
private const val MinParallelism = 1
|
||||
private const val MaxParallelism = (1 shl 24) - 1
|
||||
private const val MIN_PARALLELISM = 1
|
||||
private const val MAX_PARALLELISM = (1 shl 24) - 1
|
||||
|
||||
private const val DefaultIterations: Long = 2
|
||||
private const val DefaultMemory = (1024 * 1024).toLong()
|
||||
private const val DefaultParallelism: Long = 2
|
||||
private const val DEFAULT_ITERATIONS: Long = 2
|
||||
private const val DEFAULT_MEMORY = (1024 * 1024).toLong()
|
||||
private const val DEFAULT_PARALLELISM: Long = 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,17 +30,31 @@ abstract class KdfEngine : ObjectNameResource {
|
||||
|
||||
abstract val defaultParameters: KdfParameters
|
||||
|
||||
abstract val defaultKeyRounds: Long
|
||||
|
||||
@Throws(IOException::class)
|
||||
abstract fun transform(masterKey: ByteArray, p: KdfParameters): ByteArray
|
||||
|
||||
abstract fun randomize(p: KdfParameters)
|
||||
|
||||
/*
|
||||
* ITERATIONS
|
||||
*/
|
||||
|
||||
abstract fun getKeyRounds(p: KdfParameters): Long
|
||||
|
||||
abstract fun setKeyRounds(p: KdfParameters, keyRounds: Long)
|
||||
|
||||
abstract val defaultKeyRounds: Long
|
||||
|
||||
open val minKeyRounds: Long
|
||||
get() = 1
|
||||
|
||||
open val maxKeyRounds: Long
|
||||
get() = Int.MAX_VALUE.toLong()
|
||||
|
||||
/*
|
||||
* MEMORY
|
||||
*/
|
||||
|
||||
open fun getMemoryUsage(p: KdfParameters): Long {
|
||||
return UNKNOWN_VALUE.toLong()
|
||||
}
|
||||
@@ -49,9 +63,18 @@ abstract class KdfEngine : ObjectNameResource {
|
||||
// Do nothing by default
|
||||
}
|
||||
|
||||
open fun getDefaultMemoryUsage(): Long {
|
||||
return UNKNOWN_VALUE.toLong()
|
||||
}
|
||||
open val defaultMemoryUsage: Long
|
||||
get() = UNKNOWN_VALUE.toLong()
|
||||
|
||||
open val minMemoryUsage: Long
|
||||
get() = 1
|
||||
|
||||
open val maxMemoryUsage: Long
|
||||
get() = Int.MAX_VALUE.toLong()
|
||||
|
||||
/*
|
||||
* PARALLELISM
|
||||
*/
|
||||
|
||||
open fun getParallelism(p: KdfParameters): Int {
|
||||
return UNKNOWN_VALUE
|
||||
@@ -61,13 +84,16 @@ abstract class KdfEngine : ObjectNameResource {
|
||||
// Do nothing by default
|
||||
}
|
||||
|
||||
open fun getDefaultParallelism(): Int {
|
||||
return UNKNOWN_VALUE
|
||||
}
|
||||
open val defaultParallelism: Int
|
||||
get() = UNKNOWN_VALUE
|
||||
|
||||
open val minParallelism: Int
|
||||
get() = 1
|
||||
|
||||
open val maxParallelism: Int
|
||||
get() = Int.MAX_VALUE
|
||||
|
||||
companion object {
|
||||
|
||||
const val UNKNOWN_VALUE = -1
|
||||
const val UNKNOWN_VALUE_STRING = (-1).toString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,37 +19,7 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.crypto.keyDerivation
|
||||
|
||||
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
object KdfFactory {
|
||||
|
||||
var aesKdf = AesKdf()
|
||||
var argon2Kdf = Argon2Kdf()
|
||||
|
||||
var kdfListV3: MutableList<KdfEngine> = ArrayList()
|
||||
var kdfListV4: MutableList<KdfEngine> = ArrayList()
|
||||
|
||||
init {
|
||||
kdfListV3.add(aesKdf)
|
||||
|
||||
kdfListV4.add(aesKdf)
|
||||
kdfListV4.add(argon2Kdf)
|
||||
}
|
||||
|
||||
@Throws(UnknownKDF::class)
|
||||
fun getEngineV4(kdfParameters: KdfParameters?): KdfEngine {
|
||||
val unknownKDFException = UnknownKDF()
|
||||
if (kdfParameters == null) {
|
||||
throw unknownKDFException
|
||||
}
|
||||
for (engine in kdfListV4) {
|
||||
if (engine.uuid == kdfParameters.uuid) {
|
||||
return engine
|
||||
}
|
||||
}
|
||||
throw unknownKDFException
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ enum class SortNodeEnum {
|
||||
|
||||
override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int {
|
||||
return object1.creationTime.date
|
||||
?.compareTo(object2.creationTime.date) ?: 0
|
||||
.compareTo(object2.creationTime.date)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ enum class SortNodeEnum {
|
||||
|
||||
override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int {
|
||||
return object1.lastModificationTime.date
|
||||
?.compareTo(object2.lastModificationTime.date) ?: 0
|
||||
.compareTo(object2.lastModificationTime.date)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ enum class SortNodeEnum {
|
||||
|
||||
override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int {
|
||||
return object1.lastAccessTime.date
|
||||
?.compareTo(object2.lastAccessTime.date) ?: 0
|
||||
.compareTo(object2.lastAccessTime.date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,17 @@ class UpdateEntryRunnable constructor(
|
||||
: ActionNodeDatabaseRunnable(context, database, finishRunnable, save) {
|
||||
|
||||
// Keep backup of original values in case save fails
|
||||
private var mBackupEntry: EntryVersioned? = null
|
||||
private var mBackupEntryHistory: EntryVersioned? = null
|
||||
|
||||
override fun nodeAction() {
|
||||
mBackupEntry = database.addHistoryBackupTo(mOldEntry)
|
||||
mOldEntry.touch(modified = true, touchParents = true)
|
||||
mNewEntry.touch(modified = true, touchParents = true)
|
||||
|
||||
mBackupEntryHistory = EntryVersioned(mOldEntry)
|
||||
|
||||
// Create an entry history (an entry history don't have history)
|
||||
mNewEntry.addEntryToHistory(EntryVersioned(mOldEntry, copyHistory = false))
|
||||
|
||||
database.removeOldestHistory(mNewEntry)
|
||||
// Update entry with new values
|
||||
mOldEntry.updateWith(mNewEntry)
|
||||
}
|
||||
@@ -45,7 +51,7 @@ class UpdateEntryRunnable constructor(
|
||||
override fun nodeFinish(result: Result): ActionNodeValues {
|
||||
if (!result.isSuccess) {
|
||||
// If we fail to save, back out changes to global structure
|
||||
mBackupEntry?.let {
|
||||
mBackupEntryHistory?.let {
|
||||
mOldEntry.updateWith(it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.webkit.URLUtil
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
|
||||
import com.kunzisoft.keepass.database.NodeHandler
|
||||
import com.kunzisoft.keepass.database.cursor.EntryCursorV3
|
||||
import com.kunzisoft.keepass.database.cursor.EntryCursorV4
|
||||
@@ -88,32 +87,17 @@ class Database {
|
||||
pwDatabaseV4?.defaultUserNameChanged = PwDate()
|
||||
}
|
||||
|
||||
val encryptionAlgorithm: PwEncryptionAlgorithm?
|
||||
get() {
|
||||
return pwDatabaseV4?.encryptionAlgorithm
|
||||
}
|
||||
|
||||
val availableEncryptionAlgorithms: List<PwEncryptionAlgorithm>
|
||||
get() = pwDatabaseV3?.availableEncryptionAlgorithms ?: pwDatabaseV4?.availableEncryptionAlgorithms ?: ArrayList()
|
||||
|
||||
val encryptionAlgorithm: PwEncryptionAlgorithm?
|
||||
get() = pwDatabaseV3?.encryptionAlgorithm ?: pwDatabaseV4?.encryptionAlgorithm
|
||||
|
||||
val availableKdfEngines: List<KdfEngine>
|
||||
get() {
|
||||
if (pwDatabaseV3 != null) {
|
||||
return KdfFactory.kdfListV3
|
||||
}
|
||||
if (pwDatabaseV4 != null) {
|
||||
return KdfFactory.kdfListV4
|
||||
}
|
||||
return ArrayList()
|
||||
}
|
||||
get() = pwDatabaseV3?.kdfAvailableList ?: pwDatabaseV4?.kdfAvailableList ?: ArrayList()
|
||||
|
||||
val kdfEngine: KdfEngine
|
||||
get() {
|
||||
return pwDatabaseV4?.kdfEngine ?: return KdfFactory.aesKdf
|
||||
}
|
||||
|
||||
val numberKeyEncryptionRoundsAsString: String
|
||||
get() = numberKeyEncryptionRounds.toString()
|
||||
val kdfEngine: KdfEngine?
|
||||
get() = pwDatabaseV3?.kdfEngine ?: pwDatabaseV4?.kdfEngine
|
||||
|
||||
var numberKeyEncryptionRounds: Long
|
||||
get() = pwDatabaseV3?.numberKeyEncryptionRounds ?: pwDatabaseV4?.numberKeyEncryptionRounds ?: 0
|
||||
@@ -123,9 +107,6 @@ class Database {
|
||||
pwDatabaseV4?.numberKeyEncryptionRounds = numberRounds
|
||||
}
|
||||
|
||||
val memoryUsageAsString: String
|
||||
get() = memoryUsage.toString()
|
||||
|
||||
var memoryUsage: Long
|
||||
get() {
|
||||
return pwDatabaseV4?.memoryUsage ?: return KdfEngine.UNKNOWN_VALUE.toLong()
|
||||
@@ -134,9 +115,6 @@ class Database {
|
||||
pwDatabaseV4?.memoryUsage = memory
|
||||
}
|
||||
|
||||
val parallelismAsString: String
|
||||
get() = parallelism.toString()
|
||||
|
||||
var parallelism: Int
|
||||
get() = pwDatabaseV4?.parallelism ?: KdfEngine.UNKNOWN_VALUE
|
||||
set(parallelism) {
|
||||
@@ -161,6 +139,25 @@ class Database {
|
||||
return null
|
||||
}
|
||||
|
||||
val manageHistory: Boolean
|
||||
get() = pwDatabaseV4 != null
|
||||
|
||||
var historyMaxItems: Int
|
||||
get() {
|
||||
return pwDatabaseV4?.historyMaxItems ?: 0
|
||||
}
|
||||
set(value) {
|
||||
pwDatabaseV4?.historyMaxItems = value
|
||||
}
|
||||
|
||||
var historyMaxSize: Long
|
||||
get() {
|
||||
return pwDatabaseV4?.historyMaxSize ?: 0
|
||||
}
|
||||
set(value) {
|
||||
pwDatabaseV4?.historyMaxSize = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if RecycleBin is available or not for this version of database
|
||||
* @return true if RecycleBin available
|
||||
@@ -462,12 +459,12 @@ class Database {
|
||||
if (pwDatabaseV4?.kdfParameters?.uuid != kdfEngine.defaultParameters.uuid)
|
||||
pwDatabaseV4?.kdfParameters = kdfEngine.defaultParameters
|
||||
numberKeyEncryptionRounds = kdfEngine.defaultKeyRounds
|
||||
memoryUsage = kdfEngine.getDefaultMemoryUsage()
|
||||
parallelism = kdfEngine.getDefaultParallelism()
|
||||
memoryUsage = kdfEngine.defaultMemoryUsage
|
||||
parallelism = kdfEngine.defaultParallelism
|
||||
}
|
||||
|
||||
fun getKeyDerivationName(resources: Resources): String {
|
||||
return kdfEngine.getName(resources)
|
||||
return kdfEngine?.getName(resources) ?: ""
|
||||
}
|
||||
|
||||
fun validatePasswordEncoding(key: String?): Boolean {
|
||||
@@ -702,31 +699,28 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
fun addHistoryBackupTo(entry: EntryVersioned): EntryVersioned {
|
||||
val backupEntry = EntryVersioned(entry)
|
||||
fun removeOldestHistory(entry: EntryVersioned) {
|
||||
|
||||
entry.addBackupToHistory()
|
||||
|
||||
// Remove oldest backup if more than max items or max memory
|
||||
// Remove oldest history if more than max items or max memory
|
||||
pwDatabaseV4?.let {
|
||||
val history = entry.getHistory()
|
||||
|
||||
val maxItems = it.historyMaxItems
|
||||
val maxItems = historyMaxItems
|
||||
if (maxItems >= 0) {
|
||||
while (history.size > maxItems) {
|
||||
entry.removeOldestEntryFromHistory()
|
||||
}
|
||||
}
|
||||
|
||||
val maxSize = it.historyMaxSize
|
||||
val maxSize = historyMaxSize
|
||||
if (maxSize >= 0) {
|
||||
while (true) {
|
||||
var histSize: Long = 0
|
||||
for (backup in history) {
|
||||
histSize += backup.size
|
||||
var historySize: Long = 0
|
||||
for (entryHistory in history) {
|
||||
historySize += entryHistory.getSize()
|
||||
}
|
||||
|
||||
if (histSize > maxSize) {
|
||||
if (historySize > maxSize) {
|
||||
entry.removeOldestEntryFromHistory()
|
||||
} else {
|
||||
break
|
||||
@@ -734,8 +728,6 @@ class Database {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return backupEntry
|
||||
}
|
||||
|
||||
companion object : SingletonHolder<Database>(::Database) {
|
||||
|
||||
@@ -15,26 +15,26 @@ class EntryVersioned : NodeVersioned, PwEntryInterface<GroupVersioned> {
|
||||
var pwEntryV4: PwEntryV4? = null
|
||||
private set
|
||||
|
||||
fun updateWith(entry: EntryVersioned) {
|
||||
fun updateWith(entry: EntryVersioned, copyHistory: Boolean = true) {
|
||||
entry.pwEntryV3?.let {
|
||||
this.pwEntryV3?.updateWith(it)
|
||||
}
|
||||
entry.pwEntryV4?.let {
|
||||
this.pwEntryV4?.updateWith(it)
|
||||
this.pwEntryV4?.updateWith(it, copyHistory)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this constructor to copy an Entry with exact same values
|
||||
*/
|
||||
constructor(entry: EntryVersioned) {
|
||||
constructor(entry: EntryVersioned, copyHistory: Boolean = true) {
|
||||
if (entry.pwEntryV3 != null) {
|
||||
this.pwEntryV3 = PwEntryV3()
|
||||
}
|
||||
if (entry.pwEntryV4 != null) {
|
||||
this.pwEntryV4 = PwEntryV4()
|
||||
}
|
||||
updateWith(entry)
|
||||
updateWith(entry, copyHistory)
|
||||
}
|
||||
|
||||
constructor(entry: PwEntryV3) {
|
||||
@@ -258,20 +258,31 @@ class EntryVersioned : NodeVersioned, PwEntryInterface<GroupVersioned> {
|
||||
pwEntryV4?.stopToManageFieldReferences()
|
||||
}
|
||||
|
||||
fun addBackupToHistory() {
|
||||
pwEntryV4?.let {
|
||||
val entryHistory = PwEntryV4()
|
||||
entryHistory.updateWith(it)
|
||||
it.addEntryToHistory(entryHistory)
|
||||
fun getHistory(): ArrayList<EntryVersioned> {
|
||||
val history = ArrayList<EntryVersioned>()
|
||||
val entryV4History = pwEntryV4?.history ?: ArrayList()
|
||||
for (entryHistory in entryV4History) {
|
||||
history.add(EntryVersioned(entryHistory))
|
||||
}
|
||||
return history
|
||||
}
|
||||
|
||||
fun addEntryToHistory(entry: EntryVersioned) {
|
||||
entry.pwEntryV4?.let {
|
||||
pwEntryV4?.addEntryToHistory(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeAllHistory() {
|
||||
pwEntryV4?.removeAllHistory()
|
||||
}
|
||||
|
||||
fun removeOldestEntryFromHistory() {
|
||||
pwEntryV4?.removeOldestEntryFromHistory()
|
||||
}
|
||||
|
||||
fun getHistory(): ArrayList<PwEntryV4> {
|
||||
return pwEntryV4?.history ?: ArrayList()
|
||||
fun getSize(): Long {
|
||||
return pwEntryV4?.size ?: 0L
|
||||
}
|
||||
|
||||
fun containsCustomData(): Boolean {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package com.kunzisoft.keepass.database.element
|
||||
|
||||
import android.util.Log
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
|
||||
import com.kunzisoft.keepass.database.exception.KeyFileEmptyException
|
||||
import com.kunzisoft.keepass.utils.MemoryUtil
|
||||
@@ -39,6 +40,10 @@ abstract class PwDatabase<Group : PwGroup<*, Group, Entry>, Entry : PwEntry<Grou
|
||||
// Algorithm used to encrypt the database
|
||||
protected var algorithm: PwEncryptionAlgorithm? = null
|
||||
|
||||
abstract val kdfEngine: KdfEngine?
|
||||
|
||||
abstract val kdfAvailableList: List<KdfEngine>
|
||||
|
||||
var masterKey = ByteArray(32)
|
||||
var finalKey: ByteArray? = null
|
||||
protected set
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
package com.kunzisoft.keepass.database.element
|
||||
|
||||
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
|
||||
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
|
||||
import com.kunzisoft.keepass.stream.NullOutputStream
|
||||
import java.io.IOException
|
||||
@@ -28,18 +30,25 @@ import java.security.DigestOutputStream
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
|
||||
/**
|
||||
* @author Naomaru Itoi <nao></nao>@phoneid.org>
|
||||
* @author Bill Zwicky <wrzwicky></wrzwicky>@pobox.com>
|
||||
* @author Dominik Reichl <dominik.reichl></dominik.reichl>@t-online.de>
|
||||
*/
|
||||
class PwDatabaseV3 : PwDatabase<PwGroupV3, PwEntryV3>() {
|
||||
|
||||
private var numKeyEncRounds: Int = 0
|
||||
|
||||
private var kdfListV3: MutableList<KdfEngine> = ArrayList()
|
||||
|
||||
override val version: String
|
||||
get() = "KeePass 1"
|
||||
|
||||
init {
|
||||
kdfListV3.add(KdfFactory.aesKdf)
|
||||
}
|
||||
|
||||
override val kdfEngine: KdfEngine?
|
||||
get() = kdfListV3[0]
|
||||
|
||||
override val kdfAvailableList: List<KdfEngine>
|
||||
get() = kdfListV3
|
||||
|
||||
override val availableEncryptionAlgorithms: List<PwEncryptionAlgorithm>
|
||||
get() {
|
||||
val list = ArrayList<PwEncryptionAlgorithm>()
|
||||
|
||||
@@ -26,9 +26,7 @@ import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.crypto.CryptoUtil
|
||||
import com.kunzisoft.keepass.crypto.engine.AesEngine
|
||||
import com.kunzisoft.keepass.crypto.engine.CipherEngine
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.*
|
||||
import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
|
||||
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
||||
import com.kunzisoft.keepass.database.file.PwCompressionAlgorithm
|
||||
@@ -51,6 +49,7 @@ class PwDatabaseV4 : PwDatabase<PwGroupV4, PwEntryV4> {
|
||||
private var dataEngine: CipherEngine = AesEngine()
|
||||
var compressionAlgorithm = PwCompressionAlgorithm.Gzip
|
||||
var kdfParameters: KdfParameters? = null
|
||||
private var kdfV4List: MutableList<KdfEngine> = ArrayList()
|
||||
private var numKeyEncRounds: Long = 0
|
||||
var publicCustomData = VariantDictionary()
|
||||
|
||||
@@ -93,6 +92,11 @@ class PwDatabaseV4 : PwDatabase<PwGroupV4, PwEntryV4> {
|
||||
|
||||
var localizedAppName = "KeePassDX" // TODO resource
|
||||
|
||||
init {
|
||||
kdfV4List.add(KdfFactory.aesKdf)
|
||||
kdfV4List.add(KdfFactory.argon2Kdf)
|
||||
}
|
||||
|
||||
constructor()
|
||||
|
||||
constructor(databaseName: String) {
|
||||
@@ -107,6 +111,31 @@ class PwDatabaseV4 : PwDatabase<PwGroupV4, PwEntryV4> {
|
||||
override val version: String
|
||||
get() = "KeePass 2"
|
||||
|
||||
override val kdfEngine: KdfEngine?
|
||||
get() = try {
|
||||
getEngineV4(kdfParameters)
|
||||
} catch (unknownKDF: UnknownKDF) {
|
||||
Log.i(TAG, "Unable to retrieve KDF engine", unknownKDF)
|
||||
null
|
||||
}
|
||||
|
||||
override val kdfAvailableList: List<KdfEngine>
|
||||
get() = kdfV4List
|
||||
|
||||
@Throws(UnknownKDF::class)
|
||||
fun getEngineV4(kdfParameters: KdfParameters?): KdfEngine {
|
||||
val unknownKDFException = UnknownKDF()
|
||||
if (kdfParameters == null) {
|
||||
throw unknownKDFException
|
||||
}
|
||||
for (engine in kdfV4List) {
|
||||
if (engine.uuid == kdfParameters.uuid) {
|
||||
return engine
|
||||
}
|
||||
}
|
||||
throw unknownKDFException
|
||||
}
|
||||
|
||||
override val availableEncryptionAlgorithms: List<PwEncryptionAlgorithm>
|
||||
get() {
|
||||
val list = ArrayList<PwEncryptionAlgorithm>()
|
||||
@@ -116,45 +145,45 @@ class PwDatabaseV4 : PwDatabase<PwGroupV4, PwEntryV4> {
|
||||
return list
|
||||
}
|
||||
|
||||
val kdfEngine: KdfEngine?
|
||||
get() {
|
||||
return try {
|
||||
KdfFactory.getEngineV4(kdfParameters)
|
||||
} catch (unknownKDF: UnknownKDF) {
|
||||
Log.i(TAG, "Unable to retrieve KDF engine", unknownKDF)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override var numberKeyEncryptionRounds: Long
|
||||
get() {
|
||||
val kdfEngine = kdfEngine
|
||||
if (kdfEngine != null && kdfParameters != null)
|
||||
numKeyEncRounds = kdfEngine!!.getKeyRounds(kdfParameters!!)
|
||||
numKeyEncRounds = kdfEngine.getKeyRounds(kdfParameters!!)
|
||||
return numKeyEncRounds
|
||||
}
|
||||
@Throws(NumberFormatException::class)
|
||||
set(rounds) {
|
||||
val kdfEngine = kdfEngine
|
||||
if (kdfEngine != null && kdfParameters != null)
|
||||
kdfEngine!!.setKeyRounds(kdfParameters!!, rounds)
|
||||
kdfEngine.setKeyRounds(kdfParameters!!, rounds)
|
||||
numKeyEncRounds = rounds
|
||||
}
|
||||
|
||||
var memoryUsage: Long
|
||||
get() = if (kdfEngine != null && kdfParameters != null) {
|
||||
kdfEngine!!.getMemoryUsage(kdfParameters!!)
|
||||
} else KdfEngine.UNKNOWN_VALUE.toLong()
|
||||
get() {
|
||||
val kdfEngine = kdfEngine
|
||||
return if (kdfEngine != null && kdfParameters != null) {
|
||||
kdfEngine.getMemoryUsage(kdfParameters!!)
|
||||
} else KdfEngine.UNKNOWN_VALUE.toLong()
|
||||
}
|
||||
set(memory) {
|
||||
val kdfEngine = kdfEngine
|
||||
if (kdfEngine != null && kdfParameters != null)
|
||||
kdfEngine!!.setMemoryUsage(kdfParameters!!, memory)
|
||||
kdfEngine.setMemoryUsage(kdfParameters!!, memory)
|
||||
}
|
||||
|
||||
var parallelism: Int
|
||||
get() = if (kdfEngine != null && kdfParameters != null) {
|
||||
kdfEngine!!.getParallelism(kdfParameters!!)
|
||||
} else KdfEngine.UNKNOWN_VALUE
|
||||
get() {
|
||||
val kdfEngine = kdfEngine
|
||||
return if (kdfEngine != null && kdfParameters != null) {
|
||||
kdfEngine.getParallelism(kdfParameters!!)
|
||||
} else KdfEngine.UNKNOWN_VALUE
|
||||
}
|
||||
set(parallelism) {
|
||||
val kdfEngine = kdfEngine
|
||||
if (kdfEngine != null && kdfParameters != null)
|
||||
kdfEngine!!.setParallelism(kdfParameters!!, parallelism)
|
||||
kdfEngine.setParallelism(kdfParameters!!, parallelism)
|
||||
}
|
||||
|
||||
override val passwordEncoding: String
|
||||
@@ -227,7 +256,7 @@ class PwDatabaseV4 : PwDatabase<PwGroupV4, PwEntryV4> {
|
||||
fun makeFinalKey(masterSeed: ByteArray) {
|
||||
|
||||
kdfParameters?.let { keyDerivationFunctionParameters ->
|
||||
val kdfEngine = KdfFactory.getEngineV4(keyDerivationFunctionParameters)
|
||||
val kdfEngine = getEngineV4(keyDerivationFunctionParameters)
|
||||
|
||||
var transformedMasterKey = kdfEngine.transform(masterKey, keyDerivationFunctionParameters)
|
||||
if (transformedMasterKey.size != 32) {
|
||||
@@ -360,9 +389,7 @@ class PwDatabaseV4 : PwDatabase<PwGroupV4, PwEntryV4> {
|
||||
}
|
||||
addGroupTo(recycleBinGroup, rootGroup)
|
||||
recycleBinUUID = recycleBinGroup.id
|
||||
recycleBinGroup.lastModificationTime.date?.let {
|
||||
recycleBinChanged = it
|
||||
}
|
||||
recycleBinChanged = recycleBinGroup.lastModificationTime.date
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,14 +19,12 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.element
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import com.kunzisoft.keepass.utils.Types
|
||||
|
||||
import java.util.Arrays
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Converting from the C Date format to the Java data format is
|
||||
@@ -34,14 +32,14 @@ import java.util.Date
|
||||
*/
|
||||
class PwDate : Parcelable {
|
||||
|
||||
private var jDate: Date? = null
|
||||
private var jDate: Date = Date()
|
||||
private var jDateBuilt = false
|
||||
@Transient
|
||||
private var cDate: ByteArray? = null
|
||||
@Transient
|
||||
private var cDateBuilt = false
|
||||
|
||||
val date: Date?
|
||||
val date: Date
|
||||
get() {
|
||||
if (!jDateBuilt) {
|
||||
jDate = readTime(cDate, 0, calendar)
|
||||
@@ -68,9 +66,7 @@ class PwDate : Parcelable {
|
||||
}
|
||||
|
||||
constructor(source: PwDate) {
|
||||
if (source.jDate != null) {
|
||||
this.jDate = Date(source.jDate!!.time)
|
||||
}
|
||||
this.jDate = Date(source.jDate.time)
|
||||
this.jDateBuilt = source.jDateBuilt
|
||||
|
||||
if (source.cDate != null) {
|
||||
@@ -106,6 +102,10 @@ class PwDate : Parcelable {
|
||||
return 0
|
||||
}
|
||||
|
||||
fun getDateTimeString(resources: Resources): String {
|
||||
return Companion.getDateTimeString(resources, this.date)
|
||||
}
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeSerializable(date)
|
||||
dest.writeByte((if (jDateBuilt) 1 else 0).toByte())
|
||||
@@ -135,7 +135,7 @@ class PwDate : Parcelable {
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = jDate?.hashCode() ?: 0
|
||||
var result = jDate.hashCode()
|
||||
result = 31 * result + jDateBuilt.hashCode()
|
||||
result = 31 * result + (cDate?.contentHashCode() ?: 0)
|
||||
result = 31 * result + cDateBuilt.hashCode()
|
||||
@@ -280,5 +280,13 @@ class PwDate : Parcelable {
|
||||
cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND)
|
||||
|
||||
}
|
||||
|
||||
fun getDateTimeString(resources: Resources, date: Date): String {
|
||||
return java.text.DateFormat.getDateTimeInstance(
|
||||
java.text.DateFormat.MEDIUM,
|
||||
java.text.DateFormat.MEDIUM,
|
||||
ConfigurationCompat.getLocales(resources.configuration)[0])
|
||||
.format(date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, PwNodeV4Interface {
|
||||
* Update with deep copy of each entry element
|
||||
* @param source
|
||||
*/
|
||||
fun updateWith(source: PwEntryV4) {
|
||||
fun updateWith(source: PwEntryV4, copyHistory: Boolean = true) {
|
||||
super.updateWith(source)
|
||||
iconCustom = PwIconCustom(source.iconCustom)
|
||||
usageCount = source.usageCount
|
||||
@@ -146,7 +146,8 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, PwNodeV4Interface {
|
||||
overrideURL = source.overrideURL
|
||||
autoType = AutoType(source.autoType)
|
||||
history.clear()
|
||||
history.addAll(source.history)
|
||||
if (copyHistory)
|
||||
history.addAll(source.history)
|
||||
url = source.url
|
||||
additional = source.additional
|
||||
tags = source.tags
|
||||
@@ -287,6 +288,10 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, PwNodeV4Interface {
|
||||
history.add(entry)
|
||||
}
|
||||
|
||||
fun removeAllHistory() {
|
||||
history.clear()
|
||||
}
|
||||
|
||||
fun removeOldestEntryFromHistory() {
|
||||
var min: Date? = null
|
||||
var index = -1
|
||||
@@ -294,7 +299,7 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, PwNodeV4Interface {
|
||||
for (i in history.indices) {
|
||||
val entry = history[i]
|
||||
val lastMod = entry.lastModificationTime.date
|
||||
if (min == null || lastMod == null || lastMod.before(min)) {
|
||||
if (min == null || lastMod.before(min)) {
|
||||
index = i
|
||||
min = lastMod
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ abstract class PwNode<IdType, Parent : PwGroupInterface<Parent, Entry>, Entry :
|
||||
final override var isExpires: Boolean
|
||||
// If expireDate is before NEVER_EXPIRE date less 1 month (to be sure)
|
||||
get() = expiryTime.date
|
||||
?.before(LocalDate.fromDateFields(PwDate.NEVER_EXPIRE).minusMonths(1).toDate()) ?: true
|
||||
.before(LocalDate.fromDateFields(PwDate.NEVER_EXPIRE).minusMonths(1).toDate())
|
||||
set(value) {
|
||||
if (!value) {
|
||||
expiryTime = PwDate.PW_NEVER_EXPIRE
|
||||
|
||||
@@ -51,10 +51,10 @@ class PwDbHeaderV4(private val databaseV4: PwDatabaseV4) : PwDbHeader() {
|
||||
|
||||
// version < FILE_VERSION_32_4)
|
||||
var transformSeed: ByteArray?
|
||||
get() = databaseV4.kdfParameters?.getByteArray(AesKdf.ParamSeed)
|
||||
get() = databaseV4.kdfParameters?.getByteArray(AesKdf.PARAM_SEED)
|
||||
private set(seed) {
|
||||
assignAesKdfEngineIfNotExists()
|
||||
databaseV4.kdfParameters?.setByteArray(AesKdf.ParamSeed, seed)
|
||||
databaseV4.kdfParameters?.setByteArray(AesKdf.PARAM_SEED, seed)
|
||||
}
|
||||
|
||||
object PwDbHeaderV4Fields {
|
||||
@@ -229,7 +229,9 @@ class PwDbHeaderV4(private val databaseV4: PwDatabaseV4) : PwDbHeader() {
|
||||
}
|
||||
|
||||
private fun assignAesKdfEngineIfNotExists() {
|
||||
if (databaseV4.kdfParameters == null || databaseV4.kdfParameters!!.uuid != KdfFactory.aesKdf.uuid) {
|
||||
val kdfParams = databaseV4.kdfParameters
|
||||
if (kdfParams == null
|
||||
|| kdfParams.uuid != KdfFactory.aesKdf.uuid) {
|
||||
databaseV4.kdfParameters = KdfFactory.aesKdf.defaultParameters
|
||||
}
|
||||
}
|
||||
@@ -246,7 +248,7 @@ class PwDbHeaderV4(private val databaseV4: PwDatabaseV4) : PwDbHeader() {
|
||||
private fun setTransformRound(roundsByte: ByteArray?) {
|
||||
assignAesKdfEngineIfNotExists()
|
||||
val rounds = LEDataInputStream.readLong(roundsByte!!, 0)
|
||||
databaseV4.kdfParameters?.setUInt64(AesKdf.ParamRounds, rounds)
|
||||
databaseV4.kdfParameters?.setUInt64(AesKdf.PARAM_ROUNDS, rounds)
|
||||
databaseV4.numberKeyEncryptionRounds = rounds
|
||||
}
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
|
||||
}
|
||||
|
||||
try {
|
||||
val kdf = KdfFactory.getEngineV4(mDatabaseV4.kdfParameters)
|
||||
val kdf = mDatabaseV4.getEngineV4(mDatabaseV4.kdfParameters)
|
||||
kdf.randomize(mDatabaseV4.kdfParameters!!)
|
||||
} catch (unknownKDF: UnknownKDF) {
|
||||
Log.e(TAG, "Unable to retrieve header", unknownKDF)
|
||||
|
||||
@@ -48,10 +48,7 @@ import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.education.Education
|
||||
import com.kunzisoft.keepass.biometric.BiometricUnlockDatabaseHelper
|
||||
import com.kunzisoft.keepass.icons.IconPackChooser
|
||||
import com.kunzisoft.keepass.settings.preference.DialogListExplanationPreference
|
||||
import com.kunzisoft.keepass.settings.preference.IconPackListPreference
|
||||
import com.kunzisoft.keepass.settings.preference.InputNumberPreference
|
||||
import com.kunzisoft.keepass.settings.preference.InputTextPreference
|
||||
import com.kunzisoft.keepass.settings.preference.*
|
||||
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
|
||||
|
||||
class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
|
||||
@@ -61,9 +58,9 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
|
||||
private var mCount = 0
|
||||
|
||||
private var mRoundPref: InputNumberPreference? = null
|
||||
private var mMemoryPref: InputNumberPreference? = null
|
||||
private var mParallelismPref: InputNumberPreference? = null
|
||||
private var mRoundPref: InputKdfNumberPreference? = null
|
||||
private var mMemoryPref: InputKdfNumberPreference? = null
|
||||
private var mParallelismPref: InputKdfNumberPreference? = null
|
||||
|
||||
enum class Screen {
|
||||
APPLICATION, FORM_FILLING, ADVANCED_UNLOCK, DATABASE, APPEARANCE
|
||||
@@ -74,7 +71,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
|
||||
activity?.let { activity ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val autoFillEnablePreference: SwitchPreference? = findPreference<SwitchPreference>(getString(R.string.settings_autofill_enable_key))
|
||||
val autoFillEnablePreference: SwitchPreference? = findPreference(getString(R.string.settings_autofill_enable_key))
|
||||
if (autoFillEnablePreference != null) {
|
||||
val autofillManager = activity.getSystemService(AutofillManager::class.java)
|
||||
autoFillEnablePreference.isChecked = autofillManager != null
|
||||
@@ -139,7 +136,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
setPreferencesFromResource(R.xml.preferences_form_filling, rootKey)
|
||||
|
||||
activity?.let { activity ->
|
||||
val autoFillEnablePreference: SwitchPreference? = findPreference<SwitchPreference>(getString(R.string.settings_autofill_enable_key))
|
||||
val autoFillEnablePreference: SwitchPreference? = findPreference(getString(R.string.settings_autofill_enable_key))
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val autofillManager = activity.getSystemService(AutofillManager::class.java)
|
||||
if (autofillManager != null && autofillManager.hasEnabledAutofillServices())
|
||||
@@ -217,7 +214,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
setPreferencesFromResource(R.xml.preferences_advanced_unlock, rootKey)
|
||||
|
||||
activity?.let { activity ->
|
||||
val biometricUnlockEnablePreference: SwitchPreference? = findPreference<SwitchPreference>(getString(R.string.biometric_unlock_enable_key))
|
||||
val biometricUnlockEnablePreference: SwitchPreference? = findPreference(getString(R.string.biometric_unlock_enable_key))
|
||||
// < M solve verifyError exception
|
||||
var biometricUnlockSupported = false
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
@@ -240,7 +237,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
}
|
||||
}
|
||||
|
||||
val deleteKeysFingerprints: Preference? = findPreference<Preference>(getString(R.string.biometric_delete_all_key_key))
|
||||
val deleteKeysFingerprints: Preference? = findPreference(getString(R.string.biometric_delete_all_key_key))
|
||||
if (!biometricUnlockSupported) {
|
||||
deleteKeysFingerprints?.isEnabled = false
|
||||
} else {
|
||||
@@ -338,10 +335,10 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
|
||||
if (mDatabase.loaded) {
|
||||
|
||||
val dbGeneralPrefCategory: PreferenceCategory? = findPreference<PreferenceCategory>(getString(R.string.database_general_key))
|
||||
val dbGeneralPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_general_key))
|
||||
|
||||
// Db name
|
||||
val dbNamePref: InputTextPreference? = findPreference<InputTextPreference>(getString(R.string.database_name_key))
|
||||
val dbNamePref: InputTextPreference? = findPreference(getString(R.string.database_name_key))
|
||||
if (mDatabase.containsName()) {
|
||||
dbNamePref?.summary = mDatabase.name
|
||||
} else {
|
||||
@@ -349,7 +346,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
}
|
||||
|
||||
// Db description
|
||||
val dbDescriptionPref: InputTextPreference? = findPreference<InputTextPreference>(getString(R.string.database_description_key))
|
||||
val dbDescriptionPref: InputTextPreference? = findPreference(getString(R.string.database_description_key))
|
||||
if (mDatabase.containsDescription()) {
|
||||
dbDescriptionPref?.summary = mDatabase.description
|
||||
} else {
|
||||
@@ -357,7 +354,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
}
|
||||
|
||||
// Recycle bin
|
||||
val recycleBinPref: SwitchPreference? = findPreference<SwitchPreference>(getString(R.string.recycle_bin_key))
|
||||
val recycleBinPref: SwitchPreference? = findPreference(getString(R.string.recycle_bin_key))
|
||||
// TODO Recycle
|
||||
dbGeneralPrefCategory?.removePreference(recycleBinPref) // To delete
|
||||
if (mDatabase.isRecycleBinAvailable) {
|
||||
@@ -371,6 +368,17 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
findPreference<Preference>(getString(R.string.database_version_key))
|
||||
?.summary = mDatabase.getVersion()
|
||||
|
||||
findPreference<PreferenceCategory>(getString(R.string.database_history_key))
|
||||
?.isVisible = mDatabase.manageHistory == true
|
||||
|
||||
// Max history items
|
||||
findPreference<InputNumberPreference>(getString(R.string.max_history_items_key))
|
||||
?.summary = mDatabase.historyMaxItems.toString()
|
||||
|
||||
// Max history size
|
||||
findPreference<InputNumberPreference>(getString(R.string.max_history_size_key))
|
||||
?.summary = mDatabase.historyMaxSize.toString()
|
||||
|
||||
// Encryption Algorithm
|
||||
findPreference<DialogListExplanationPreference>(getString(R.string.encryption_algorithm_key))
|
||||
?.summary = mDatabase.getEncryptionAlgorithmName(resources)
|
||||
@@ -380,16 +388,16 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
?.summary = mDatabase.getKeyDerivationName(resources)
|
||||
|
||||
// Round encryption
|
||||
mRoundPref = findPreference<InputNumberPreference>(getString(R.string.transform_rounds_key))
|
||||
mRoundPref?.summary = mDatabase.numberKeyEncryptionRoundsAsString
|
||||
mRoundPref = findPreference(getString(R.string.transform_rounds_key))
|
||||
mRoundPref?.summary = mDatabase.numberKeyEncryptionRounds.toString()
|
||||
|
||||
// Memory Usage
|
||||
mMemoryPref = findPreference<InputNumberPreference>(getString(R.string.memory_usage_key))
|
||||
mMemoryPref?.summary = mDatabase.memoryUsageAsString
|
||||
mMemoryPref = findPreference(getString(R.string.memory_usage_key))
|
||||
mMemoryPref?.summary = mDatabase.memoryUsage.toString()
|
||||
|
||||
// Parallelism
|
||||
mParallelismPref = findPreference<InputNumberPreference>(getString(R.string.parallelism_key))
|
||||
mParallelismPref?.summary = mDatabase.parallelismAsString
|
||||
mParallelismPref = findPreference(getString(R.string.parallelism_key))
|
||||
mParallelismPref?.summary = mDatabase.parallelism.toString()
|
||||
|
||||
} else {
|
||||
Log.e(javaClass.name, "Database isn't ready")
|
||||
@@ -397,7 +405,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
}
|
||||
|
||||
private fun allowCopyPassword() {
|
||||
val copyPasswordPreference: SwitchPreference? = findPreference<SwitchPreference>(getString(R.string.allow_copy_password_key))
|
||||
val copyPasswordPreference: SwitchPreference? = findPreference(getString(R.string.allow_copy_password_key))
|
||||
copyPasswordPreference?.setOnPreferenceChangeListener { _, newValue ->
|
||||
if (newValue as Boolean && context != null) {
|
||||
val message = getString(R.string.allow_copy_password_warning) +
|
||||
@@ -461,6 +469,12 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
preference.key == getString(R.string.database_description_key) -> {
|
||||
dialogFragment = DatabaseDescriptionPreferenceDialogFragmentCompat.newInstance(preference.key)
|
||||
}
|
||||
preference.key == getString(R.string.max_history_items_key) -> {
|
||||
dialogFragment = MaxHistoryItemsPreferenceDialogFragmentCompat.newInstance(preference.key)
|
||||
}
|
||||
preference.key == getString(R.string.max_history_size_key) -> {
|
||||
dialogFragment = MaxHistorySizePreferenceDialogFragmentCompat.newInstance(preference.key)
|
||||
}
|
||||
preference.key == getString(R.string.encryption_algorithm_key) -> {
|
||||
dialogFragment = DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.newInstance(preference.key)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.settings.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
|
||||
class InputKdfNumberPreference @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = R.attr.dialogPreferenceStyle,
|
||||
defStyleRes: Int = defStyleAttr)
|
||||
: InputTextPreference(context, attrs, defStyleAttr, defStyleRes) {
|
||||
|
||||
override fun setSummary(summary: CharSequence) {
|
||||
if (summary == UNKNOWN_VALUE_STRING) {
|
||||
isEnabled = false
|
||||
super.setSummary("")
|
||||
} else {
|
||||
isEnabled = true
|
||||
super.setSummary(summary)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val UNKNOWN_VALUE_STRING = KdfEngine.UNKNOWN_VALUE.toString()
|
||||
}
|
||||
}
|
||||
@@ -20,66 +20,28 @@
|
||||
package com.kunzisoft.keepass.settings.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.util.AttributeSet
|
||||
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
|
||||
class InputNumberPreference @JvmOverloads constructor(context: Context,
|
||||
open class InputNumberPreference @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = R.attr.dialogPreferenceStyle,
|
||||
defStyleRes: Int = defStyleAttr)
|
||||
: InputTextExplanationPreference(context, attrs, defStyleAttr, defStyleRes) {
|
||||
|
||||
// Save to Shared Preferences
|
||||
var number: Long = 0
|
||||
set(number) {
|
||||
field = number
|
||||
persistLong(number)
|
||||
}
|
||||
: InputTextPreference(context, attrs, defStyleAttr, defStyleRes) {
|
||||
|
||||
override fun getDialogLayoutResource(): Int {
|
||||
return R.layout.pref_dialog_numbers
|
||||
return R.layout.pref_dialog_input_numbers
|
||||
}
|
||||
|
||||
override fun setSummary(summary: CharSequence) {
|
||||
if (summary == KdfEngine.UNKNOWN_VALUE_STRING) {
|
||||
isEnabled = false
|
||||
if (summary == INFINITE_VALUE_STRING) {
|
||||
super.setSummary("")
|
||||
} else {
|
||||
isEnabled = true
|
||||
super.setSummary(summary)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetDefaultValue(a: TypedArray?, index: Int): Any {
|
||||
// Default value from attribute. Fallback value is set to 0.
|
||||
return a?.getInt(index, 0) ?: 0
|
||||
companion object {
|
||||
const val INFINITE_VALUE_STRING = "-1"
|
||||
}
|
||||
|
||||
override fun onSetInitialValue(restorePersistedValue: Boolean,
|
||||
defaultValue: Any?) {
|
||||
// Read the value. Use the default value if it is not possible.
|
||||
var numberValue: Long
|
||||
if (!restorePersistedValue) {
|
||||
numberValue = 100000
|
||||
if (defaultValue is String) {
|
||||
numberValue = java.lang.Long.parseLong(defaultValue)
|
||||
}
|
||||
if (defaultValue is Int) {
|
||||
numberValue = defaultValue.toLong()
|
||||
}
|
||||
try {
|
||||
numberValue = defaultValue as Long
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} else {
|
||||
numberValue = getPersistedLong(this.number)
|
||||
}
|
||||
|
||||
number = numberValue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.kunzisoft.keepass.settings.preference
|
||||
|
||||
import android.content.Context
|
||||
import androidx.preference.DialogPreference
|
||||
import android.util.AttributeSet
|
||||
|
||||
import com.kunzisoft.keepass.R
|
||||
|
||||
open class InputTextExplanationPreference @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = R.attr.dialogPreferenceStyle,
|
||||
defStyleRes: Int = defStyleAttr)
|
||||
: DialogPreference(context, attrs, defStyleAttr, defStyleRes) {
|
||||
|
||||
var explanation: String? = null
|
||||
|
||||
init {
|
||||
val styleAttributes = context.theme.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.explanationDialog,
|
||||
0, 0)
|
||||
try {
|
||||
explanation = styleAttributes.getString(R.styleable.explanationDialog_explanations)
|
||||
} finally {
|
||||
styleAttributes.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDialogLayoutResource(): Int {
|
||||
return R.layout.pref_dialog_input_text_explanation
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,10 @@ import android.util.AttributeSet
|
||||
|
||||
import com.kunzisoft.keepass.R
|
||||
|
||||
class InputTextPreference @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = R.attr.dialogPreferenceStyle,
|
||||
defStyleRes: Int = defStyleAttr)
|
||||
open class InputTextPreference @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = R.attr.dialogPreferenceStyle,
|
||||
defStyleRes: Int = defStyleAttr)
|
||||
: DialogPreference(context, attrs, defStyleAttr, defStyleRes) {
|
||||
|
||||
override fun getDialogLayoutResource(): Int {
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
class DatabaseDescriptionPreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
|
||||
class DatabaseDescriptionPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
@@ -48,10 +48,13 @@ class DatabaseDescriptionPreferenceDialogFragmentCompat : InputDatabaseSavePrefe
|
||||
: ActionRunnable() {
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
val descriptionToShow = mNewDescription
|
||||
if (!result.isSuccess) {
|
||||
database?.assignDescription(mOldDescription)
|
||||
}
|
||||
val descriptionToShow =
|
||||
if (result.isSuccess) {
|
||||
mNewDescription
|
||||
} else {
|
||||
database?.assignDescription(mOldDescription)
|
||||
mOldDescription
|
||||
}
|
||||
preference.summary = descriptionToShow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,17 +56,21 @@ class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
if (database != null && positiveResult && database!!.allowEncryptionAlgorithmModification()) {
|
||||
|
||||
if (algorithmSelected != null) {
|
||||
val newAlgorithm = algorithmSelected
|
||||
val oldAlgorithm = database?.encryptionAlgorithm
|
||||
newAlgorithm?.let {
|
||||
database?.assignEncryptionAlgorithm(it)
|
||||
if (positiveResult) {
|
||||
database?.let { database ->
|
||||
if (database.allowEncryptionAlgorithmModification()) {
|
||||
if (algorithmSelected != null) {
|
||||
val newAlgorithm = algorithmSelected
|
||||
val oldAlgorithm = database.encryptionAlgorithm
|
||||
newAlgorithm?.let {
|
||||
database.assignEncryptionAlgorithm(it)
|
||||
}
|
||||
|
||||
if (oldAlgorithm != null && newAlgorithm != null)
|
||||
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newAlgorithm, oldAlgorithm)
|
||||
}
|
||||
}
|
||||
|
||||
if (oldAlgorithm != null && newAlgorithm != null)
|
||||
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newAlgorithm, oldAlgorithm)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,11 +86,13 @@ class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
|
||||
: ActionRunnable() {
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
var algorithmToShow = mNewAlgorithm
|
||||
if (!result.isSuccess) {
|
||||
database?.assignEncryptionAlgorithm(mOldAlgorithm)
|
||||
algorithmToShow = mOldAlgorithm
|
||||
}
|
||||
val algorithmToShow =
|
||||
if (result.isSuccess) {
|
||||
mNewAlgorithm
|
||||
} else {
|
||||
database?.assignEncryptionAlgorithm(mOldAlgorithm)
|
||||
mOldAlgorithm
|
||||
}
|
||||
preference.summary = algorithmToShow.getName(settingsResources)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,21 +52,24 @@ class DatabaseKeyDerivationPreferenceDialogFragmentCompat
|
||||
recyclerView.adapter = kdfAdapter
|
||||
|
||||
database?.let { database ->
|
||||
kdfEngineSelected = database.kdfEngine
|
||||
if (kdfEngineSelected != null)
|
||||
kdfAdapter.setItems(database.availableKdfEngines, kdfEngineSelected!!)
|
||||
kdfEngineSelected = database.kdfEngine?.apply {
|
||||
kdfAdapter.setItems(database.availableKdfEngines, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
if (database != null && positiveResult && database!!.allowKdfModification()) {
|
||||
if (kdfEngineSelected != null) {
|
||||
val newKdfEngine = kdfEngineSelected!!
|
||||
val oldKdfEngine = database!!.kdfEngine
|
||||
database?.assignKdfEngine(newKdfEngine)
|
||||
|
||||
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newKdfEngine, oldKdfEngine)
|
||||
if (positiveResult) {
|
||||
database?.let { database ->
|
||||
if (database.allowKdfModification()) {
|
||||
val newKdfEngine = kdfEngineSelected
|
||||
val oldKdfEngine = database.kdfEngine
|
||||
if (newKdfEngine != null && oldKdfEngine != null) {
|
||||
database.assignKdfEngine(newKdfEngine)
|
||||
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newKdfEngine, oldKdfEngine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,17 +97,19 @@ class DatabaseKeyDerivationPreferenceDialogFragmentCompat
|
||||
: ActionRunnable() {
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
val kdfEngineToShow = mNewKdfEngine
|
||||
|
||||
if (!result.isSuccess) {
|
||||
database?.assignKdfEngine(mOldKdfEngine)
|
||||
}
|
||||
val kdfEngineToShow =
|
||||
if (result.isSuccess) {
|
||||
mNewKdfEngine
|
||||
} else {
|
||||
database?.assignKdfEngine(mOldKdfEngine)
|
||||
mOldKdfEngine
|
||||
}
|
||||
preference.summary = kdfEngineToShow.getName(settingsResources)
|
||||
|
||||
roundPreference?.summary = kdfEngineToShow.defaultKeyRounds.toString()
|
||||
// Disable memory and parallelism if not available
|
||||
memoryPreference?.summary = kdfEngineToShow.getDefaultMemoryUsage().toString()
|
||||
parallelismPreference?.summary = kdfEngineToShow.getDefaultParallelism().toString()
|
||||
memoryPreference?.summary = kdfEngineToShow.defaultMemoryUsage.toString()
|
||||
parallelismPreference?.summary = kdfEngineToShow.defaultParallelism.toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
class DatabaseNamePreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
|
||||
class DatabaseNamePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
@@ -32,12 +32,14 @@ class DatabaseNamePreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDi
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
if (database != null && positiveResult) {
|
||||
val newName = inputText
|
||||
val oldName = database!!.name
|
||||
database?.assignName(newName)
|
||||
if (positiveResult) {
|
||||
database?.let { database ->
|
||||
val newName = inputText
|
||||
val oldName = database.name
|
||||
database.assignName(newName)
|
||||
|
||||
actionInUIThreadAfterSaveDatabase = AfterNameSave(newName, oldName)
|
||||
actionInUIThreadAfterSaveDatabase = AfterNameSave(newName, oldName)
|
||||
}
|
||||
}
|
||||
|
||||
super.onDialogClosed(positiveResult)
|
||||
@@ -48,10 +50,13 @@ class DatabaseNamePreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDi
|
||||
: ActionRunnable() {
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
val nameToShow = mNewName
|
||||
if (!result.isSuccess) {
|
||||
database?.assignName(mOldName)
|
||||
}
|
||||
val nameToShow =
|
||||
if (result.isSuccess) {
|
||||
mNewName
|
||||
} else {
|
||||
database?.assignName(mOldName)
|
||||
mOldName
|
||||
}
|
||||
preference.summary = nameToShow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
|
||||
import com.kunzisoft.keepass.R
|
||||
|
||||
open class InputDatabaseSavePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||
|
||||
private var inputTextView: EditText? = null
|
||||
|
||||
var inputText: String
|
||||
get() = this.inputTextView?.text?.toString() ?: ""
|
||||
set(inputText) {
|
||||
if (inputTextView != null) {
|
||||
this.inputTextView?.setText(inputText)
|
||||
this.inputTextView?.setSelection(this.inputTextView!!.text.length)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
|
||||
inputTextView = view.findViewById(R.id.input_text)
|
||||
}
|
||||
}
|
||||
@@ -19,36 +19,71 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.preference.PreferenceDialogFragmentCompat
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
|
||||
import com.kunzisoft.keepass.R
|
||||
|
||||
abstract class InputPreferenceDialogFragmentCompat : PreferenceDialogFragmentCompat() {
|
||||
|
||||
private var inputTextView: EditText? = null
|
||||
private var textExplanationView: TextView? = null
|
||||
private var switchElementView: CompoundButton? = null
|
||||
|
||||
var inputText: String
|
||||
get() = this.inputTextView?.text?.toString() ?: ""
|
||||
set(inputText) {
|
||||
if (inputTextView != null) {
|
||||
this.inputTextView?.setText(inputText)
|
||||
this.inputTextView?.setSelection(this.inputTextView!!.text.length)
|
||||
}
|
||||
}
|
||||
|
||||
var explanationText: String?
|
||||
get() = textExplanationView?.text?.toString() ?: ""
|
||||
set(explanationText) {
|
||||
if (explanationText != null && explanationText.isNotEmpty()) {
|
||||
textExplanationView?.text = explanationText
|
||||
textExplanationView?.visibility = View.VISIBLE
|
||||
} else {
|
||||
textExplanationView?.text = explanationText
|
||||
textExplanationView?.visibility = View.VISIBLE
|
||||
textExplanationView?.apply {
|
||||
if (explanationText != null && explanationText.isNotEmpty()) {
|
||||
text = explanationText
|
||||
visibility = View.VISIBLE
|
||||
} else {
|
||||
text = ""
|
||||
visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
|
||||
inputTextView = view.findViewById(R.id.input_text)
|
||||
textExplanationView = view.findViewById(R.id.explanation_text)
|
||||
textExplanationView?.visibility = View.GONE
|
||||
switchElementView = view.findViewById(R.id.switch_element)
|
||||
switchElementView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
fun setInoutText(@StringRes inputTextId: Int) {
|
||||
inputText = getString(inputTextId)
|
||||
}
|
||||
|
||||
fun showInputText(show: Boolean) {
|
||||
inputTextView?.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
fun setExplanationText(@StringRes explanationTextId: Int) {
|
||||
explanationText = getString(explanationTextId)
|
||||
}
|
||||
|
||||
fun setSwitchAction(onCheckedChange: ((isChecked: Boolean)-> Unit)?, defaultChecked: Boolean) {
|
||||
switchElementView?.visibility = if (onCheckedChange == null) View.GONE else View.VISIBLE
|
||||
switchElementView?.isChecked = defaultChecked
|
||||
inputTextView?.visibility = if (defaultChecked) View.VISIBLE else View.GONE
|
||||
switchElementView?.setOnCheckedChangeListener { _, isChecked ->
|
||||
onCheckedChange?.invoke(isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
class MaxHistoryItemsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
|
||||
setExplanationText(R.string.max_history_items_summary)
|
||||
database?.historyMaxItems?.let { maxItemsDatabase ->
|
||||
inputText = maxItemsDatabase.toString()
|
||||
setSwitchAction({ isChecked ->
|
||||
inputText = if (!isChecked) {
|
||||
INFINITE_MAX_HISTORY_ITEMS.toString()
|
||||
} else
|
||||
DEFAULT_MAX_HISTORY_ITEMS.toString()
|
||||
showInputText(isChecked)
|
||||
}, maxItemsDatabase > INFINITE_MAX_HISTORY_ITEMS)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
if (positiveResult) {
|
||||
database?.let { database ->
|
||||
var maxHistoryItems: Int = try {
|
||||
inputText.toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
DEFAULT_MAX_HISTORY_ITEMS
|
||||
}
|
||||
if (maxHistoryItems < INFINITE_MAX_HISTORY_ITEMS) {
|
||||
maxHistoryItems = INFINITE_MAX_HISTORY_ITEMS
|
||||
}
|
||||
|
||||
val oldMaxHistoryItems = database.historyMaxItems
|
||||
database.historyMaxItems = maxHistoryItems
|
||||
|
||||
actionInUIThreadAfterSaveDatabase = AfterMaxHistoryItemsSave(maxHistoryItems, oldMaxHistoryItems)
|
||||
}
|
||||
}
|
||||
|
||||
super.onDialogClosed(positiveResult)
|
||||
}
|
||||
|
||||
private inner class AfterMaxHistoryItemsSave(private val mNewMaxHistoryItems: Int,
|
||||
private val mOldMaxHistoryItems: Int)
|
||||
: ActionRunnable() {
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
val maxHistoryItemsToShow =
|
||||
if (result.isSuccess) {
|
||||
mNewMaxHistoryItems
|
||||
} else {
|
||||
database?.historyMaxItems = mOldMaxHistoryItems
|
||||
mOldMaxHistoryItems
|
||||
}
|
||||
preference.summary = maxHistoryItemsToShow.toString()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val DEFAULT_MAX_HISTORY_ITEMS = 10
|
||||
const val INFINITE_MAX_HISTORY_ITEMS = -1
|
||||
|
||||
fun newInstance(key: String): MaxHistoryItemsPreferenceDialogFragmentCompat {
|
||||
val fragment = MaxHistoryItemsPreferenceDialogFragmentCompat()
|
||||
val bundle = Bundle(1)
|
||||
bundle.putString(ARG_KEY, key)
|
||||
fragment.arguments = bundle
|
||||
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
class MaxHistorySizePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
|
||||
setExplanationText(R.string.max_history_size_summary)
|
||||
database?.historyMaxSize?.let { maxItemsDatabase ->
|
||||
inputText = maxItemsDatabase.toString()
|
||||
setSwitchAction({ isChecked ->
|
||||
inputText = if (!isChecked) {
|
||||
INFINITE_MAX_HISTORY_SIZE.toString()
|
||||
} else
|
||||
DEFAULT_MAX_HISTORY_SIZE.toString()
|
||||
showInputText(isChecked)
|
||||
}, maxItemsDatabase > INFINITE_MAX_HISTORY_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
if (positiveResult) {
|
||||
database?.let { database ->
|
||||
var maxHistorySize: Long = try {
|
||||
inputText.toLong()
|
||||
} catch (e: NumberFormatException) {
|
||||
DEFAULT_MAX_HISTORY_SIZE
|
||||
}
|
||||
if (maxHistorySize < INFINITE_MAX_HISTORY_SIZE) {
|
||||
maxHistorySize = INFINITE_MAX_HISTORY_SIZE
|
||||
}
|
||||
|
||||
val oldMaxHistorySize = database.historyMaxSize
|
||||
database.historyMaxSize = maxHistorySize
|
||||
|
||||
actionInUIThreadAfterSaveDatabase = AfterMaxHistorySizeSave(maxHistorySize, oldMaxHistorySize)
|
||||
}
|
||||
}
|
||||
|
||||
super.onDialogClosed(positiveResult)
|
||||
}
|
||||
|
||||
private inner class AfterMaxHistorySizeSave(private val mNewMaxHistorySize: Long,
|
||||
private val mOldMaxHistorySize: Long)
|
||||
: ActionRunnable() {
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
val maxHistorySizeToShow =
|
||||
if (result.isSuccess) {
|
||||
mNewMaxHistorySize
|
||||
} else {
|
||||
database?.historyMaxSize = mOldMaxHistorySize
|
||||
mOldMaxHistorySize
|
||||
}
|
||||
preference.summary = maxHistorySizeToShow.toString()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val DEFAULT_MAX_HISTORY_SIZE = 134217728L
|
||||
const val INFINITE_MAX_HISTORY_SIZE = -1L
|
||||
|
||||
fun newInstance(key: String): MaxHistorySizePreferenceDialogFragmentCompat {
|
||||
val fragment = MaxHistorySizePreferenceDialogFragmentCompat()
|
||||
val bundle = Bundle(1)
|
||||
bundle.putString(ARG_KEY, key)
|
||||
fragment.arguments = bundle
|
||||
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,38 +21,36 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
class MemoryUsagePreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
|
||||
class MemoryUsagePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
|
||||
setExplanationText(R.string.memory_usage_explanation)
|
||||
inputText = database?.memoryUsageAsString ?: ""
|
||||
inputText = database?.memoryUsage?.toString()?: MIN_MEMORY_USAGE.toString()
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
if (database != null && positiveResult) {
|
||||
var memoryUsage: Long
|
||||
try {
|
||||
val stringMemory = inputText
|
||||
memoryUsage = java.lang.Long.parseLong(stringMemory)
|
||||
} catch (e: NumberFormatException) {
|
||||
Toast.makeText(context, R.string.error_rounds_not_number, Toast.LENGTH_LONG).show() // TODO change error
|
||||
return
|
||||
if (positiveResult) {
|
||||
database?.let { database ->
|
||||
var memoryUsage: Long = try {
|
||||
inputText.toLong()
|
||||
} catch (e: NumberFormatException) {
|
||||
MIN_MEMORY_USAGE
|
||||
}
|
||||
if (memoryUsage < MIN_MEMORY_USAGE) {
|
||||
memoryUsage = MIN_MEMORY_USAGE
|
||||
}
|
||||
// TODO Max Memory
|
||||
|
||||
val oldMemoryUsage = database.memoryUsage
|
||||
database.memoryUsage = memoryUsage
|
||||
|
||||
actionInUIThreadAfterSaveDatabase = AfterMemorySave(memoryUsage, oldMemoryUsage)
|
||||
}
|
||||
|
||||
if (memoryUsage < 1) {
|
||||
memoryUsage = 1
|
||||
}
|
||||
|
||||
val oldMemoryUsage = database!!.memoryUsage
|
||||
database!!.memoryUsage = memoryUsage
|
||||
|
||||
actionInUIThreadAfterSaveDatabase = AfterMemorySave(memoryUsage, oldMemoryUsage)
|
||||
}
|
||||
|
||||
super.onDialogClosed(positiveResult)
|
||||
@@ -63,16 +61,21 @@ class MemoryUsagePreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDia
|
||||
: ActionRunnable() {
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
val memoryToShow = mNewMemory
|
||||
if (!result.isSuccess) {
|
||||
database?.memoryUsage = mOldMemory
|
||||
}
|
||||
val memoryToShow =
|
||||
if (result.isSuccess) {
|
||||
mNewMemory
|
||||
} else {
|
||||
database?.memoryUsage = mOldMemory
|
||||
mOldMemory
|
||||
}
|
||||
preference.summary = memoryToShow.toString()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val MIN_MEMORY_USAGE = 1L
|
||||
|
||||
fun newInstance(key: String): MemoryUsagePreferenceDialogFragmentCompat {
|
||||
val fragment = MemoryUsagePreferenceDialogFragmentCompat()
|
||||
val bundle = Bundle(1)
|
||||
|
||||
@@ -21,38 +21,36 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
class ParallelismPreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
|
||||
class ParallelismPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
|
||||
setExplanationText(R.string.parallelism_explanation)
|
||||
inputText = database?.parallelismAsString ?: ""
|
||||
inputText = database?.parallelism?.toString() ?: MIN_PARALLELISM.toString()
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
if (database != null && positiveResult) {
|
||||
var parallelism: Int
|
||||
try {
|
||||
val stringParallelism = inputText
|
||||
parallelism = Integer.parseInt(stringParallelism)
|
||||
} catch (e: NumberFormatException) {
|
||||
Toast.makeText(context, R.string.error_rounds_not_number, Toast.LENGTH_LONG).show() // TODO change error
|
||||
return
|
||||
if (positiveResult) {
|
||||
database?.let { database ->
|
||||
var parallelism: Int = try {
|
||||
inputText.toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
MIN_PARALLELISM
|
||||
}
|
||||
if (parallelism < MIN_PARALLELISM) {
|
||||
parallelism = MIN_PARALLELISM
|
||||
}
|
||||
// TODO Max Parallelism
|
||||
|
||||
val oldParallelism = database.parallelism
|
||||
database.parallelism = parallelism
|
||||
|
||||
actionInUIThreadAfterSaveDatabase = AfterParallelismSave(parallelism, oldParallelism)
|
||||
}
|
||||
|
||||
if (parallelism < 1) {
|
||||
parallelism = 1
|
||||
}
|
||||
|
||||
val oldParallelism = database!!.parallelism
|
||||
database?.parallelism = parallelism
|
||||
|
||||
actionInUIThreadAfterSaveDatabase = AfterParallelismSave(parallelism, oldParallelism)
|
||||
}
|
||||
|
||||
super.onDialogClosed(positiveResult)
|
||||
@@ -63,16 +61,21 @@ class ParallelismPreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDia
|
||||
: ActionRunnable() {
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
val parallelismToShow = mNewParallelism
|
||||
if (!result.isSuccess) {
|
||||
database?.parallelism = mOldParallelism
|
||||
}
|
||||
val parallelismToShow =
|
||||
if (result.isSuccess) {
|
||||
mNewParallelism
|
||||
} else {
|
||||
database?.parallelism = mOldParallelism
|
||||
mOldParallelism
|
||||
}
|
||||
preference.summary = parallelismToShow.toString()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val MIN_PARALLELISM = 1
|
||||
|
||||
fun newInstance(key: String): ParallelismPreferenceDialogFragmentCompat {
|
||||
val fragment = ParallelismPreferenceDialogFragmentCompat()
|
||||
val bundle = Bundle(1)
|
||||
|
||||
@@ -25,39 +25,38 @@ import android.widget.Toast
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
class RoundsPreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
|
||||
class RoundsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||
|
||||
override fun onBindDialogView(view: View) {
|
||||
super.onBindDialogView(view)
|
||||
|
||||
explanationText = getString(R.string.rounds_explanation)
|
||||
inputText = database?.numberKeyEncryptionRoundsAsString ?: ""
|
||||
inputText = database?.numberKeyEncryptionRounds?.toString() ?: MIN_ITERATIONS.toString()
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
if (database != null && positiveResult) {
|
||||
var rounds: Long
|
||||
try {
|
||||
val strRounds = inputText
|
||||
rounds = java.lang.Long.parseLong(strRounds)
|
||||
} catch (e: NumberFormatException) {
|
||||
Toast.makeText(context, R.string.error_rounds_not_number, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
if (positiveResult) {
|
||||
database?.let { database ->
|
||||
var rounds: Long = try {
|
||||
inputText.toLong()
|
||||
} catch (e: NumberFormatException) {
|
||||
MIN_ITERATIONS
|
||||
}
|
||||
if (rounds < MIN_ITERATIONS) {
|
||||
rounds = MIN_ITERATIONS
|
||||
}
|
||||
// TODO Max iterations
|
||||
|
||||
if (rounds < 1) {
|
||||
rounds = 1
|
||||
}
|
||||
val oldRounds = database.numberKeyEncryptionRounds
|
||||
try {
|
||||
database.numberKeyEncryptionRounds = rounds
|
||||
} catch (e: NumberFormatException) {
|
||||
Toast.makeText(context, R.string.error_rounds_too_large, Toast.LENGTH_LONG).show()
|
||||
database.numberKeyEncryptionRounds = Long.MAX_VALUE
|
||||
}
|
||||
|
||||
val oldRounds = database!!.numberKeyEncryptionRounds
|
||||
try {
|
||||
database?.numberKeyEncryptionRounds = rounds
|
||||
} catch (e: NumberFormatException) {
|
||||
Toast.makeText(context, R.string.error_rounds_too_large, Toast.LENGTH_LONG).show()
|
||||
database?.numberKeyEncryptionRounds = Integer.MAX_VALUE.toLong()
|
||||
actionInUIThreadAfterSaveDatabase = AfterRoundSave(rounds, oldRounds)
|
||||
}
|
||||
|
||||
actionInUIThreadAfterSaveDatabase = AfterRoundSave(rounds, oldRounds)
|
||||
}
|
||||
|
||||
super.onDialogClosed(positiveResult)
|
||||
@@ -67,17 +66,21 @@ class RoundsPreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFr
|
||||
private val mOldRounds: Long) : ActionRunnable() {
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
val roundsToShow = mNewRounds
|
||||
if (!result.isSuccess) {
|
||||
database?.numberKeyEncryptionRounds = mOldRounds
|
||||
}
|
||||
|
||||
val roundsToShow =
|
||||
if (result.isSuccess) {
|
||||
mNewRounds
|
||||
} else {
|
||||
database?.numberKeyEncryptionRounds = mOldRounds
|
||||
mOldRounds
|
||||
}
|
||||
preference.summary = roundsToShow.toString()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val MIN_ITERATIONS = 1L
|
||||
|
||||
fun newInstance(key: String): RoundsPreferenceDialogFragmentCompat {
|
||||
val fragment = RoundsPreferenceDialogFragmentCompat()
|
||||
val bundle = Bundle(1)
|
||||
|
||||
@@ -20,7 +20,6 @@ package com.kunzisoft.keepass.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import androidx.core.content.ContextCompat
|
||||
import android.text.method.PasswordTransformationMethod
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
@@ -29,9 +28,14 @@ import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.adapters.EntryHistoryAdapter
|
||||
import com.kunzisoft.keepass.database.element.EntryVersioned
|
||||
import com.kunzisoft.keepass.database.element.PwDate
|
||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
class EntryContentsView @JvmOverloads constructor(context: Context,
|
||||
@@ -59,9 +63,6 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
|
||||
private val extrasContainerView: View
|
||||
private val extrasView: ViewGroup
|
||||
|
||||
private val dateFormat: DateFormat = android.text.format.DateFormat.getDateFormat(context)
|
||||
private val timeFormat: DateFormat = android.text.format.DateFormat.getTimeFormat(context)
|
||||
|
||||
private val creationDateView: TextView
|
||||
private val modificationDateView: TextView
|
||||
private val lastAccessDateView: TextView
|
||||
@@ -69,6 +70,10 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
|
||||
|
||||
private val uuidView: TextView
|
||||
|
||||
private val historyContainerView: View
|
||||
private val historyListView: RecyclerView
|
||||
private val historyAdapter = EntryHistoryAdapter(context)
|
||||
|
||||
val isUserNamePresent: Boolean
|
||||
get() = userNameContainerView.visibility == View.VISIBLE
|
||||
|
||||
@@ -103,6 +108,13 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
|
||||
|
||||
uuidView = findViewById(R.id.entry_UUID)
|
||||
|
||||
historyContainerView = findViewById(R.id.entry_history_container)
|
||||
historyListView = findViewById(R.id.entry_history_list)
|
||||
historyListView?.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
|
||||
adapter = historyAdapter
|
||||
}
|
||||
|
||||
val attrColorAccent = intArrayOf(R.attr.colorAccent)
|
||||
val taColorAccent = context.theme.obtainStyledAttributes(attrColorAccent)
|
||||
colorAccent = taColorAccent.getColor(0, Color.BLACK)
|
||||
@@ -230,24 +242,20 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
|
||||
extrasContainerView.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun getDateTime(date: Date): String {
|
||||
return dateFormat.format(date) + " " + timeFormat.format(date)
|
||||
fun assignCreationDate(date: PwDate) {
|
||||
creationDateView.text = date.getDateTimeString(resources)
|
||||
}
|
||||
|
||||
fun assignCreationDate(date: Date) {
|
||||
creationDateView.text = getDateTime(date)
|
||||
fun assignModificationDate(date: PwDate) {
|
||||
modificationDateView.text = date.getDateTimeString(resources)
|
||||
}
|
||||
|
||||
fun assignModificationDate(date: Date) {
|
||||
modificationDateView.text = getDateTime(date)
|
||||
fun assignLastAccessDate(date: PwDate) {
|
||||
lastAccessDateView.text = date.getDateTimeString(resources)
|
||||
}
|
||||
|
||||
fun assignLastAccessDate(date: Date) {
|
||||
lastAccessDateView.text = getDateTime(date)
|
||||
}
|
||||
|
||||
fun assignExpiresDate(date: Date) {
|
||||
expiresDateView.text = getDateTime(date)
|
||||
fun assignExpiresDate(date: PwDate) {
|
||||
expiresDateView.text = date.getDateTimeString(resources)
|
||||
}
|
||||
|
||||
fun assignExpiresDate(constString: String) {
|
||||
@@ -258,6 +266,21 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
|
||||
uuidView.text = uuid.toString()
|
||||
}
|
||||
|
||||
fun showHistory(show: Boolean) {
|
||||
historyContainerView.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
fun assignHistory(history: ArrayList<EntryVersioned>) {
|
||||
historyAdapter.clear()
|
||||
historyAdapter.entryHistoryList.addAll(history)
|
||||
}
|
||||
|
||||
fun onHistoryClick(action: (historyItem: EntryVersioned, position: Int)->Unit) {
|
||||
historyAdapter.onItemClickListener = { item, position ->
|
||||
action.invoke(item, position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateDefaultLayoutParams(): LayoutParams {
|
||||
return LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/entry_scroll"
|
||||
android:layout_width="match_parent"
|
||||
@@ -86,12 +85,30 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/history_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:background="?attr/colorAccent"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:textColor="?attr/textColorInverse"
|
||||
android:text="@string/entry_history"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.kunzisoft.keepass.view.EntryContentsView
|
||||
android:id="@+id/entry_contents"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="0dp"
|
||||
app:layout_constraintWidth_percent="@dimen/content_percent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/history_container"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
57
app/src/main/res/layout/item_list_entry_history.xml
Normal file
57
app/src/main/res/layout/item_list_entry_history.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_history_last_modified"
|
||||
tools:text = "Last Modified"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_title"
|
||||
android:gravity="center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_history_title"
|
||||
tools:text = "Title"
|
||||
app:layout_constraintStart_toEndOf="@+id/entry_history_last_modified"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_username"
|
||||
android:gravity="center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_history_username"
|
||||
tools:text = "Username"
|
||||
app:layout_constraintStart_toEndOf="@+id/entry_history_title"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_url"
|
||||
android:gravity="center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_history_url"
|
||||
tools:text = "URL"
|
||||
app:layout_constraintStart_toEndOf="@+id/entry_history_username"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:gravity="center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -17,11 +17,13 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/edit"
|
||||
android:padding="20dp"
|
||||
android:orientation="vertical"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAutofill="noExcludeDescendants"
|
||||
@@ -29,14 +31,29 @@
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/explanation_text"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
style="@style/KeepassDXStyle.TextAppearance.SmallTitle"/>
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/switch_element"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/enable"
|
||||
app:layout_constraintTop_toBottomOf="@+id/explanation_text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:minHeight="48dp"/>
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/input_text"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/input_text"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/switch_element"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:minHeight="48dp"
|
||||
android:digits="0123456789"
|
||||
android:inputType="number"/>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2018 Jeremy Jamet / Kunzisoft.
|
||||
Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
|
||||
This file is part of KeePass DX.
|
||||
|
||||
@@ -17,17 +17,41 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/edit"
|
||||
android:padding="20dp"
|
||||
android:orientation="vertical"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAutofill="noExcludeDescendants"
|
||||
tools:targetApi="o">
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/explanation_text"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
style="@style/KeepassDXStyle.TextAppearance.SmallTitle"/>
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/switch_element"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/enable"
|
||||
app:layout_constraintTop_toBottomOf="@+id/explanation_text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:minHeight="48dp"/>
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/input_text"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"/>
|
||||
</LinearLayout>
|
||||
android:id="@+id/input_text"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/switch_element"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:minHeight="48dp"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2018 Jeremy Jamet / Kunzisoft.
|
||||
|
||||
This file is part of KeePass DX.
|
||||
|
||||
KeePass DX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
KeePass DX is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/edit"
|
||||
android:padding="20dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAutofill="noExcludeDescendants"
|
||||
tools:targetApi="o">
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/explanation_text"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="8dp"
|
||||
style="@style/KeepassDXStyle.TextAppearance.SmallTitle"/>
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/input_text"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -254,7 +254,7 @@
|
||||
android:layout_margin="@dimen/default_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Expires -->
|
||||
<!-- UUID -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_UUID_label"
|
||||
android:layout_width="match_parent"
|
||||
@@ -275,4 +275,93 @@
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/entry_history_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/default_margin"
|
||||
android:layout_marginEnd="@dimen/default_margin"
|
||||
android:layout_marginLeft="@dimen/default_margin"
|
||||
android:layout_marginRight="@dimen/default_margin"
|
||||
android:layout_marginBottom="@dimen/default_margin">
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="@dimen/default_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- History -->
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_history_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/entry_history"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_history_last_modified"
|
||||
android:text="@string/entry_modified"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_title"
|
||||
android:gravity="center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_history_title"
|
||||
android:text="@string/entry_title"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
||||
app:layout_constraintStart_toEndOf="@+id/entry_history_last_modified"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_username"
|
||||
android:gravity="center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_history_username"
|
||||
android:text="@string/entry_user_name"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
||||
app:layout_constraintStart_toEndOf="@+id/entry_history_title"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_url"
|
||||
android:gravity="center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/entry_history_url"
|
||||
android:text="@string/entry_url"
|
||||
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
||||
app:layout_constraintStart_toEndOf="@+id/entry_history_username"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:gravity="center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/entry_history_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -143,7 +143,6 @@
|
||||
<string name="error_load_database">تعذر تحميل قاعدة البيانات.</string>
|
||||
<string name="error_load_database_KDF_memory">غير قادر على تحميل المفتاح، في محاولة لتقليل الذاكرة المستخدمة من قبل KDF.</string>
|
||||
<string name="error_pass_gen_type">يجب تحديد كلمة مرور واحد على الأقل نوع الجيل</string>
|
||||
<string name="error_rounds_not_number">يجب أن تكون \"جولات\" عددا.</string>
|
||||
<string name="error_rounds_too_large">\"جولات\" كبيرة جداً. الإعداد إلى 2147483648.</string>
|
||||
<string name="error_string_key">يجب أن يكون لكل سلسلة اسم حقل.</string>
|
||||
<string name="error_wrong_length">أدخل عددًا صحيحًا موجبًا في حقل «الطول».</string>
|
||||
@@ -216,7 +215,7 @@
|
||||
\nاستعد كلمة السر.</string>
|
||||
<string name="biometric_not_recognized">لم يتعرّف على البصمة</string>
|
||||
<string name="open_biometric_prompt_store_credential">استخدم البصمة لحفظ كلمة السر</string>
|
||||
<string name="history">تأريخ</string>
|
||||
<string name="database_history">تأريخ</string>
|
||||
<string name="clipboard_notifications_summary">مكن اشعارات الحافظة لنسخ الحقول</string>
|
||||
<string name="fingerprint_advanced_unlock_title">كيف أعد فحص البصمة للفتح السريع \?</string>
|
||||
<string name="fingerprint_setting_text">"احفظ البصمات في "</string>
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
<string name="error_out_of_memory">El telèfon sha quedat sense memòria processant la teva base de dades. Potser és massa gran pel teu telèfon.</string>
|
||||
<string name="error_pass_gen_type">Has de seleccionar almenys un tipus de generador de contrasenyes</string>
|
||||
<string name="error_pass_match">Les contrasenyes no coincideixen.</string>
|
||||
<string name="error_rounds_not_number">Les passades han de ser un número.</string>
|
||||
<string name="error_rounds_too_large">Massa passades. Establint a 2147483648.</string>
|
||||
<string name="error_title_required">És necessari un títol.</string>
|
||||
<string name="error_wrong_length">Insereix un enter positiu al camp longitud</string>
|
||||
|
||||
@@ -73,7 +73,6 @@
|
||||
<string name="error_out_of_memory">Nedostatek paměti k otevření databáze.</string>
|
||||
<string name="error_pass_gen_type">Je třeba zvolit alespoň jeden způsob vytváření hesla.</string>
|
||||
<string name="error_pass_match">Zadání hesla se neshodují.</string>
|
||||
<string name="error_rounds_not_number">„Počet průchodů“ musí být číslo.</string>
|
||||
<string name="error_rounds_too_large">Příliš vysoký „Počet průchodů“. Nastavuji na 2147483648.</string>
|
||||
<string name="error_string_key">Je třeba, aby každý řetězec měl název kolonky.</string>
|
||||
<string name="error_title_required">Přidejte název.</string>
|
||||
@@ -222,7 +221,7 @@
|
||||
<string name="biometric_scanning_error">Problém s otiskem prstu: %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Použít pro uložení tohoto hesla otisk prstu</string>
|
||||
<string name="no_credentials_stored">Tato databáze zatím není chráněna heslem.</string>
|
||||
<string name="history">Historie</string>
|
||||
<string name="database_history">Historie</string>
|
||||
<string name="menu_appearance_settings">Vzhled</string>
|
||||
<string name="general">Obecné</string>
|
||||
<string name="autofill">Automatické vyplnění</string>
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
<string name="error_out_of_memory">Ikke nok hukommelse til at indlæse hele databasen.</string>
|
||||
<string name="error_pass_gen_type">Der skal vælges mindst én kode for kodeordsgenerering.</string>
|
||||
<string name="error_pass_match">Adgangskoderne er ikke ens.</string>
|
||||
<string name="error_rounds_not_number">\"Transformationsrunder\" skal være en talværdi.</string>
|
||||
<string name="error_rounds_too_large">\"Transformation Runder\" er for stor. Sættes til 2147483648.</string>
|
||||
<string name="error_string_key">Hver streng skal have et feltnavn.</string>
|
||||
<string name="error_title_required">Tilføj en titel.</string>
|
||||
@@ -221,7 +220,7 @@
|
||||
<string name="biometric_scanning_error">Problem med fingeraftryk: %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Brug fingeraftryk til at gemme adgangskoden</string>
|
||||
<string name="no_credentials_stored">Databasen har endnu ikke en adgangskode.</string>
|
||||
<string name="history">Historik</string>
|
||||
<string name="database_history">Historik</string>
|
||||
<string name="menu_appearance_settings">Udseende</string>
|
||||
<string name="general">Generelt</string>
|
||||
<string name="autofill">Autoudfyld</string>
|
||||
|
||||
@@ -75,7 +75,6 @@
|
||||
<string name="error_out_of_memory">Zu wenig Speicherplatz, um die ganze Datenbank zu laden.</string>
|
||||
<string name="error_pass_gen_type">Mindestens eine Art der Passwortgenerierung muss ausgewählt werden.</string>
|
||||
<string name="error_pass_match">Die Passwörter stimmen nicht überein.</string>
|
||||
<string name="error_rounds_not_number">„Transformationsrunden“ zu einer Zahl machen.</string>
|
||||
<string name="error_rounds_too_large">„Transformationsrunden“ zu hoch. Wird auf 214748364848 eingestellt.</string>
|
||||
<string name="error_string_key">Für jede Zeichenfolge ist ein Feldname notwendig.</string>
|
||||
<string name="error_title_required">Titel hinzufügen.</string>
|
||||
@@ -207,7 +206,7 @@
|
||||
<string name="encrypted_value_stored">Verschlüsseltes Passwort wurde gespeichert</string>
|
||||
<string name="biometric_invalid_key">Fingerabdruckschlüssel nicht lesbar. Bitte das Passwort wiederherstellen.</string>
|
||||
<string name="biometric_scanning_error">Problem mit dem Fingerabdruck: %1$s</string>
|
||||
<string name="history">Verlauf</string>
|
||||
<string name="database_history">Verlauf</string>
|
||||
<string name="fingerprint_advanced_unlock_title">Wie richte ich den Fingerabdruckscanner für schnelles Entsperren ein?</string>
|
||||
<string name="fingerprint_setting_text">Eingelesenen Fingerabdruck für das Gerät speichern in</string>
|
||||
<string name="fingerprint_setting_link_text">„Einstellungen“ → „Sicherheit“ → „Fingerabdruck“</string>
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
<string name="error_out_of_memory">Το τηλέφωνο ξέμεινε από μνήμη κατά τη διάρκεια προσπέλασης της βάσης δεδομένων σας. Μπορεί να είναι πολύ μεγάλη για το τηλέφωνο σας.</string>
|
||||
<string name="error_pass_gen_type">Πρέπει να επιλεγεί τουλάχιστον ένας τύπος δημιουργίας κωδικού πρόσβασης</string>
|
||||
<string name="error_pass_match">Οι κωδικοί δεν ταιριάζουν.</string>
|
||||
<string name="error_rounds_not_number">Οι κύκλοι πρέπει να είναι αριθμός.</string>
|
||||
<string name="error_rounds_too_large">Οι κύκλοι είναι υπερβολικά πολλοί. Ορισμός σε 2147483648.</string>
|
||||
<string name="error_string_key">Ένα όνομα πεδίου απαιτείται για κάθε σειρά.</string>
|
||||
<string name="error_title_required">Απαιτείται ένας τίτλος.</string>
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
<string name="error_out_of_memory">El dispositivo se quedó sin memory mientras analizada la base de datos. Puede ser demasiado grande para este dispositivo.</string>
|
||||
<string name="error_pass_gen_type">Debe seleccionar al menos un tipo de generación de contraseñas</string>
|
||||
<string name="error_pass_match">Las contraseñas no coinciden.</string>
|
||||
<string name="error_rounds_not_number">Las pasadas deben ser un número.</string>
|
||||
<string name="error_rounds_too_large">Pasadas demasiado grande. Establecido a 2147483648.</string>
|
||||
<string name="error_title_required">Se necesita un título.</string>
|
||||
<string name="error_wrong_length">Introduzca un entero positivo en el campo longitud</string>
|
||||
@@ -207,7 +206,7 @@
|
||||
<string name="biometric_scanning_error">Problema de huella digital: %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Usa la huella digital para almacenar esta contraseña</string>
|
||||
<string name="no_credentials_stored">Aún sin contraseña almacenada para esta base de datos</string>
|
||||
<string name="history">Historial</string>
|
||||
<string name="database_history">Historial</string>
|
||||
<string name="menu_appearance_settings">Apariencia</string>
|
||||
<string name="general">General</string>
|
||||
<string name="autofill">Autocompletar</string>
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
<string name="error_out_of_memory">Telefonoa memoriarik gabe gelditu da zure datubasea arakatzean. Handiegia izan daiteke zure telefonorako.</string>
|
||||
<string name="error_pass_gen_type">Pasahitza sortzeko mota bat gutxienez aukeratu behar da</string>
|
||||
<string name="error_pass_match">Pasahitzak ez datoz bat.</string>
|
||||
<string name="error_rounds_not_number">Rondak zenbaki bat izan behar dira.</string>
|
||||
<string name="error_rounds_too_large">Rondak handiegiak. 2147483648 balorean jarrita.</string>
|
||||
<string name="error_string_key">Eremu izen bat behar da testu kate bakoitzerako.</string>
|
||||
<string name="error_title_required">Izenburu bat behar da.</string>
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
<string name="error_out_of_memory">Puhelimesta loppui muisti salasanatietokantaa avatessa. Tietokanta voi olla liian suuri tälle puhelinmallille.</string>
|
||||
<string name="error_pass_gen_type">Vähintään yksi salasanagenerointitapa täytyy olla valittuna.</string>
|
||||
<string name="error_pass_match">Salasanat eivät täsmää.</string>
|
||||
<string name="error_rounds_not_number">Kierroksia täytyy olla numero.</string>
|
||||
<string name="error_rounds_too_large">Kierroksia on liian paljon. Asetetaan se arvoon 2147483648.</string>
|
||||
<string name="error_string_key">Kentän nimi on pakollinen joka tekstille.</string>
|
||||
<string name="error_title_required">Otsikko on pakollinen.</string>
|
||||
|
||||
@@ -76,7 +76,6 @@
|
||||
<string name="error_out_of_memory">Mémoire insuffisante pour charger l’ensemble de votre base de données.</string>
|
||||
<string name="error_pass_gen_type">Au moins un type de génération de mot de passe doit être sélectionné.</string>
|
||||
<string name="error_pass_match">Les mots de passe ne correspondent pas.</string>
|
||||
<string name="error_rounds_not_number">« Tours de transformation » doit être un nombre.</string>
|
||||
<string name="error_rounds_too_large">« Tours de transformation » trop grand. Défini à 2147483648.</string>
|
||||
<string name="error_string_key">Chaque chaîne doit avoir un nom de champ.</string>
|
||||
<string name="error_title_required">Ajoutez un titre.</string>
|
||||
@@ -187,7 +186,7 @@
|
||||
<string name="biometric_scanning_error">Problème d’empreinte digitale : %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Utiliser l’empreinte digitale pour stocker ce mot de passe</string>
|
||||
<string name="no_credentials_stored">Cette base de données n’a pas encore de mot de passe.</string>
|
||||
<string name="history">Historique</string>
|
||||
<string name="database_history">Historique</string>
|
||||
<string name="menu_appearance_settings">Apparence</string>
|
||||
<string name="general">Général</string>
|
||||
<string name="autofill">Remplissage automatique</string>
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
<string name="error_out_of_memory">Nincs elég memória a teljes adatbázis betöltéséhez.</string>
|
||||
<string name="error_pass_gen_type">Legalább egy jelszóelőállítási típust kell választania.</string>
|
||||
<string name="error_pass_match">A jelszavak nem egyeznek meg.</string>
|
||||
<string name="error_rounds_not_number">A „Transzformációs körök” szám kell legyen.</string>
|
||||
<string name="error_rounds_too_large">A „Transzformációs körök” száma túl nagy. Beállítás 2147483648-ra.</string>
|
||||
<string name="error_string_key">Minden karakterlánchoz szükséges egy mezőnév.</string>
|
||||
<string name="error_title_required">Adjon hozzá egy címet.</string>
|
||||
@@ -223,7 +222,7 @@
|
||||
<string name="warning_no_encryption_key">Biztos, hogy nem akar semmilyen titkosítási kulcsot használni\?</string>
|
||||
<string name="build_label">Összeállítás: %1$s</string>
|
||||
<string name="biometric_not_recognized">Az ujjlenyomat nem ismerhető fel</string>
|
||||
<string name="history">Előzmények</string>
|
||||
<string name="database_history">Előzmények</string>
|
||||
<string name="menu_appearance_settings">Megjelenés</string>
|
||||
<string name="general">Általános</string>
|
||||
<string name="autofill">Automatikus kitöltés</string>
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
<string name="error_out_of_memory">Memoria insufficiente per caricare l\'intero database.</string>
|
||||
<string name="error_pass_gen_type">Deve essere selezionato almeno un tipo di generazione password.</string>
|
||||
<string name="error_pass_match">Le password non corrispondono.</string>
|
||||
<string name="error_rounds_not_number">Rendi il \"livello\" un numero.</string>
|
||||
<string name="error_rounds_too_large">\"Livello\" troppo alto. Impostato a 2147483648.</string>
|
||||
<string name="error_string_key">Ogni stringa deve avere un nome.</string>
|
||||
<string name="error_title_required">Aggiungi un titolo.</string>
|
||||
@@ -215,7 +214,7 @@
|
||||
<string name="warning_empty_password">Vuoi veramente che non ci sia una password di sblocco\?</string>
|
||||
<string name="warning_no_encryption_key">Sei sicuro di non volere usare una chiave di cifratura?</string>
|
||||
<string name="biometric_not_recognized">Impronta non riconosciuta</string>
|
||||
<string name="history">Cronologia</string>
|
||||
<string name="database_history">Cronologia</string>
|
||||
<string name="menu_appearance_settings">Aspetto</string>
|
||||
<string name="general">Generale</string>
|
||||
<string name="autofill">Autocompletamento</string>
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
<string name="error_out_of_memory">זיכרון המכשיר אזל בזמן ניתוח מסד הנתונים. יתכן והוא גדול מדי למכשירך.</string>
|
||||
<string name="error_pass_gen_type">לפחות סוג אחד ליצירת סיסמה צריך להיבחר</string>
|
||||
<string name="error_pass_match">הסיסמאות לא תואמות.</string>
|
||||
<string name="error_rounds_not_number">סיבובים חייב להיות מספר.</string>
|
||||
<string name="error_rounds_too_large">מספר סיבובים גדול מדי. מגדיר ל-2147483648.</string>
|
||||
<string name="error_string_key">שדה שם נדרש לכל מחרוזת.</string>
|
||||
<string name="error_title_required">כותרת נדרשת.</string>
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
<string name="error_out_of_memory">データベース解析中にメモリ不足になりました。</string>
|
||||
<string name="error_pass_gen_type">少なくとも1つ以上のパスワード生成タイプを選択する必要があります。</string>
|
||||
<string name="error_pass_match">パスワードが一致しません</string>
|
||||
<string name="error_rounds_not_number">数値を入力してください。</string>
|
||||
<string name="error_rounds_too_large">値が大きすぎます。 2147483648にセットしました。</string>
|
||||
<string name="error_title_required">タイトルは必須入力です。</string>
|
||||
<string name="error_wrong_length">\"長さ\"欄には正の整数を入力してください。</string>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="error_load_database_KDF_memory">키를 로드할 수 없습니다. KDF \"메모리 사용량\"을 줄여 보세요.</string>
|
||||
<string name="error_pass_gen_type">최소 한 가지의 비밀번호 생성 방식이 선택되어야 합니다.</string>
|
||||
<string name="error_pass_match">비밀번호가 일치하지 않습니다.</string>
|
||||
<string name="error_rounds_not_number">\"Transformation rounds\"는 숫자로 입력해 주세요.</string>
|
||||
<string name="error_rounds_too_large">\"Transformation rounds\" 가 너무 높습니다. 2147483648로 설정합니다.</string>
|
||||
<string name="error_string_key">각 항목은 필드 이름을 가져야 합니다.</string>
|
||||
<string name="error_title_required">제목을 입력하십시오.</string>
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
<string name="error_out_of_memory">Darbam ar datu bāzi, tālrunī nepietiek atmiņas.</string>
|
||||
<string name="error_pass_gen_type">Ir jāatlasa vismaz viens paroles ģenerēšanas tips</string>
|
||||
<string name="error_pass_match">Paroles nesakrīt.</string>
|
||||
<string name="error_rounds_not_number">Ievadiet līmeni no 1 līdz 2147483648</string>
|
||||
<string name="error_rounds_too_large">Līmenis pārāk liels. Maksimālais 2147483648</string>
|
||||
<string name="error_string_key">A field name is required for each string.</string>
|
||||
<string name="error_title_required">Nepieciešams nosaukums.</string>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="error_load_database_KDF_memory">Kunne ikke laste nøkkelen, prøv å senke minnet brukt av KDF.</string>
|
||||
<string name="error_pass_gen_type">Minst én passordgenereringstype må velges.</string>
|
||||
<string name="error_pass_match">Passordene samsvarer ikke.</string>
|
||||
<string name="error_rounds_not_number">\"Omganger\" må være et tall.</string>
|
||||
<string name="error_rounds_too_large">\"Omganger\" er for stort. Setter til 2147483648.</string>
|
||||
<string name="error_string_key">Hver streng må ha et feltnavn.</string>
|
||||
<string name="error_title_required">En tittel er påkrevd.</string>
|
||||
@@ -199,7 +198,7 @@
|
||||
<string name="biometric_scanning_error">Fingeravtrykksproblem: %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Bruk fingeravtrykk til å lagre dette passordet</string>
|
||||
<string name="no_credentials_stored">Denne databasen har ikke et passord enda.</string>
|
||||
<string name="history">Historikk</string>
|
||||
<string name="database_history">Historikk</string>
|
||||
<string name="menu_appearance_settings">Utseende</string>
|
||||
<string name="general">Generelt</string>
|
||||
<string name="autofill">Autofyll</string>
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
<string name="error_out_of_memory">Onvoldoende vrij geheugen om de gehele databank te laden.</string>
|
||||
<string name="error_pass_gen_type">Je moet minimaal één soort wachtwoordgenerering kiezen.</string>
|
||||
<string name="error_pass_match">De wachtwoorden komen niet overeen.</string>
|
||||
<string name="error_rounds_not_number">\"Cycli-waarde\" moet een getal zijn.</string>
|
||||
<string name="error_rounds_too_large">\"Cycli-waarde\" te groot. Wordt ingesteld op 2147483648.</string>
|
||||
<string name="error_title_required">Voeg een titel toe.</string>
|
||||
<string name="error_wrong_length">Voer een positief geheel getal in in het veld \"Lengte\".</string>
|
||||
@@ -222,7 +221,7 @@
|
||||
<string name="biometric_scanning_error">Vingerafdrukprobleem: %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Vingerafdruk gebruiken om dit wachtwoord op te slaan</string>
|
||||
<string name="no_credentials_stored">Deze databank heeft nog geen wachtwoord.</string>
|
||||
<string name="history">Geschiedenis</string>
|
||||
<string name="database_history">Geschiedenis</string>
|
||||
<string name="menu_appearance_settings">Uiterlijk</string>
|
||||
<string name="general">Algemeen</string>
|
||||
<string name="autofill">Auto-aanvullen</string>
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
<string name="error_out_of_memory">Telefonen gjekk tom for minne ved lesinga av databasen din. Databasen er kanskje for stor.</string>
|
||||
<string name="error_pass_gen_type">Du må velja minst éin passordlagingstype</string>
|
||||
<string name="error_pass_match">Passorda samsvarer ikkje.</string>
|
||||
<string name="error_rounds_not_number">Omgangar må vera eit tal.</string>
|
||||
<string name="error_rounds_too_large">For mange omgangar. Bruker 2147483648.</string>
|
||||
<string name="error_title_required">Treng ein tittel.</string>
|
||||
<string name="error_wrong_length">Bruk eit positivt heiltal i lengdfeltet</string>
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
<string name="error_out_of_memory">W urządzeniu zabrakło pamięci do załadowania całej bazy danych.</string>
|
||||
<string name="error_pass_gen_type">Należy wybrać co najmniej jeden rodzaj generowania hasła.</string>
|
||||
<string name="error_pass_match">Hasła nie pasują do siebie.</string>
|
||||
<string name="error_rounds_not_number">\"Rundy szyfrowania\" muszą być liczbą.</string>
|
||||
<string name="error_rounds_too_large">\"Rundy szyfrowania\" są zbyt wysokie. Ustaw na 2147483648.</string>
|
||||
<string name="error_title_required">Dodaj tytuł.</string>
|
||||
<string name="error_wrong_length">Wprowadź dodatnią liczbę całkowitą w polu \"Długość\".</string>
|
||||
@@ -220,7 +219,7 @@
|
||||
<string name="biometric_scanning_error">Problem z odciskiem palca: %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Użyj odcisku palca, aby zapisać to hasło</string>
|
||||
<string name="no_credentials_stored">Baza danych nie ma jeszcze hasła.</string>
|
||||
<string name="history">Historia</string>
|
||||
<string name="database_history">Historia</string>
|
||||
<string name="menu_appearance_settings">Wygląd</string>
|
||||
<string name="general">Ogólne</string>
|
||||
<string name="autofill">Wypełnij automatycznie</string>
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
<string name="error_out_of_memory">Falta de memória para abrir todo o banco.</string>
|
||||
<string name="error_pass_gen_type">Pelo menos um tipo de geração de senhas deve ser selecionado.</string>
|
||||
<string name="error_pass_match">As senhas não combinam.</string>
|
||||
<string name="error_rounds_not_number">\"Número de rodadas\" deve ser um número.</string>
|
||||
<string name="error_rounds_too_large">\"Número de rodadas\" é muito grande. Modificado para 2147483648.</string>
|
||||
<string name="error_title_required">Insira um título.</string>
|
||||
<string name="error_wrong_length">Digite um número inteiro positivo no campo \"Tamanho\".</string>
|
||||
@@ -216,7 +215,7 @@
|
||||
<string name="biometric_scanning_error">Problema de Impressão digital: %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Use Impressão digital para armazenar esta senha</string>
|
||||
<string name="no_credentials_stored">Ainda não há nenhuma senha armazenada nesse banco de dados.</string>
|
||||
<string name="history">Histórico</string>
|
||||
<string name="database_history">Histórico</string>
|
||||
<string name="menu_appearance_settings">Aparência</string>
|
||||
<string name="general">Geral</string>
|
||||
<string name="autofill">Preenchimento automático</string>
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
<string name="error_out_of_memory">Sem memória para carregar toda a bases de dados.</string>
|
||||
<string name="error_pass_gen_type">Pelo menos um tipo de geração de palavra-chave deve ser selecionado.</string>
|
||||
<string name="error_pass_match">As palavras-passe não coincidem.</string>
|
||||
<string name="error_rounds_not_number">\"Número de rodadas\" deve ser um número.</string>
|
||||
<string name="error_rounds_too_large">\"Número de rodadas\" é muito grande. Modificado para 2147483648.</string>
|
||||
<string name="error_string_key">Um nome do campo é necessário para cada string.</string>
|
||||
<string name="error_title_required">Adicione um título.</string>
|
||||
@@ -212,7 +211,7 @@
|
||||
<string name="biometric_scanning_error">Problema da Impressão digital: %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Use a impressão digital para armazenar esta palavra-chave</string>
|
||||
<string name="no_credentials_stored">Ainda não há nenhuma palavra-chave armazenada nesta base de dados.</string>
|
||||
<string name="history">Histórico</string>
|
||||
<string name="database_history">Histórico</string>
|
||||
<string name="menu_appearance_settings">Aparência</string>
|
||||
<string name="general">Geral</string>
|
||||
<string name="autofill">Preenchimento automático</string>
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
<string name="error_out_of_memory">Недостаточно памяти для работы с базой.</string>
|
||||
<string name="error_pass_gen_type">Выберите один или несколько типов символов.</string>
|
||||
<string name="error_pass_match">Пароли не совпадают.</string>
|
||||
<string name="error_rounds_not_number">Введите число.</string>
|
||||
<string name="error_rounds_too_large">Предельное значение 2147483648.</string>
|
||||
<string name="error_string_key">Каждое поле должно иметь название.</string>
|
||||
<string name="error_title_required">Введите название.</string>
|
||||
@@ -220,7 +219,7 @@
|
||||
<string name="warning_empty_password">Вы действительно хотите использовать пустой пароль?</string>
|
||||
<string name="warning_no_encryption_key">Вы действительно не хотите использовать ключ шифрования?</string>
|
||||
<string name="biometric_not_recognized">Отпечаток пальца не распознан</string>
|
||||
<string name="history">История</string>
|
||||
<string name="database_history">История</string>
|
||||
<string name="menu_appearance_settings">Внешний вид</string>
|
||||
<string name="general">Общие</string>
|
||||
<string name="autofill">Автозаполнение</string>
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
<string name="error_out_of_memory">Telefón vyčerpal pamäť pri analýze databázy. Možno je to príliš na Váš telefón.</string>
|
||||
<string name="error_pass_gen_type">Musí byť vybraý najmenej jeden typ generovania hesla</string>
|
||||
<string name="error_pass_match">Heslá sa nezhodujú.</string>
|
||||
<string name="error_rounds_not_number">Opakovanie musí byť číslo.</string>
|
||||
<string name="error_rounds_too_large">Príliš veľa opakovaní. Nastavujem na 2147483648.</string>
|
||||
<string name="error_title_required">Vyžaduje sa názov.</string>
|
||||
<string name="error_wrong_length">Zadajte celé kladné číslo na dĺžku poľa</string>
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
<string name="error_out_of_memory">The phone ran out of memory while parsing your database. It may be too large for your phone.</string>
|
||||
<string name="error_pass_gen_type">At least one password generation type must be selected</string>
|
||||
<string name="error_pass_match">Lösenorden matchar inte.</string>
|
||||
<string name="error_rounds_not_number">Antalet rundor måste vara en siffra.</string>
|
||||
<string name="error_rounds_too_large">Antalet rundor är för stort. Sätter värdet till 2147483648.</string>
|
||||
<string name="error_string_key">Ett fältnamn krävs för varje sträng.</string>
|
||||
<string name="error_title_required">En titel krävs.</string>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="error_load_database_KDF_memory">Anahtar yüklenemedi. KDF \"Bellek Kullanımı\" nı azaltmaya çalışın.</string>
|
||||
<string name="error_pass_gen_type">En az bir parola oluşturma türü seçilmelidir.</string>
|
||||
<string name="error_pass_match">Parolalar uyuşmuyor.</string>
|
||||
<string name="error_rounds_not_number">\"Dönüşüm turları\"nı bir sayı yapın.</string>
|
||||
<string name="error_rounds_too_large">\"Dönüşüm turları\" çok yüksek. 2147483648\'e ayarlayın.</string>
|
||||
<string name="error_string_key">Her dizenin bir alan adı olmalıdır.</string>
|
||||
<string name="error_title_required">Bir başlık ekle.</string>
|
||||
@@ -202,7 +201,7 @@
|
||||
<string name="biometric_scanning_error">Parmak izi sorunu: %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">Bu şifreyi saklamak için parmak izini kullanın</string>
|
||||
<string name="no_credentials_stored">Bu veritabanının henüz bir parolası yok.</string>
|
||||
<string name="history">Geçmiş</string>
|
||||
<string name="database_history">Geçmiş</string>
|
||||
<string name="menu_appearance_settings">Görünüm</string>
|
||||
<string name="general">Genel</string>
|
||||
<string name="autofill">Otomatik Doldurma</string>
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
<string name="error_out_of_memory">Телефону не вистачило пам’яті при аналізі вашої бази даних. Можливо база надто велика для вашог телефона.</string>
|
||||
<string name="error_pass_gen_type">Принаймні один тип генерації пароля необхідно вибрати.</string>
|
||||
<string name="error_pass_match">Паролі не співпадають.</string>
|
||||
<string name="error_rounds_not_number">Кількість циклів має бути числом.</string>
|
||||
<string name="error_rounds_too_large">Надто багато циклів. Установлено 2147483648.</string>
|
||||
<string name="error_title_required">Необхідно вказати заголовок.</string>
|
||||
<string name="error_wrong_length">Введіть ціле число на усю довжину поля</string>
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
<string name="error_out_of_memory">因内存不足无法加载数据库。</string>
|
||||
<string name="error_pass_gen_type">必须至少选择一种密码生成类型。</string>
|
||||
<string name="error_pass_match">密码不匹配。</string>
|
||||
<string name="error_rounds_not_number">“变换次数”必须是数字。</string>
|
||||
<string name="error_rounds_too_large">“变换次数”过多。已设置为 2147483648。</string>
|
||||
<string name="error_title_required">请添加标题。</string>
|
||||
<string name="error_wrong_length">请在“长度”字段输入一个正整数。</string>
|
||||
@@ -209,7 +208,7 @@
|
||||
<string name="warning">警告</string>
|
||||
<string name="version_label">版本 %1$s</string>
|
||||
<string name="open_biometric_prompt_store_credential">使用指纹保存此密码</string>
|
||||
<string name="history">历史</string>
|
||||
<string name="database_history">历史</string>
|
||||
<string name="menu_appearance_settings">外观</string>
|
||||
<string name="general">一般设置</string>
|
||||
<string name="autofill">自动填充</string>
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
<string name="error_out_of_memory">這款手機運行記憶體不足而不能解析資料庫。這可能是資料庫太大的緣故。</string>
|
||||
<string name="error_pass_gen_type">至少必須選擇一個密碼生成類型</string>
|
||||
<string name="error_pass_match">密碼不正確。</string>
|
||||
<string name="error_rounds_not_number">次數必須是數字。</string>
|
||||
<string name="error_rounds_too_large">次數太多。最大設置到2147483648。</string>
|
||||
<string name="error_title_required">標題為必填。</string>
|
||||
<string name="error_wrong_length">長度欄位輸入一個正整數</string>
|
||||
|
||||
@@ -149,12 +149,17 @@
|
||||
<!-- Database Settings -->
|
||||
<string name="settings_database_key" translatable="false">settings_database_key</string>
|
||||
<string name="settings_database_change_credentials_key" translatable="false">settings_database_change_credentials_key</string>
|
||||
<string name="database_general_key" translatable="false">database_general_key</string>
|
||||
|
||||
<string name="database_general_key" translatable="false">database_general_key</string>
|
||||
<string name="database_name_key" translatable="false">database_name_key</string>
|
||||
<string name="database_description_key" translatable="false">database_description_key</string>
|
||||
<string name="recycle_bin_key" translatable="false">recycle_bin_key</string>
|
||||
<string name="database_version_key" translatable="false">database_version_key</string>
|
||||
|
||||
<string name="database_history_key" translatable="false">database_history_key</string>
|
||||
<string name="max_history_items_key" translatable="false">max_history_items_key</string>
|
||||
<string name="max_history_size_key" translatable="false">max_history_size_key</string>
|
||||
<string name="recycle_bin_key" translatable="false">recycle_bin_key</string>
|
||||
|
||||
<string name="encryption_algorithm_key" translatable="false">algorithm</string>
|
||||
<string name="key_derivation_function_key" translatable="false">key_derivation_function_key</string>
|
||||
<string name="transform_rounds_key" translatable="false">transform_rounds_key</string>
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
<string name="entry_created">Created</string>
|
||||
<string name="entry_expires">Expires</string>
|
||||
<string name="entry_UUID">UUID</string>
|
||||
<string name="entry_history">History</string>
|
||||
<string name="entry_keyfile">Keyfile</string>
|
||||
<string name="entry_modified">Modified</string>
|
||||
<string name="entry_not_found">Could not find entry data.</string>
|
||||
@@ -101,7 +102,6 @@
|
||||
<string name="error_load_database_KDF_memory">Could not load the key. Try to lower the KDF \"Memory Usage\".</string>
|
||||
<string name="error_pass_gen_type">At least one password generation type must be selected.</string>
|
||||
<string name="error_pass_match">The passwords do not match.</string>
|
||||
<string name="error_rounds_not_number">Make \"Transformation rounds\" a number.</string>
|
||||
<string name="error_rounds_too_large">\"Transformation rounds\" too high. Setting to 2147483648.</string>
|
||||
<string name="error_string_key">Each string must have a field name.</string>
|
||||
<string name="error_title_required">Add a title.</string>
|
||||
@@ -238,7 +238,7 @@
|
||||
<!--This problem could be with scanning, or something else.-->
|
||||
<string name="biometric_scanning_error">Biometric error: %1$s</string>
|
||||
<string name="no_credentials_stored">This database does not have stored credential yet.</string>
|
||||
<string name="history">History</string>
|
||||
<string name="database_history">History</string>
|
||||
<string name="menu_appearance_settings">Appearance</string>
|
||||
<string name="biometric">Biometric</string>
|
||||
<string name="general">General</string>
|
||||
@@ -288,6 +288,10 @@
|
||||
<string name="full_file_path_enable_summary">View the full file path</string>
|
||||
<string name="recycle_bin_title">Use recycle bin</string>
|
||||
<string name="recycle_bin_summary">Moves groups and entries to \"Recycle bin\" before deleting</string>
|
||||
<string name="max_history_items_title">Max. history items</string>
|
||||
<string name="max_history_items_summary">Limit number of history items per entry</string>
|
||||
<string name="max_history_size_title">Max. history size</string>
|
||||
<string name="max_history_size_summary">Limit history size per entry (in binary bytes)</string>
|
||||
<string name="monospace_font_fields_enable_title">Field font</string>
|
||||
<string name="monospace_font_fields_enable_summary">Change font used in fields for better character visibility</string>
|
||||
<string name="auto_open_file_uri_title">Open files by selecting</string>
|
||||
|
||||
@@ -312,6 +312,10 @@
|
||||
<item name="android:textColor">?attr/colorAccent</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
</style>
|
||||
<style name="KeepassDXStyle.TextAppearance.LabelTableTextStyle" parent="KeepassDXStyle.TextAppearance">
|
||||
<item name="android:textSize">12sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
<style name="KeepassDXStyle.TextAppearance.TextEntryItem" parent="KeepassDXStyle.TextAppearance">
|
||||
<item name="android:padding">5sp</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/history">
|
||||
android:title="@string/database_history">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="@string/recentfile_key"
|
||||
|
||||
@@ -36,12 +36,6 @@
|
||||
android:title="@string/database_description_title"
|
||||
android:positiveButtonText="@string/entry_save"
|
||||
android:negativeButtonText="@string/entry_cancel"/>
|
||||
<SwitchPreference
|
||||
android:key="@string/recycle_bin_key"
|
||||
android:persistent="false"
|
||||
android:title="@string/recycle_bin_title"
|
||||
android:summary="@string/recycle_bin_summary"
|
||||
android:checked="false"/>
|
||||
<Preference
|
||||
android:key="@string/database_version_key"
|
||||
android:persistent="false"
|
||||
@@ -49,6 +43,38 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/recycle_bin">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="@string/recycle_bin_key"
|
||||
android:persistent="false"
|
||||
android:title="@string/recycle_bin_title"
|
||||
android:summary="@string/recycle_bin_summary"
|
||||
android:checked="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="@string/database_history_key"
|
||||
android:title="@string/entry_history">
|
||||
|
||||
<com.kunzisoft.keepass.settings.preference.InputNumberPreference
|
||||
android:key="@string/max_history_items_key"
|
||||
android:persistent="false"
|
||||
android:title="@string/max_history_items_title"
|
||||
android:positiveButtonText="@string/entry_save"
|
||||
android:negativeButtonText="@string/entry_cancel"/>
|
||||
<com.kunzisoft.keepass.settings.preference.InputNumberPreference
|
||||
android:key="@string/max_history_size_key"
|
||||
android:persistent="false"
|
||||
android:title="@string/max_history_size_title"
|
||||
android:summary="@string/max_history_size_summary"
|
||||
android:positiveButtonText="@string/entry_save"
|
||||
android:negativeButtonText="@string/entry_cancel"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/encryption">
|
||||
|
||||
@@ -60,21 +86,21 @@
|
||||
android:key="@string/key_derivation_function_key"
|
||||
android:persistent="false"
|
||||
android:title="@string/key_derivation_function"/>
|
||||
<com.kunzisoft.keepass.settings.preference.InputNumberPreference
|
||||
<com.kunzisoft.keepass.settings.preference.InputKdfNumberPreference
|
||||
android:key="@string/transform_rounds_key"
|
||||
android:persistent="false"
|
||||
android:title="@string/rounds"
|
||||
custom:explanations="@string/rounds_explanation"
|
||||
android:positiveButtonText="@string/entry_save"
|
||||
android:negativeButtonText="@string/entry_cancel"/>
|
||||
<com.kunzisoft.keepass.settings.preference.InputNumberPreference
|
||||
<com.kunzisoft.keepass.settings.preference.InputKdfNumberPreference
|
||||
android:key="@string/memory_usage_key"
|
||||
android:persistent="false"
|
||||
android:title="@string/memory_usage"
|
||||
custom:explanations="@string/memory_usage_explanation"
|
||||
android:positiveButtonText="@string/entry_save"
|
||||
android:negativeButtonText="@string/entry_cancel"/>
|
||||
<com.kunzisoft.keepass.settings.preference.InputNumberPreference
|
||||
<com.kunzisoft.keepass.settings.preference.InputKdfNumberPreference
|
||||
android:key="@string/parallelism_key"
|
||||
android:persistent="false"
|
||||
android:title="@string/parallelism"
|
||||
|
||||
Reference in New Issue
Block a user