mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'release/3.1'
This commit is contained in:
10
CHANGELOG
10
CHANGELOG
@@ -1,3 +1,13 @@
|
|||||||
|
KeePassDX(3.1.0)
|
||||||
|
* Add breadcrumb
|
||||||
|
* Add path in search results #1148
|
||||||
|
* Add group info dialog #1177
|
||||||
|
* Manage colors #64 #913
|
||||||
|
* Fix UI in Android 8 #509
|
||||||
|
* Upgrade libs and SDK to 31 #833
|
||||||
|
* Fix parser of database v1 #1201
|
||||||
|
* Stop asking WRITE_EXTERNAL_STORAGE permission
|
||||||
|
|
||||||
KeePassDX(3.0.4)
|
KeePassDX(3.0.4)
|
||||||
* Fix autofill inline bugs #1173 #1165
|
* Fix autofill inline bugs #1173 #1165
|
||||||
* Small UI change
|
* Small UI change
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ Other questions? You can read the [FAQ](https://github.com/Kunzisoft/KeePassDX/w
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright © 2020 Jeremy Jamet / [Kunzisoft](https://www.kunzisoft.com).
|
Copyright © 2022 Jeremy Jamet / [Kunzisoft](https://www.kunzisoft.com).
|
||||||
|
|
||||||
This file is part of KeePassDX.
|
This file is part of KeePassDX.
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ apply plugin: 'kotlin-android'
|
|||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 31
|
||||||
buildToolsVersion "30.0.3"
|
buildToolsVersion "31.0.0"
|
||||||
ndkVersion "21.4.7075529"
|
ndkVersion "21.4.7075529"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.kunzisoft.keepass"
|
applicationId "com.kunzisoft.keepass"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 30
|
targetSdkVersion 31
|
||||||
versionCode = 91
|
versionCode = 92
|
||||||
versionName = "3.0.4"
|
versionName = "3.1.0"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
||||||
testApplicationId = "com.kunzisoft.keepass.tests"
|
testApplicationId = "com.kunzisoft.keepass.tests"
|
||||||
@@ -99,21 +99,21 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def room_version = "2.3.0"
|
def room_version = "2.4.1"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "androidx.appcompat:appcompat:$android_appcompat_version"
|
implementation "androidx.appcompat:appcompat:$android_appcompat_version"
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
||||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
implementation 'androidx.biometric:biometric:1.1.0'
|
implementation 'androidx.biometric:biometric:1.1.0'
|
||||||
implementation 'androidx.media:media:1.4.3'
|
implementation 'androidx.media:media:1.4.3'
|
||||||
// Lifecycle - LiveData - ViewModel - Coroutines
|
// Lifecycle - LiveData - ViewModel - Coroutines
|
||||||
implementation "androidx.core:core-ktx:$android_core_version"
|
implementation "androidx.core:core-ktx:$android_core_version"
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.3.6'
|
implementation 'androidx.fragment:fragment-ktx:1.4.0'
|
||||||
implementation "com.google.android.material:material:$android_material_version"
|
implementation "com.google.android.material:material:$android_material_version"
|
||||||
// Database
|
// Database
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
@@ -123,7 +123,7 @@ dependencies {
|
|||||||
// Time
|
// Time
|
||||||
implementation 'joda-time:joda-time:2.10.13'
|
implementation 'joda-time:joda-time:2.10.13'
|
||||||
// Color
|
// Color
|
||||||
implementation 'com.github.Kunzisoft:AndroidClearChroma:2.4'
|
implementation 'com.github.Kunzisoft:AndroidClearChroma:2.6'
|
||||||
// Education
|
// Education
|
||||||
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
|
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
|
||||||
// Apache Commons
|
// Apache Commons
|
||||||
|
|||||||
@@ -10,15 +10,12 @@
|
|||||||
android:anyDensity="true" />
|
android:anyDensity="true" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.FOREGROUND_SERVICE" />
|
android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.USE_BIOMETRIC" />
|
android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.VIBRATE"/>
|
android:name="android.permission.VIBRATE"/>
|
||||||
<!-- Write permission until Android 10 -->
|
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
|
||||||
android:maxSdkVersion="28"
|
|
||||||
tools:ignore="ScopedStorage" />
|
|
||||||
<!-- Open apps from links -->
|
<!-- Open apps from links -->
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
@@ -30,12 +27,13 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:name="com.kunzisoft.keepass.app.App"
|
android:name="com.kunzisoft.keepass.app.App"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/old_backup_rules"
|
||||||
|
android:dataExtractionRules="@xml/backup_rules"
|
||||||
android:backupAgent="com.kunzisoft.keepass.backup.SettingsBackupAgent"
|
android:backupAgent="com.kunzisoft.keepass.backup.SettingsBackupAgent"
|
||||||
android:largeHeap="true"
|
android:largeHeap="true"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:theme="@style/KeepassDXStyle.Night"
|
android:theme="@style/KeepassDXStyle.Night"
|
||||||
tools:targetApi="n">
|
tools:targetApi="s">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.backup.api_key"
|
android:name="com.google.android.backup.api_key"
|
||||||
android:value="${googleAndroidBackupAPIKey}" />
|
android:value="${googleAndroidBackupAPIKey}" />
|
||||||
|
|||||||
@@ -233,9 +233,8 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
// TODO Mutable
|
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
|
||||||
PendingIntent.FLAG_CANCEL_CURRENT
|
|
||||||
} else {
|
} else {
|
||||||
PendingIntent.FLAG_CANCEL_CURRENT
|
PendingIntent.FLAG_CANCEL_CURRENT
|
||||||
})
|
})
|
||||||
@@ -248,9 +247,8 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
|||||||
EntrySelectionHelper.addSpecialModeInIntent(this, SpecialMode.REGISTRATION)
|
EntrySelectionHelper.addSpecialModeInIntent(this, SpecialMode.REGISTRATION)
|
||||||
putExtra(KEY_REGISTER_INFO, registerInfo)
|
putExtra(KEY_REGISTER_INFO, registerInfo)
|
||||||
},
|
},
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
// TODO Mutable
|
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
|
||||||
PendingIntent.FLAG_CANCEL_CURRENT
|
|
||||||
} else {
|
} else {
|
||||||
PendingIntent.FLAG_CANCEL_CURRENT
|
PendingIntent.FLAG_CANCEL_CURRENT
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ import androidx.activity.result.ActivityResultLauncher
|
|||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||||
|
import androidx.core.graphics.BlendModeCompat
|
||||||
|
import androidx.core.graphics.ColorUtils
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
|
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.fragments.EntryFragment
|
import com.kunzisoft.keepass.activities.fragments.EntryFragment
|
||||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||||
@@ -58,7 +63,11 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
|||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager
|
import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager
|
||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
import com.kunzisoft.keepass.utils.*
|
import com.kunzisoft.keepass.utils.MenuUtil
|
||||||
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
import com.kunzisoft.keepass.utils.UuidUtil
|
||||||
|
import com.kunzisoft.keepass.view.changeControlColor
|
||||||
|
import com.kunzisoft.keepass.view.changeTitleColor
|
||||||
import com.kunzisoft.keepass.view.hideByFading
|
import com.kunzisoft.keepass.view.hideByFading
|
||||||
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
||||||
import com.kunzisoft.keepass.viewmodels.EntryViewModel
|
import com.kunzisoft.keepass.viewmodels.EntryViewModel
|
||||||
@@ -68,9 +77,10 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
|
|
||||||
private var coordinatorLayout: CoordinatorLayout? = null
|
private var coordinatorLayout: CoordinatorLayout? = null
|
||||||
private var collapsingToolbarLayout: CollapsingToolbarLayout? = null
|
private var collapsingToolbarLayout: CollapsingToolbarLayout? = null
|
||||||
|
private var appBarLayout: AppBarLayout? = null
|
||||||
private var titleIconView: ImageView? = null
|
private var titleIconView: ImageView? = null
|
||||||
private var historyView: View? = null
|
private var historyView: View? = null
|
||||||
private var entryProgress: ProgressBar? = null
|
private var entryProgress: LinearProgressIndicator? = null
|
||||||
private var lockView: View? = null
|
private var lockView: View? = null
|
||||||
private var toolbar: Toolbar? = null
|
private var toolbar: Toolbar? = null
|
||||||
private var loadingView: ProgressBar? = null
|
private var loadingView: ProgressBar? = null
|
||||||
@@ -93,7 +103,12 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var mIcon: IconImage? = null
|
private var mIcon: IconImage? = null
|
||||||
private var mIconColor: Int = 0
|
private var mColorAccent: Int = 0
|
||||||
|
private var mControlColor: Int = 0
|
||||||
|
private var mColorPrimary: Int = 0
|
||||||
|
private var mColorBackground: Int = 0
|
||||||
|
private var mBackgroundColor: Int? = null
|
||||||
|
private var mForegroundColor: Int? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -108,6 +123,7 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
// Get views
|
// Get views
|
||||||
coordinatorLayout = findViewById(R.id.toolbar_coordinator)
|
coordinatorLayout = findViewById(R.id.toolbar_coordinator)
|
||||||
collapsingToolbarLayout = findViewById(R.id.toolbar_layout)
|
collapsingToolbarLayout = findViewById(R.id.toolbar_layout)
|
||||||
|
appBarLayout = findViewById(R.id.app_bar)
|
||||||
titleIconView = findViewById(R.id.entry_icon)
|
titleIconView = findViewById(R.id.entry_icon)
|
||||||
historyView = findViewById(R.id.history_container)
|
historyView = findViewById(R.id.history_container)
|
||||||
entryProgress = findViewById(R.id.entry_progress)
|
entryProgress = findViewById(R.id.entry_progress)
|
||||||
@@ -118,10 +134,19 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
collapsingToolbarLayout?.title = " "
|
collapsingToolbarLayout?.title = " "
|
||||||
toolbar?.title = " "
|
toolbar?.title = " "
|
||||||
|
|
||||||
// Retrieve the textColor to tint the icon
|
// Retrieve the textColor to tint the toolbar
|
||||||
val taIconColor = theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
|
val taColorAccent = theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
|
||||||
mIconColor = taIconColor.getColor(0, Color.BLACK)
|
val taControlColor = theme.obtainStyledAttributes(intArrayOf(R.attr.toolbarColorControl))
|
||||||
taIconColor.recycle()
|
val taColorPrimary = theme.obtainStyledAttributes(intArrayOf(R.attr.colorPrimary))
|
||||||
|
val taColorBackground = theme.obtainStyledAttributes(intArrayOf(android.R.attr.windowBackground))
|
||||||
|
mColorAccent = taColorAccent.getColor(0, Color.BLACK)
|
||||||
|
mControlColor = taControlColor.getColor(0, Color.BLACK)
|
||||||
|
mColorPrimary = taColorPrimary.getColor(0, Color.BLACK)
|
||||||
|
mColorBackground = taColorBackground.getColor(0, Color.BLACK)
|
||||||
|
taColorAccent.recycle()
|
||||||
|
taControlColor.recycle()
|
||||||
|
taColorPrimary.recycle()
|
||||||
|
taColorBackground.recycle()
|
||||||
|
|
||||||
// Get Entry from UUID
|
// Get Entry from UUID
|
||||||
try {
|
try {
|
||||||
@@ -166,10 +191,8 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
// Assign history dedicated view
|
// Assign history dedicated view
|
||||||
historyView?.visibility = if (entryIsHistory) View.VISIBLE else View.GONE
|
historyView?.visibility = if (entryIsHistory) View.VISIBLE else View.GONE
|
||||||
if (entryIsHistory) {
|
if (entryIsHistory) {
|
||||||
val taColorAccent = theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
|
|
||||||
collapsingToolbarLayout?.contentScrim =
|
collapsingToolbarLayout?.contentScrim =
|
||||||
ColorDrawable(taColorAccent.getColor(0, Color.BLACK))
|
ColorDrawable(mColorAccent)
|
||||||
taColorAccent.recycle()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val entryInfo = entryInfoHistory.entryInfo
|
val entryInfo = entryInfoHistory.entryInfo
|
||||||
@@ -184,15 +207,15 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
// Assign title icon
|
// Assign title icon
|
||||||
mIcon = entryInfo.icon
|
mIcon = entryInfo.icon
|
||||||
titleIconView?.let { iconView ->
|
|
||||||
mIconDrawableFactory?.assignDatabaseIcon(iconView, entryInfo.icon, mIconColor)
|
|
||||||
}
|
|
||||||
// Assign title text
|
// Assign title text
|
||||||
val entryTitle =
|
val entryTitle =
|
||||||
if (entryInfo.title.isNotEmpty()) entryInfo.title else entryInfo.id.toString()
|
if (entryInfo.title.isNotEmpty()) entryInfo.title else UuidUtil.toHexString(entryInfo.id)
|
||||||
collapsingToolbarLayout?.title = entryTitle
|
collapsingToolbarLayout?.title = entryTitle
|
||||||
toolbar?.title = entryTitle
|
toolbar?.title = entryTitle
|
||||||
mUrl = entryInfo.url
|
mUrl = entryInfo.url
|
||||||
|
// Assign colors
|
||||||
|
mBackgroundColor = entryInfo.backgroundColor
|
||||||
|
mForegroundColor = entryInfo.foregroundColor
|
||||||
|
|
||||||
loadingView?.hideByFading()
|
loadingView?.hideByFading()
|
||||||
mEntryLoaded = true
|
mEntryLoaded = true
|
||||||
@@ -204,9 +227,9 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mEntryViewModel.onOtpElementUpdated.observe(this) { otpElement ->
|
mEntryViewModel.onOtpElementUpdated.observe(this) { otpElement ->
|
||||||
if (otpElement == null)
|
if (otpElement == null) {
|
||||||
entryProgress?.visibility = View.GONE
|
entryProgress?.visibility = View.GONE
|
||||||
when (otpElement?.type) {
|
} else when (otpElement.type) {
|
||||||
// Only add token if HOTP
|
// Only add token if HOTP
|
||||||
OtpType.HOTP -> {
|
OtpType.HOTP -> {
|
||||||
entryProgress?.visibility = View.GONE
|
entryProgress?.visibility = View.GONE
|
||||||
@@ -215,7 +238,7 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
OtpType.TOTP -> {
|
OtpType.TOTP -> {
|
||||||
entryProgress?.apply {
|
entryProgress?.apply {
|
||||||
max = otpElement.period
|
max = otpElement.period
|
||||||
progress = otpElement.secondsRemaining
|
setProgressCompat(otpElement.secondsRemaining, true)
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,13 +275,6 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
super.onDatabaseRetrieved(database)
|
super.onDatabaseRetrieved(database)
|
||||||
|
|
||||||
mEntryViewModel.loadDatabase(database)
|
mEntryViewModel.loadDatabase(database)
|
||||||
|
|
||||||
// Assign title icon
|
|
||||||
mIcon?.let { icon ->
|
|
||||||
titleIconView?.let { iconView ->
|
|
||||||
mIconDrawableFactory?.assignDatabaseIcon(iconView, icon, mIconColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDatabaseActionFinished(
|
override fun onDatabaseActionFinished(
|
||||||
@@ -304,6 +320,29 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
super.onPause()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyToolbarColors() {
|
||||||
|
appBarLayout?.setBackgroundColor(mBackgroundColor ?: mColorPrimary)
|
||||||
|
collapsingToolbarLayout?.contentScrim = ColorDrawable(mBackgroundColor ?: mColorPrimary)
|
||||||
|
val backgroundDarker = if (mBackgroundColor != null) {
|
||||||
|
ColorUtils.blendARGB(mBackgroundColor!!, Color.WHITE, 0.1f)
|
||||||
|
} else {
|
||||||
|
mColorBackground
|
||||||
|
}
|
||||||
|
titleIconView?.background?.colorFilter = BlendModeColorFilterCompat
|
||||||
|
.createBlendModeColorFilterCompat(backgroundDarker, BlendModeCompat.SRC_IN)
|
||||||
|
mIcon?.let { icon ->
|
||||||
|
titleIconView?.let { iconView ->
|
||||||
|
mIconDrawableFactory?.assignDatabaseIcon(
|
||||||
|
iconView,
|
||||||
|
icon,
|
||||||
|
mForegroundColor ?: mColorAccent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toolbar?.changeControlColor(mForegroundColor ?: mControlColor)
|
||||||
|
collapsingToolbarLayout?.changeTitleColor(mForegroundColor ?: mControlColor)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
super.onCreateOptionsMenu(menu)
|
super.onCreateOptionsMenu(menu)
|
||||||
if (mEntryLoaded) {
|
if (mEntryLoaded) {
|
||||||
@@ -340,6 +379,7 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
if (mSpecialMode != SpecialMode.DEFAULT) {
|
if (mSpecialMode != SpecialMode.DEFAULT) {
|
||||||
menu?.findItem(R.id.menu_reload_database)?.isVisible = false
|
menu?.findItem(R.id.menu_reload_database)?.isVisible = false
|
||||||
}
|
}
|
||||||
|
applyToolbarColors()
|
||||||
return super.onPrepareOptionsMenu(menu)
|
return super.onPrepareOptionsMenu(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager
|
|||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
import com.kunzisoft.keepass.utils.UriUtil
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
import com.kunzisoft.keepass.view.*
|
import com.kunzisoft.keepass.view.*
|
||||||
|
import com.kunzisoft.keepass.viewmodels.ColorPickerViewModel
|
||||||
import com.kunzisoft.keepass.viewmodels.EntryEditViewModel
|
import com.kunzisoft.keepass.viewmodels.EntryEditViewModel
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -103,6 +104,8 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
private var mEntryLoaded: Boolean = false
|
private var mEntryLoaded: Boolean = false
|
||||||
private var mTemplatesSelectorAdapter: TemplatesSelectorAdapter? = null
|
private var mTemplatesSelectorAdapter: TemplatesSelectorAdapter? = null
|
||||||
|
|
||||||
|
private val mColorPickerViewModel: ColorPickerViewModel by viewModels()
|
||||||
|
|
||||||
private var mAllowCustomFields = false
|
private var mAllowCustomFields = false
|
||||||
private var mAllowOTP = false
|
private var mAllowOTP = false
|
||||||
|
|
||||||
@@ -243,6 +246,15 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
IconPickerActivity.launch(this@EntryEditActivity, iconImage, mIconSelectionActivityResultLauncher)
|
IconPickerActivity.launch(this@EntryEditActivity, iconImage, mIconSelectionActivityResultLauncher)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mEntryEditViewModel.requestColorSelection.observe(this) { color ->
|
||||||
|
ColorPickerDialogFragment.newInstance(color)
|
||||||
|
.show(supportFragmentManager, "ColorPickerFragment")
|
||||||
|
}
|
||||||
|
|
||||||
|
mColorPickerViewModel.colorPicked.observe(this) { color ->
|
||||||
|
mEntryEditViewModel.selectColor(color)
|
||||||
|
}
|
||||||
|
|
||||||
mEntryEditViewModel.requestDateTimeSelection.observe(this) { dateInstant ->
|
mEntryEditViewModel.requestDateTimeSelection.observe(this) { dateInstant ->
|
||||||
if (dateInstant.type == DateInstant.Type.TIME) {
|
if (dateInstant.type == DateInstant.Type.TIME) {
|
||||||
// Launch the time picker
|
// Launch the time picker
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import android.app.TimePickerDialog
|
|||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.PorterDuff
|
||||||
import android.os.*
|
import android.os.*
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
@@ -41,12 +41,15 @@ import androidx.appcompat.view.ActionMode
|
|||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.dialogs.*
|
import com.kunzisoft.keepass.activities.dialogs.*
|
||||||
import com.kunzisoft.keepass.activities.fragments.GroupFragment
|
import com.kunzisoft.keepass.activities.fragments.GroupFragment
|
||||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
|
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
|
||||||
|
import com.kunzisoft.keepass.adapters.BreadcrumbAdapter
|
||||||
import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter
|
import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter
|
||||||
import com.kunzisoft.keepass.autofill.AutofillComponent
|
import com.kunzisoft.keepass.autofill.AutofillComponent
|
||||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||||
@@ -60,6 +63,7 @@ import com.kunzisoft.keepass.model.GroupInfo
|
|||||||
import com.kunzisoft.keepass.model.RegisterInfo
|
import com.kunzisoft.keepass.model.RegisterInfo
|
||||||
import com.kunzisoft.keepass.model.SearchInfo
|
import com.kunzisoft.keepass.model.SearchInfo
|
||||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
||||||
|
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_GROUP_TASK
|
||||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.NEW_NODES_KEY
|
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.NEW_NODES_KEY
|
||||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.getListNodesFromBundle
|
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.getListNodesFromBundle
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
@@ -77,6 +81,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
GroupFragment.NodeClickListener,
|
GroupFragment.NodeClickListener,
|
||||||
GroupFragment.NodesActionMenuListener,
|
GroupFragment.NodesActionMenuListener,
|
||||||
GroupFragment.OnScrollListener,
|
GroupFragment.OnScrollListener,
|
||||||
|
GroupFragment.GroupRefreshedListener,
|
||||||
SortDialogFragment.SortSelectionListener {
|
SortDialogFragment.SortSelectionListener {
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
@@ -84,18 +89,25 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
private var coordinatorLayout: CoordinatorLayout? = null
|
private var coordinatorLayout: CoordinatorLayout? = null
|
||||||
private var lockView: View? = null
|
private var lockView: View? = null
|
||||||
private var toolbar: Toolbar? = null
|
private var toolbar: Toolbar? = null
|
||||||
|
private var databaseNameContainer: ViewGroup? = null
|
||||||
|
private var databaseColorView: ImageView? = null
|
||||||
|
private var databaseNameView: TextView? = null
|
||||||
|
private var searchContainer: ViewGroup? = null
|
||||||
|
private var searchNumbers: TextView? = null
|
||||||
|
private var searchString: TextView? = null
|
||||||
|
private var toolbarBreadcrumb: Toolbar? = null
|
||||||
private var searchTitleView: View? = null
|
private var searchTitleView: View? = null
|
||||||
private var toolbarAction: ToolbarAction? = null
|
private var toolbarAction: ToolbarAction? = null
|
||||||
private var iconView: ImageView? = null
|
|
||||||
private var numberChildrenView: TextView? = null
|
private var numberChildrenView: TextView? = null
|
||||||
private var addNodeButtonView: AddNodeButtonView? = null
|
private var addNodeButtonView: AddNodeButtonView? = null
|
||||||
private var groupNameView: TextView? = null
|
private var breadcrumbListView: RecyclerView? = null
|
||||||
private var groupMetaView: TextView? = null
|
|
||||||
private var loadingView: ProgressBar? = null
|
private var loadingView: ProgressBar? = null
|
||||||
|
|
||||||
private val mGroupViewModel: GroupViewModel by viewModels()
|
private val mGroupViewModel: GroupViewModel by viewModels()
|
||||||
private val mGroupEditViewModel: GroupEditViewModel by viewModels()
|
private val mGroupEditViewModel: GroupEditViewModel by viewModels()
|
||||||
|
|
||||||
|
private var mBreadcrumbAdapter: BreadcrumbAdapter? = null
|
||||||
|
|
||||||
private var mGroupFragment: GroupFragment? = null
|
private var mGroupFragment: GroupFragment? = null
|
||||||
private var mRecyclingBinEnabled = false
|
private var mRecyclingBinEnabled = false
|
||||||
private var mRecyclingBinIsCurrentGroup = false
|
private var mRecyclingBinIsCurrentGroup = false
|
||||||
@@ -123,8 +135,6 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
AutofillHelper.buildActivityResultLauncher(this)
|
AutofillHelper.buildActivityResultLauncher(this)
|
||||||
else null
|
else null
|
||||||
|
|
||||||
private var mIconColor: Int = 0
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -134,13 +144,18 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
// Initialize views
|
// Initialize views
|
||||||
rootContainerView = findViewById(R.id.activity_group_container_view)
|
rootContainerView = findViewById(R.id.activity_group_container_view)
|
||||||
coordinatorLayout = findViewById(R.id.group_coordinator)
|
coordinatorLayout = findViewById(R.id.group_coordinator)
|
||||||
iconView = findViewById(R.id.group_icon)
|
|
||||||
numberChildrenView = findViewById(R.id.group_numbers)
|
numberChildrenView = findViewById(R.id.group_numbers)
|
||||||
addNodeButtonView = findViewById(R.id.add_node_button)
|
addNodeButtonView = findViewById(R.id.add_node_button)
|
||||||
toolbar = findViewById(R.id.toolbar)
|
toolbar = findViewById(R.id.toolbar)
|
||||||
|
databaseNameContainer = findViewById(R.id.database_name_container)
|
||||||
|
databaseColorView = findViewById(R.id.database_color)
|
||||||
|
databaseNameView = findViewById(R.id.database_name)
|
||||||
|
searchContainer = findViewById(R.id.search_container)
|
||||||
|
searchNumbers = findViewById(R.id.search_numbers)
|
||||||
|
searchString = findViewById(R.id.search_string)
|
||||||
|
toolbarBreadcrumb = findViewById(R.id.toolbar_breadcrumb)
|
||||||
searchTitleView = findViewById(R.id.search_title)
|
searchTitleView = findViewById(R.id.search_title)
|
||||||
groupNameView = findViewById(R.id.group_name)
|
breadcrumbListView = findViewById(R.id.breadcrumb_list)
|
||||||
groupMetaView = findViewById(R.id.group_meta)
|
|
||||||
toolbarAction = findViewById(R.id.toolbar_action)
|
toolbarAction = findViewById(R.id.toolbar_action)
|
||||||
lockView = findViewById(R.id.lock_button)
|
lockView = findViewById(R.id.lock_button)
|
||||||
loadingView = findViewById(R.id.loading)
|
loadingView = findViewById(R.id.loading)
|
||||||
@@ -152,10 +167,42 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
toolbar?.title = ""
|
toolbar?.title = ""
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
// Retrieve the textColor to tint the icon
|
mBreadcrumbAdapter = BreadcrumbAdapter(this).apply {
|
||||||
val taTextColor = theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse))
|
// Open group on breadcrumb click
|
||||||
mIconColor = taTextColor.getColor(0, Color.WHITE)
|
onItemClickListener = { node, _ ->
|
||||||
taTextColor.recycle()
|
// If last item & not a virtual root group
|
||||||
|
val currentGroup = mCurrentGroup
|
||||||
|
if (currentGroup != null && node == currentGroup
|
||||||
|
&& (currentGroup != mDatabase?.rootGroup
|
||||||
|
|| mDatabase?.rootGroupIsVirtual == false)
|
||||||
|
) {
|
||||||
|
finishNodeAction()
|
||||||
|
launchDialogToShowGroupInfo(currentGroup)
|
||||||
|
} else {
|
||||||
|
if (mGroupFragment?.nodeActionSelectionMode == true) {
|
||||||
|
finishNodeAction()
|
||||||
|
}
|
||||||
|
mDatabase?.let { database ->
|
||||||
|
onNodeClick(database, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onLongItemClickListener = { node, position ->
|
||||||
|
val currentGroup = mCurrentGroup
|
||||||
|
if (currentGroup != null && node == currentGroup
|
||||||
|
&& (currentGroup != mDatabase?.rootGroup
|
||||||
|
|| mDatabase?.rootGroupIsVirtual == false)
|
||||||
|
) {
|
||||||
|
finishNodeAction()
|
||||||
|
launchDialogForGroupUpdate(currentGroup)
|
||||||
|
} else {
|
||||||
|
onItemClickListener?.invoke(node, position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
breadcrumbListView?.apply {
|
||||||
|
adapter = mBreadcrumbAdapter
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve group if defined at launch
|
// Retrieve group if defined at launch
|
||||||
manageIntent(intent)
|
manageIntent(intent)
|
||||||
@@ -213,23 +260,21 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
|
|
||||||
// Add listeners to the add buttons
|
// Add listeners to the add buttons
|
||||||
addNodeButtonView?.setAddGroupClickListener {
|
addNodeButtonView?.setAddGroupClickListener {
|
||||||
GroupEditDialogFragment.create(GroupInfo().apply {
|
launchDialogForGroupCreation(currentGroup)
|
||||||
if (currentGroup.allowAddNoteInGroup) {
|
|
||||||
notes = ""
|
|
||||||
}
|
|
||||||
}).show(supportFragmentManager, GroupEditDialogFragment.TAG_CREATE_GROUP)
|
|
||||||
}
|
}
|
||||||
addNodeButtonView?.setAddEntryClickListener {
|
addNodeButtonView?.setAddEntryClickListener {
|
||||||
mDatabase?.let { database ->
|
mDatabase?.let { database ->
|
||||||
EntrySelectionHelper.doSpecialAction(intent,
|
EntrySelectionHelper.doSpecialAction(intent,
|
||||||
{
|
{
|
||||||
mGroupFragment?.mEntryActivityResultLauncher?.let { resultLauncher ->
|
mCurrentGroup?.nodeId?.let { currentParentGroupId ->
|
||||||
EntryEditActivity.launchToCreate(
|
mGroupFragment?.mEntryActivityResultLauncher?.let { resultLauncher ->
|
||||||
this@GroupActivity,
|
EntryEditActivity.launchToCreate(
|
||||||
database,
|
this@GroupActivity,
|
||||||
currentGroup.nodeId,
|
database,
|
||||||
resultLauncher
|
currentParentGroupId,
|
||||||
)
|
resultLauncher
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -282,9 +327,6 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assignGroupViewElements(currentGroup)
|
|
||||||
invalidateOptionsMenu()
|
|
||||||
|
|
||||||
loadingView?.hideByFading()
|
loadingView?.hideByFading()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +413,19 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
|
|
||||||
// Search suggestion
|
// Search suggestion
|
||||||
database?.let {
|
database?.let {
|
||||||
|
databaseNameView?.text = if (it.name.isNotEmpty()) it.name else getString(R.string.database)
|
||||||
|
val customColor = it.customColor
|
||||||
|
if (customColor != null) {
|
||||||
|
databaseColorView?.visibility = View.VISIBLE
|
||||||
|
databaseColorView?.setColorFilter(
|
||||||
|
customColor,
|
||||||
|
PorterDuff.Mode.SRC_IN
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
databaseColorView?.visibility = View.GONE
|
||||||
|
}
|
||||||
mSearchSuggestionAdapter = SearchEntryCursorAdapter(this, it)
|
mSearchSuggestionAdapter = SearchEntryCursorAdapter(this, it)
|
||||||
|
mBreadcrumbAdapter?.iconDrawableFactory = it.iconDrawableFactory
|
||||||
mOnSuggestionListener = object : SearchView.OnSuggestionListener {
|
mOnSuggestionListener = object : SearchView.OnSuggestionListener {
|
||||||
override fun onSuggestionClick(position: Int): Boolean {
|
override fun onSuggestionClick(position: Int): Boolean {
|
||||||
mSearchSuggestionAdapter?.let { searchAdapter ->
|
mSearchSuggestionAdapter?.let { searchAdapter ->
|
||||||
@@ -446,16 +500,27 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ACTION_DATABASE_UPDATE_GROUP_TASK -> {
|
||||||
|
if (result.isSuccess) {
|
||||||
|
try {
|
||||||
|
if (mCurrentGroup == newNodes[0] as Group)
|
||||||
|
reloadCurrentGroup()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"Unable to perform action after group update",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coordinatorLayout?.showActionErrorIfNeeded(result)
|
coordinatorLayout?.showActionErrorIfNeeded(result)
|
||||||
if (!result.isSuccess) {
|
if (!result.isSuccess) {
|
||||||
reloadCurrentGroup()
|
reloadCurrentGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
finishNodeAction()
|
finishNodeAction()
|
||||||
|
|
||||||
refreshNumberOfChildren(mCurrentGroup)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -500,62 +565,44 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onGroupRefreshed() {
|
||||||
|
mCurrentGroup?.let { currentGroup ->
|
||||||
|
assignGroupViewElements(currentGroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun assignGroupViewElements(group: Group?) {
|
private fun assignGroupViewElements(group: Group?) {
|
||||||
// Assign title
|
// Assign title
|
||||||
if (group != null) {
|
|
||||||
if (groupNameView != null) {
|
|
||||||
val title = group.title
|
|
||||||
groupNameView?.text = if (title.isNotEmpty()) title else getText(R.string.root)
|
|
||||||
groupNameView?.invalidate()
|
|
||||||
}
|
|
||||||
if (groupMetaView != null) {
|
|
||||||
val meta = group.nodeId.toString()
|
|
||||||
groupMetaView?.text = meta
|
|
||||||
if (meta.isNotEmpty()
|
|
||||||
&& !group.isVirtual
|
|
||||||
&& PreferencesUtil.showUUID(this)) {
|
|
||||||
groupMetaView?.visibility = View.VISIBLE
|
|
||||||
} else {
|
|
||||||
groupMetaView?.visibility = View.GONE
|
|
||||||
}
|
|
||||||
groupMetaView?.invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group?.isVirtual == true) {
|
if (group?.isVirtual == true) {
|
||||||
searchTitleView?.visibility = View.VISIBLE
|
searchContainer?.visibility = View.VISIBLE
|
||||||
if (toolbar != null) {
|
val title = group.title
|
||||||
toolbar?.navigationIcon = null
|
searchString?.text = if (title.isNotEmpty()) title else ""
|
||||||
}
|
searchNumbers?.text = group.numberOfChildEntries.toString()
|
||||||
iconView?.visibility = View.GONE
|
databaseNameContainer?.visibility = View.GONE
|
||||||
|
toolbarBreadcrumb?.navigationIcon = null
|
||||||
|
toolbarBreadcrumb?.collapse()
|
||||||
} else {
|
} else {
|
||||||
searchTitleView?.visibility = View.GONE
|
searchContainer?.visibility = View.GONE
|
||||||
// Assign the group icon depending of IconPack or custom icon
|
databaseNameContainer?.visibility = View.VISIBLE
|
||||||
iconView?.visibility = View.VISIBLE
|
// Refresh breadcrumb
|
||||||
group?.let { currentGroup ->
|
if (toolbarBreadcrumb?.isVisible != true) {
|
||||||
iconView?.let { imageView ->
|
toolbarBreadcrumb?.expand {
|
||||||
mIconDrawableFactory?.assignDatabaseIcon(
|
setBreadcrumbNode(group)
|
||||||
imageView,
|
|
||||||
currentGroup.icon,
|
|
||||||
mIconColor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toolbar != null) {
|
|
||||||
if (group.containsParent())
|
|
||||||
toolbar?.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp)
|
|
||||||
else {
|
|
||||||
toolbar?.navigationIcon = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Add breadcrumb
|
||||||
|
setBreadcrumbNode(group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign number of children
|
|
||||||
refreshNumberOfChildren(group)
|
|
||||||
|
|
||||||
// Hide button
|
|
||||||
initAddButton(group)
|
initAddButton(group)
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setBreadcrumbNode(group: Group?) {
|
||||||
|
mBreadcrumbAdapter?.apply {
|
||||||
|
setNode(group)
|
||||||
|
breadcrumbListView?.scrollToPosition(itemCount -1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initAddButton(group: Group?) {
|
private fun initAddButton(group: Group?) {
|
||||||
@@ -577,18 +624,6 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshNumberOfChildren(group: Group?) {
|
|
||||||
numberChildrenView?.apply {
|
|
||||||
if (PreferencesUtil.showNumberEntries(context)) {
|
|
||||||
group?.refreshNumberOfChildEntries(Group.ChildFilter.getDefaults(context))
|
|
||||||
text = group?.numberOfChildEntries?.toString() ?: ""
|
|
||||||
visibility = View.VISIBLE
|
|
||||||
} else {
|
|
||||||
visibility = View.GONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onScrolled(dy: Int) {
|
override fun onScrolled(dy: Int) {
|
||||||
if (actionNodeMode == null)
|
if (actionNodeMode == null)
|
||||||
addNodeButtonView?.hideOrShowButtonOnScrollListener(dy)
|
addNodeButtonView?.hideOrShowButtonOnScrollListener(dy)
|
||||||
@@ -815,12 +850,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
finishNodeAction()
|
finishNodeAction()
|
||||||
when (node.type) {
|
when (node.type) {
|
||||||
Type.GROUP -> {
|
Type.GROUP -> {
|
||||||
mOldGroupToUpdate = node as Group
|
launchDialogForGroupUpdate(node as Group)
|
||||||
GroupEditDialogFragment.update(mOldGroupToUpdate!!.getGroupInfo())
|
|
||||||
.show(
|
|
||||||
supportFragmentManager,
|
|
||||||
GroupEditDialogFragment.TAG_CREATE_GROUP
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Type.ENTRY -> {
|
Type.ENTRY -> {
|
||||||
mGroupFragment?.mEntryActivityResultLauncher?.let { resultLauncher ->
|
mGroupFragment?.mEntryActivityResultLauncher?.let { resultLauncher ->
|
||||||
@@ -837,6 +867,25 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun launchDialogToShowGroupInfo(group: Group) {
|
||||||
|
GroupDialogFragment.launch(group.getGroupInfo())
|
||||||
|
.show(supportFragmentManager, GroupDialogFragment.TAG_SHOW_GROUP)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun launchDialogForGroupCreation(group: Group) {
|
||||||
|
GroupEditDialogFragment.create(GroupInfo().apply {
|
||||||
|
if (group.allowAddNoteInGroup) {
|
||||||
|
notes = ""
|
||||||
|
}
|
||||||
|
}).show(supportFragmentManager, GroupEditDialogFragment.TAG_CREATE_GROUP)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun launchDialogForGroupUpdate(group: Group) {
|
||||||
|
mOldGroupToUpdate = group
|
||||||
|
GroupEditDialogFragment.update(group.getGroupInfo())
|
||||||
|
.show(supportFragmentManager, GroupEditDialogFragment.TAG_CREATE_GROUP)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCopyMenuClick(
|
override fun onCopyMenuClick(
|
||||||
database: Database,
|
database: Database,
|
||||||
nodes: List<Node>
|
nodes: List<Node>
|
||||||
@@ -1034,7 +1083,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
onBackPressed()
|
// TODO change database
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.menu_search ->
|
R.id.menu_search ->
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.activities
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -41,7 +40,6 @@ import androidx.annotation.RequiresApi
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.fragment.app.commit
|
import androidx.fragment.app.commit
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
@@ -101,18 +99,8 @@ class PasswordActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderL
|
|||||||
private var mRememberKeyFile: Boolean = false
|
private var mRememberKeyFile: Boolean = false
|
||||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||||
|
|
||||||
private var mPermissionAsked = false
|
|
||||||
private var mReadOnly: Boolean = false
|
private var mReadOnly: Boolean = false
|
||||||
private var mForceReadOnly: Boolean = false
|
private var mForceReadOnly: Boolean = false
|
||||||
set(value) {
|
|
||||||
infoContainerView?.visibility = if (value) {
|
|
||||||
mReadOnly = true
|
|
||||||
View.VISIBLE
|
|
||||||
} else {
|
|
||||||
View.GONE
|
|
||||||
}
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
private var mAutofillActivityResultLauncher: ActivityResultLauncher<Intent>? =
|
private var mAutofillActivityResultLauncher: ActivityResultLauncher<Intent>? =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||||
@@ -139,7 +127,6 @@ class PasswordActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderL
|
|||||||
infoContainerView = findViewById(R.id.activity_password_info_container)
|
infoContainerView = findViewById(R.id.activity_password_info_container)
|
||||||
coordinatorLayout = findViewById(R.id.activity_password_coordinator_layout)
|
coordinatorLayout = findViewById(R.id.activity_password_coordinator_layout)
|
||||||
|
|
||||||
mPermissionAsked = savedInstanceState?.getBoolean(KEY_PERMISSION_ASKED) ?: mPermissionAsked
|
|
||||||
mReadOnly = if (savedInstanceState != null && savedInstanceState.containsKey(KEY_READ_ONLY)) {
|
mReadOnly = if (savedInstanceState != null && savedInstanceState.containsKey(KEY_READ_ONLY)) {
|
||||||
savedInstanceState.getBoolean(KEY_READ_ONLY)
|
savedInstanceState.getBoolean(KEY_READ_ONLY)
|
||||||
} else {
|
} else {
|
||||||
@@ -208,10 +195,19 @@ class PasswordActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderL
|
|||||||
|
|
||||||
// Observe database file change
|
// Observe database file change
|
||||||
mDatabaseFileViewModel.databaseFileLoaded.observe(this) { databaseFile ->
|
mDatabaseFileViewModel.databaseFileLoaded.observe(this) { databaseFile ->
|
||||||
|
|
||||||
// Force read only if the file does not exists
|
// Force read only if the file does not exists
|
||||||
mForceReadOnly = databaseFile?.let {
|
val databaseFileNotExists = databaseFile?.let {
|
||||||
!it.databaseFileExists
|
!it.databaseFileExists
|
||||||
} ?: true
|
} ?: true
|
||||||
|
infoContainerView?.visibility = if (databaseFileNotExists) {
|
||||||
|
mReadOnly = true
|
||||||
|
View.VISIBLE
|
||||||
|
} else {
|
||||||
|
View.GONE
|
||||||
|
}
|
||||||
|
mForceReadOnly = databaseFileNotExists
|
||||||
|
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
|
|
||||||
// Post init uri with KeyFile only if needed
|
// Post init uri with KeyFile only if needed
|
||||||
@@ -249,8 +245,6 @@ class PasswordActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderL
|
|||||||
mDatabaseFileViewModel.loadDatabaseFile(databaseFileUri)
|
mDatabaseFileViewModel.loadDatabaseFile(databaseFileUri)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPermission()
|
|
||||||
|
|
||||||
mDatabase?.let { database ->
|
mDatabase?.let { database ->
|
||||||
launchGroupActivityIfLoaded(database)
|
launchGroupActivityIfLoaded(database)
|
||||||
}
|
}
|
||||||
@@ -510,7 +504,6 @@ class PasswordActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderL
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
outState.putBoolean(KEY_PERMISSION_ASKED, mPermissionAsked)
|
|
||||||
mDatabaseKeyFileUri?.let {
|
mDatabaseKeyFileUri?.let {
|
||||||
outState.putString(KEY_KEYFILE, it.toString())
|
outState.putString(KEY_KEYFILE, it.toString())
|
||||||
}
|
}
|
||||||
@@ -613,35 +606,6 @@ class PasswordActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderL
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check permission
|
|
||||||
private fun checkPermission() {
|
|
||||||
if (Build.VERSION.SDK_INT in 23..28
|
|
||||||
&& !mReadOnly
|
|
||||||
&& !mPermissionAsked) {
|
|
||||||
mPermissionAsked = true
|
|
||||||
// Check self permission to show or not the dialog
|
|
||||||
val writePermission = android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
val permissions = arrayOf(writePermission)
|
|
||||||
if (toolbar != null
|
|
||||||
&& ActivityCompat.checkSelfPermission(this, writePermission) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
ActivityCompat.requestPermissions(this, permissions, WRITE_EXTERNAL_STORAGE_REQUEST)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
||||||
|
|
||||||
when (requestCode) {
|
|
||||||
WRITE_EXTERNAL_STORAGE_REQUEST -> {
|
|
||||||
if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE))
|
|
||||||
Toast.makeText(this, R.string.read_only_warning, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// To fix multiple view education
|
// To fix multiple view education
|
||||||
private var performedEductionInProgress = false
|
private var performedEductionInProgress = false
|
||||||
private fun launchEducation(menu: Menu) {
|
private fun launchEducation(menu: Menu) {
|
||||||
@@ -729,8 +693,6 @@ class PasswordActivity : DatabaseModeActivity(), AdvancedUnlockFragment.BuilderL
|
|||||||
private const val KEY_READ_ONLY = "KEY_READ_ONLY"
|
private const val KEY_READ_ONLY = "KEY_READ_ONLY"
|
||||||
private const val KEY_PASSWORD = "password"
|
private const val KEY_PASSWORD = "password"
|
||||||
private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately"
|
private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately"
|
||||||
private const val KEY_PERMISSION_ASKED = "KEY_PERMISSION_ASKED"
|
|
||||||
private const val WRITE_EXTERNAL_STORAGE_REQUEST = 647
|
|
||||||
|
|
||||||
private fun buildAndLaunchIntent(activity: Activity, databaseFile: Uri, keyFile: Uri?,
|
private fun buildAndLaunchIntent(activity: Activity, databaseFile: Uri, keyFile: Uri?,
|
||||||
intentBuildLauncher: (Intent) -> Unit) {
|
intentBuildLauncher: (Intent) -> Unit) {
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package com.kunzisoft.keepass.activities.dialogs
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.CompoundButton
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import com.kunzisoft.androidclearchroma.view.ChromaColorView
|
||||||
|
import com.kunzisoft.keepass.R
|
||||||
|
import com.kunzisoft.keepass.viewmodels.ColorPickerViewModel
|
||||||
|
|
||||||
|
class ColorPickerDialogFragment : DatabaseDialogFragment() {
|
||||||
|
|
||||||
|
private val mColorPickerViewModel: ColorPickerViewModel by activityViewModels()
|
||||||
|
|
||||||
|
private lateinit var enableSwitchView: CompoundButton
|
||||||
|
private lateinit var chromaColorView: ChromaColorView
|
||||||
|
|
||||||
|
private var mDefaultColor = Color.WHITE
|
||||||
|
private var mActivated = false
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
|
||||||
|
activity?.let { activity ->
|
||||||
|
val root = activity.layoutInflater.inflate(R.layout.fragment_color_picker, null)
|
||||||
|
enableSwitchView = root.findViewById(R.id.switch_element)
|
||||||
|
chromaColorView = root.findViewById(R.id.chroma_color_view)
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
if (savedInstanceState.containsKey(ARG_INITIAL_COLOR)) {
|
||||||
|
mDefaultColor = savedInstanceState.getInt(ARG_INITIAL_COLOR)
|
||||||
|
}
|
||||||
|
if (savedInstanceState.containsKey(ARG_ACTIVATED)) {
|
||||||
|
mActivated = savedInstanceState.getBoolean(ARG_ACTIVATED)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arguments?.apply {
|
||||||
|
if (containsKey(ARG_INITIAL_COLOR)) {
|
||||||
|
mDefaultColor = getInt(ARG_INITIAL_COLOR)
|
||||||
|
}
|
||||||
|
if (containsKey(ARG_ACTIVATED)) {
|
||||||
|
mActivated = getBoolean(ARG_ACTIVATED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enableSwitchView.isChecked = mActivated
|
||||||
|
chromaColorView.currentColor = mDefaultColor
|
||||||
|
|
||||||
|
chromaColorView.setOnColorChangedListener {
|
||||||
|
if (!enableSwitchView.isChecked)
|
||||||
|
enableSwitchView.isChecked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder = AlertDialog.Builder(activity)
|
||||||
|
builder.setView(root)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
val color: Int? = if (enableSwitchView.isChecked)
|
||||||
|
chromaColorView.currentColor
|
||||||
|
else
|
||||||
|
null
|
||||||
|
mColorPickerViewModel.pickColor(color)
|
||||||
|
}
|
||||||
|
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.create()
|
||||||
|
}
|
||||||
|
return super.onCreateDialog(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putInt(ARG_INITIAL_COLOR, chromaColorView.currentColor)
|
||||||
|
outState.putBoolean(ARG_ACTIVATED, mActivated)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val ARG_INITIAL_COLOR = "ARG_INITIAL_COLOR"
|
||||||
|
private const val ARG_ACTIVATED = "ARG_ACTIVATED"
|
||||||
|
|
||||||
|
fun newInstance(
|
||||||
|
@ColorInt initialColor: Int?,
|
||||||
|
): ColorPickerDialogFragment {
|
||||||
|
return ColorPickerDialogFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putInt(ARG_INITIAL_COLOR, initialColor ?: Color.WHITE)
|
||||||
|
putBoolean(ARG_ACTIVATED, initialColor != null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||||
|
*
|
||||||
|
* This file is part of KeePassDX.
|
||||||
|
*
|
||||||
|
* KeePassDX is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* KeePassDX is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.kunzisoft.keepass.activities.dialogs
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.kunzisoft.keepass.R
|
||||||
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||||
|
import com.kunzisoft.keepass.model.GroupInfo
|
||||||
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
|
import com.kunzisoft.keepass.utils.UuidUtil
|
||||||
|
import com.kunzisoft.keepass.view.DateTimeFieldView
|
||||||
|
|
||||||
|
class GroupDialogFragment : DatabaseDialogFragment() {
|
||||||
|
|
||||||
|
private var mPopulateIconMethod: ((ImageView, IconImage) -> Unit)? = null
|
||||||
|
private var mGroupInfo = GroupInfo()
|
||||||
|
|
||||||
|
private lateinit var iconView: ImageView
|
||||||
|
private var mIconColor: Int = 0
|
||||||
|
private lateinit var nameTextView: TextView
|
||||||
|
private lateinit var notesTextLabelView: TextView
|
||||||
|
private lateinit var notesTextView: TextView
|
||||||
|
private lateinit var expirationView: DateTimeFieldView
|
||||||
|
private lateinit var creationView: TextView
|
||||||
|
private lateinit var modificationView: TextView
|
||||||
|
private lateinit var uuidContainerView: ViewGroup
|
||||||
|
private lateinit var uuidReferenceView: TextView
|
||||||
|
|
||||||
|
override fun onDatabaseRetrieved(database: Database?) {
|
||||||
|
super.onDatabaseRetrieved(database)
|
||||||
|
mPopulateIconMethod = { imageView, icon ->
|
||||||
|
database?.iconDrawableFactory?.assignDatabaseIcon(imageView, icon, mIconColor)
|
||||||
|
}
|
||||||
|
mPopulateIconMethod?.invoke(iconView, mGroupInfo.icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
activity?.let { activity ->
|
||||||
|
val root = activity.layoutInflater.inflate(R.layout.fragment_group, null)
|
||||||
|
iconView = root.findViewById(R.id.group_icon)
|
||||||
|
nameTextView = root.findViewById(R.id.group_name)
|
||||||
|
notesTextLabelView = root.findViewById(R.id.group_note_label)
|
||||||
|
notesTextView = root.findViewById(R.id.group_note)
|
||||||
|
expirationView = root.findViewById(R.id.group_expiration)
|
||||||
|
creationView = root.findViewById(R.id.group_created)
|
||||||
|
modificationView = root.findViewById(R.id.group_modified)
|
||||||
|
uuidContainerView = root.findViewById(R.id.group_UUID_container)
|
||||||
|
uuidReferenceView = root.findViewById(R.id.group_UUID_reference)
|
||||||
|
|
||||||
|
// Retrieve the textColor to tint the icon
|
||||||
|
val ta = activity.theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
|
||||||
|
mIconColor = ta.getColor(0, Color.WHITE)
|
||||||
|
ta.recycle()
|
||||||
|
|
||||||
|
if (savedInstanceState != null
|
||||||
|
&& savedInstanceState.containsKey(KEY_GROUP_INFO)) {
|
||||||
|
mGroupInfo = savedInstanceState.getParcelable(KEY_GROUP_INFO) ?: mGroupInfo
|
||||||
|
} else {
|
||||||
|
arguments?.apply {
|
||||||
|
if (containsKey(KEY_GROUP_INFO)) {
|
||||||
|
mGroupInfo = getParcelable(KEY_GROUP_INFO) ?: mGroupInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// populate info in views
|
||||||
|
val title = mGroupInfo.title
|
||||||
|
if (title.isEmpty()) {
|
||||||
|
nameTextView.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
nameTextView.text = title
|
||||||
|
nameTextView.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
val notes = mGroupInfo.notes
|
||||||
|
if (notes == null || notes.isEmpty()) {
|
||||||
|
notesTextLabelView.visibility = View.GONE
|
||||||
|
notesTextView.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
notesTextView.text = notes
|
||||||
|
notesTextLabelView.visibility = View.VISIBLE
|
||||||
|
notesTextView.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
expirationView.activation = mGroupInfo.expires
|
||||||
|
expirationView.dateTime = mGroupInfo.expiryTime
|
||||||
|
creationView.text = mGroupInfo.creationTime.getDateTimeString(resources)
|
||||||
|
modificationView.text = mGroupInfo.lastModificationTime.getDateTimeString(resources)
|
||||||
|
val uuid = UuidUtil.toHexString(mGroupInfo.id)
|
||||||
|
if (uuid == null || uuid.isEmpty()) {
|
||||||
|
uuidContainerView.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
uuidReferenceView.text = uuid
|
||||||
|
uuidContainerView.apply {
|
||||||
|
visibility = if (PreferencesUtil.showUUID(context)) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder = AlertDialog.Builder(activity)
|
||||||
|
builder.setView(root)
|
||||||
|
.setPositiveButton(android.R.string.ok){ _, _ ->
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
return builder.create()
|
||||||
|
}
|
||||||
|
return super.onCreateDialog(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
outState.putParcelable(KEY_GROUP_INFO, mGroupInfo)
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Error(val isError: Boolean, val messageId: Int?)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG_SHOW_GROUP = "TAG_SHOW_GROUP"
|
||||||
|
private const val KEY_GROUP_INFO = "KEY_GROUP_INFO"
|
||||||
|
|
||||||
|
fun launch(groupInfo: GroupInfo): GroupDialogFragment {
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.putParcelable(KEY_GROUP_INFO, groupInfo)
|
||||||
|
val fragment = GroupDialogFragment()
|
||||||
|
fragment.arguments = bundle
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -246,8 +246,8 @@ class GroupEditDialogFragment : DatabaseDialogFragment() {
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val TAG_CREATE_GROUP = "TAG_CREATE_GROUP"
|
const val TAG_CREATE_GROUP = "TAG_CREATE_GROUP"
|
||||||
const val KEY_ACTION_ID = "KEY_ACTION_ID"
|
private const val KEY_ACTION_ID = "KEY_ACTION_ID"
|
||||||
const val KEY_GROUP_INFO = "KEY_GROUP_INFO"
|
private const val KEY_GROUP_INFO = "KEY_GROUP_INFO"
|
||||||
|
|
||||||
fun create(groupInfo: GroupInfo): GroupEditDialogFragment {
|
fun create(groupInfo: GroupInfo): GroupEditDialogFragment {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
|
|||||||
@@ -99,6 +99,12 @@ class EntryEditFragment: DatabaseFragment() {
|
|||||||
setOnIconClickListener {
|
setOnIconClickListener {
|
||||||
mEntryEditViewModel.requestIconSelection(templateView.getIcon())
|
mEntryEditViewModel.requestIconSelection(templateView.getIcon())
|
||||||
}
|
}
|
||||||
|
setOnBackgroundColorClickListener {
|
||||||
|
mEntryEditViewModel.requestBackgroundColorSelection(templateView.getBackgroundColor())
|
||||||
|
}
|
||||||
|
setOnForegroundColorClickListener {
|
||||||
|
mEntryEditViewModel.requestForegroundColorSelection(templateView.getForegroundColor())
|
||||||
|
}
|
||||||
setOnCustomEditionActionClickListener { field ->
|
setOnCustomEditionActionClickListener { field ->
|
||||||
mEntryEditViewModel.requestCustomFieldEdition(field)
|
mEntryEditViewModel.requestCustomFieldEdition(field)
|
||||||
}
|
}
|
||||||
@@ -147,6 +153,14 @@ class EntryEditFragment: DatabaseFragment() {
|
|||||||
templateView.setIcon(iconImage)
|
templateView.setIcon(iconImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mEntryEditViewModel.onBackgroundColorSelected.observe(this) { color ->
|
||||||
|
templateView.setBackgroundColor(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
mEntryEditViewModel.onForegroundColorSelected.observe(this) { color ->
|
||||||
|
templateView.setForegroundColor(color)
|
||||||
|
}
|
||||||
|
|
||||||
mEntryEditViewModel.onPasswordSelected.observe(viewLifecycleOwner) { passwordField ->
|
mEntryEditViewModel.onPasswordSelected.observe(viewLifecycleOwner) { passwordField ->
|
||||||
templateView.setPasswordField(passwordField)
|
templateView.setPasswordField(passwordField)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
private var attachmentsAdapter: EntryAttachmentsItemsAdapter? = null
|
private var attachmentsAdapter: EntryAttachmentsItemsAdapter? = null
|
||||||
|
|
||||||
private lateinit var uuidContainerView: View
|
private lateinit var uuidContainerView: View
|
||||||
private lateinit var uuidView: TextView
|
|
||||||
private lateinit var uuidReferenceView: TextView
|
private lateinit var uuidReferenceView: TextView
|
||||||
|
|
||||||
private var mClipboardHelper: ClipboardHelper? = null
|
private var mClipboardHelper: ClipboardHelper? = null
|
||||||
@@ -88,7 +87,6 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
uuidContainerView.apply {
|
uuidContainerView.apply {
|
||||||
visibility = if (PreferencesUtil.showUUID(context)) View.VISIBLE else View.GONE
|
visibility = if (PreferencesUtil.showUUID(context)) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
uuidView = view.findViewById(R.id.entry_UUID)
|
|
||||||
uuidReferenceView = view.findViewById(R.id.entry_UUID_reference)
|
uuidReferenceView = view.findViewById(R.id.entry_UUID_reference)
|
||||||
|
|
||||||
mEntryViewModel.entryInfoHistory.observe(viewLifecycleOwner) { entryInfoHistory ->
|
mEntryViewModel.entryInfoHistory.observe(viewLifecycleOwner) { entryInfoHistory ->
|
||||||
@@ -200,7 +198,6 @@ class EntryFragment: DatabaseFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun assignUUID(uuid: UUID?) {
|
private fun assignUUID(uuid: UUID?) {
|
||||||
uuidView.text = uuid?.toString()
|
|
||||||
uuidReferenceView.text = UuidUtil.toHexString(uuid)
|
uuidReferenceView.text = UuidUtil.toHexString(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package com.kunzisoft.keepass.activities.fragments
|
package com.kunzisoft.keepass.activities.fragments
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.*
|
import android.view.*
|
||||||
@@ -34,12 +33,11 @@ import com.kunzisoft.keepass.activities.EntryEditActivity
|
|||||||
import com.kunzisoft.keepass.activities.dialogs.SortDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.SortDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||||
import com.kunzisoft.keepass.adapters.NodeAdapter
|
import com.kunzisoft.keepass.adapters.NodesAdapter
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.Group
|
import com.kunzisoft.keepass.database.element.Group
|
||||||
import com.kunzisoft.keepass.database.element.SortNodeEnum
|
import com.kunzisoft.keepass.database.element.SortNodeEnum
|
||||||
import com.kunzisoft.keepass.database.element.node.Node
|
import com.kunzisoft.keepass.database.element.node.Node
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
|
||||||
import com.kunzisoft.keepass.database.element.node.Type
|
import com.kunzisoft.keepass.database.element.node.Type
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
@@ -50,10 +48,11 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
|
|
||||||
private var nodeClickListener: NodeClickListener? = null
|
private var nodeClickListener: NodeClickListener? = null
|
||||||
private var onScrollListener: OnScrollListener? = null
|
private var onScrollListener: OnScrollListener? = null
|
||||||
|
private var groupRefreshed: GroupRefreshedListener? = null
|
||||||
|
|
||||||
private var mNodesRecyclerView: RecyclerView? = null
|
private var mNodesRecyclerView: RecyclerView? = null
|
||||||
private var mLayoutManager: LinearLayoutManager? = null
|
private var mLayoutManager: LinearLayoutManager? = null
|
||||||
private var mAdapter: NodeAdapter? = null
|
private var mAdapter: NodesAdapter? = null
|
||||||
|
|
||||||
private val mGroupViewModel: GroupViewModel by activityViewModels()
|
private val mGroupViewModel: GroupViewModel by activityViewModels()
|
||||||
|
|
||||||
@@ -102,12 +101,14 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
super.onAttach(context)
|
super.onAttach(context)
|
||||||
|
|
||||||
|
// TODO Change to ViewModel
|
||||||
try {
|
try {
|
||||||
nodeClickListener = context as NodeClickListener
|
nodeClickListener = context as NodeClickListener
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
// The activity doesn't implement the interface, throw exception
|
// The activity doesn't implement the interface, throw exception
|
||||||
throw ClassCastException(context.toString()
|
throw ClassCastException(context.toString()
|
||||||
+ " must implement " + NodeAdapter.NodeClickCallback::class.java.name)
|
+ " must implement " + NodesAdapter.NodeClickCallback::class.java.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -115,14 +116,24 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
onScrollListener = null
|
onScrollListener = null
|
||||||
// Context menu can be omit
|
// Context menu can be omit
|
||||||
Log.w(TAG, context.toString()
|
Log.w(
|
||||||
|
TAG, context.toString()
|
||||||
+ " must implement " + RecyclerView.OnScrollListener::class.java.name)
|
+ " must implement " + RecyclerView.OnScrollListener::class.java.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
groupRefreshed = context as GroupRefreshedListener
|
||||||
|
} catch (e: ClassCastException) {
|
||||||
|
// The activity doesn't implement the interface, throw exception
|
||||||
|
throw ClassCastException(context.toString()
|
||||||
|
+ " must implement " + GroupRefreshedListener::class.java.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetach() {
|
override fun onDetach() {
|
||||||
nodeClickListener = null
|
nodeClickListener = null
|
||||||
onScrollListener = null
|
onScrollListener = null
|
||||||
|
groupRefreshed = null
|
||||||
super.onDetach()
|
super.onDetach()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,8 +149,8 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
|
|
||||||
contextThemed?.let { context ->
|
contextThemed?.let { context ->
|
||||||
database?.let { database ->
|
database?.let { database ->
|
||||||
mAdapter = NodeAdapter(context, database).apply {
|
mAdapter = NodesAdapter(context, database).apply {
|
||||||
setOnNodeClickListener(object : NodeAdapter.NodeClickCallback {
|
setOnNodeClickListener(object : NodesAdapter.NodeClickCallback {
|
||||||
override fun onNodeClick(database: Database, node: Node) {
|
override fun onNodeClick(database: Database, node: Node) {
|
||||||
if (nodeActionSelectionMode) {
|
if (nodeActionSelectionMode) {
|
||||||
if (listActionNodes.contains(node)) {
|
if (listActionNodes.contains(node)) {
|
||||||
@@ -195,7 +206,7 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
super.onCreateView(inflater, container, savedInstanceState)
|
super.onCreateView(inflater, container, savedInstanceState)
|
||||||
// To apply theme
|
// To apply theme
|
||||||
return inflater.cloneInContext(contextThemed)
|
return inflater.cloneInContext(contextThemed)
|
||||||
.inflate(R.layout.fragment_group, container, false)
|
.inflate(R.layout.fragment_nodes, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -260,6 +271,8 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
} else {
|
} else {
|
||||||
notFoundView?.visibility = View.GONE
|
notFoundView?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groupRefreshed?.onGroupRefreshed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSortSelected(sortNodeEnum: SortNodeEnum,
|
override fun onSortSelected(sortNodeEnum: SortNodeEnum,
|
||||||
@@ -292,15 +305,17 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
val sortDialogFragment: SortDialogFragment =
|
val sortDialogFragment: SortDialogFragment =
|
||||||
if (mRecycleBinEnable) {
|
if (mRecycleBinEnable) {
|
||||||
SortDialogFragment.getInstance(
|
SortDialogFragment.getInstance(
|
||||||
PreferencesUtil.getListSort(context),
|
PreferencesUtil.getListSort(context),
|
||||||
PreferencesUtil.getAscendingSort(context),
|
PreferencesUtil.getAscendingSort(context),
|
||||||
PreferencesUtil.getGroupsBeforeSort(context),
|
PreferencesUtil.getGroupsBeforeSort(context),
|
||||||
PreferencesUtil.getRecycleBinBottomSort(context))
|
PreferencesUtil.getRecycleBinBottomSort(context)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
SortDialogFragment.getInstance(
|
SortDialogFragment.getInstance(
|
||||||
PreferencesUtil.getListSort(context),
|
PreferencesUtil.getListSort(context),
|
||||||
PreferencesUtil.getAscendingSort(context),
|
PreferencesUtil.getAscendingSort(context),
|
||||||
PreferencesUtil.getGroupsBeforeSort(context))
|
PreferencesUtil.getGroupsBeforeSort(context)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
sortDialogFragment.show(childFragmentManager, "sortDialog")
|
sortDialogFragment.show(childFragmentManager, "sortDialog")
|
||||||
@@ -447,6 +462,10 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
fun onScrolled(dy: Int)
|
fun onScrolled(dy: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GroupRefreshedListener {
|
||||||
|
fun onGroupRefreshed()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = GroupFragment::class.java.name
|
private val TAG = GroupFragment::class.java.name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import android.util.Log
|
|||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.annotation.StyleRes
|
import androidx.annotation.StyleRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.kunzisoft.keepass.settings.NestedAppSettingsFragment.Companion.DATABASE_APPEARANCE_PREFERENCE_CHANGED
|
import com.kunzisoft.keepass.settings.NestedAppSettingsFragment.Companion.DATABASE_PREFERENCE_CHANGED
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stylish Hide Activity that apply a dynamic style and sets FLAG_SECURE to prevent screenshots / from
|
* Stylish Hide Activity that apply a dynamic style and sets FLAG_SECURE to prevent screenshots / from
|
||||||
@@ -89,8 +89,8 @@ abstract class StylishActivity : AppCompatActivity() {
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
if ((customStyle && Stylish.getThemeId(this) != this.themeId)
|
if ((customStyle && Stylish.getThemeId(this) != this.themeId)
|
||||||
|| DATABASE_APPEARANCE_PREFERENCE_CHANGED) {
|
|| DATABASE_PREFERENCE_CHANGED) {
|
||||||
DATABASE_APPEARANCE_PREFERENCE_CHANGED = false
|
DATABASE_PREFERENCE_CHANGED = false
|
||||||
Log.d(this.javaClass.name, "Theme change detected, restarting activity")
|
Log.d(this.javaClass.name, "Theme change detected, restarting activity")
|
||||||
recreateActivity()
|
recreateActivity()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
package com.kunzisoft.keepass.adapters
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.kunzisoft.keepass.R
|
||||||
|
import com.kunzisoft.keepass.database.element.Group
|
||||||
|
import com.kunzisoft.keepass.database.element.node.Node
|
||||||
|
import com.kunzisoft.keepass.database.element.node.Type
|
||||||
|
import com.kunzisoft.keepass.icons.IconDrawableFactory
|
||||||
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
|
import com.kunzisoft.keepass.view.strikeOut
|
||||||
|
|
||||||
|
class BreadcrumbAdapter(val context: Context)
|
||||||
|
: RecyclerView.Adapter<BreadcrumbAdapter.BreadcrumbGroupViewHolder>() {
|
||||||
|
|
||||||
|
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||||
|
var iconDrawableFactory: IconDrawableFactory? = null
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
private var mNodeBreadcrumb: MutableList<Node?> = mutableListOf()
|
||||||
|
var onItemClickListener: ((item: Node, position: Int)->Unit)? = null
|
||||||
|
var onLongItemClickListener: ((item: Node, position: Int)->Unit)? = null
|
||||||
|
|
||||||
|
private var mShowNumberEntries = false
|
||||||
|
private var mShowUUID = false
|
||||||
|
private var mIconColor: Int = 0
|
||||||
|
|
||||||
|
init {
|
||||||
|
mShowNumberEntries = PreferencesUtil.showNumberEntries(context)
|
||||||
|
mShowUUID = PreferencesUtil.showUUID(context)
|
||||||
|
|
||||||
|
// Retrieve the textColor to tint the icon
|
||||||
|
val taTextColor = context.theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse))
|
||||||
|
mIconColor = taTextColor.getColor(0, Color.WHITE)
|
||||||
|
taTextColor.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun setNode(node: Node?) {
|
||||||
|
mNodeBreadcrumb.clear()
|
||||||
|
node?.let {
|
||||||
|
var currentNode = it
|
||||||
|
mNodeBreadcrumb.add(0, currentNode)
|
||||||
|
while (currentNode.containsParent()) {
|
||||||
|
currentNode.parent?.let { parent ->
|
||||||
|
currentNode = parent
|
||||||
|
mNodeBreadcrumb.add(0, currentNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return when (position) {
|
||||||
|
mNodeBreadcrumb.size - 1 -> 0
|
||||||
|
else -> 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BreadcrumbGroupViewHolder {
|
||||||
|
return BreadcrumbGroupViewHolder(inflater.inflate(
|
||||||
|
when (viewType) {
|
||||||
|
0 -> R.layout.item_group
|
||||||
|
else -> R.layout.item_breadcrumb
|
||||||
|
}, parent, false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: BreadcrumbGroupViewHolder, position: Int) {
|
||||||
|
val node = mNodeBreadcrumb[position]
|
||||||
|
|
||||||
|
holder.groupNameView.apply {
|
||||||
|
text = node?.title ?: ""
|
||||||
|
strikeOut(node?.isCurrentlyExpires ?: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.itemView.apply {
|
||||||
|
setOnClickListener {
|
||||||
|
node?.let {
|
||||||
|
onItemClickListener?.invoke(it, position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setOnLongClickListener {
|
||||||
|
node?.let {
|
||||||
|
onLongItemClickListener?.invoke(it, position)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node?.type == Type.GROUP) {
|
||||||
|
(node as Group).let { group ->
|
||||||
|
|
||||||
|
holder.groupIconView?.let { imageView ->
|
||||||
|
iconDrawableFactory?.assignDatabaseIcon(
|
||||||
|
imageView,
|
||||||
|
group.icon,
|
||||||
|
mIconColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.groupNumbersView?.apply {
|
||||||
|
if (mShowNumberEntries) {
|
||||||
|
group.refreshNumberOfChildEntries(Group.ChildFilter.getDefaults(context))
|
||||||
|
text = group.numberOfChildEntries.toString()
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.groupMetaView?.apply {
|
||||||
|
val meta = group.nodeId.toVisualString()
|
||||||
|
visibility = if (meta != null
|
||||||
|
&& !group.isVirtual
|
||||||
|
&& mShowUUID
|
||||||
|
) {
|
||||||
|
text = meta
|
||||||
|
View.VISIBLE
|
||||||
|
} else {
|
||||||
|
View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return mNodeBreadcrumb.size
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class BreadcrumbGroupViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
var groupIconView: ImageView? = itemView.findViewById(R.id.group_icon)
|
||||||
|
var groupNumbersView: TextView? = itemView.findViewById(R.id.group_numbers)
|
||||||
|
var groupNameView: TextView = itemView.findViewById(R.id.group_name)
|
||||||
|
var groupMetaView: TextView? = itemView.findViewById(R.id.group_meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.SortedList
|
import androidx.recyclerview.widget.SortedList
|
||||||
import androidx.recyclerview.widget.SortedListAdapterCallback
|
import androidx.recyclerview.widget.SortedListAdapterCallback
|
||||||
|
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.Entry
|
import com.kunzisoft.keepass.database.element.Entry
|
||||||
@@ -55,9 +56,9 @@ import java.util.*
|
|||||||
* Create node list adapter with contextMenu or not
|
* Create node list adapter with contextMenu or not
|
||||||
* @param context Context to use
|
* @param context Context to use
|
||||||
*/
|
*/
|
||||||
class NodeAdapter (private val context: Context,
|
class NodesAdapter (private val context: Context,
|
||||||
private val database: Database)
|
private val database: Database)
|
||||||
: RecyclerView.Adapter<NodeAdapter.NodeViewHolder>() {
|
: RecyclerView.Adapter<NodesAdapter.NodeViewHolder>() {
|
||||||
|
|
||||||
private var mNodeComparator: Comparator<NodeVersionedInterface<Group>>? = null
|
private var mNodeComparator: Comparator<NodeVersionedInterface<Group>>? = null
|
||||||
private val mNodeSortedListCallback: NodeSortedListCallback
|
private val mNodeSortedListCallback: NodeSortedListCallback
|
||||||
@@ -79,6 +80,8 @@ class NodeAdapter (private val context: Context,
|
|||||||
private var mShowOTP: Boolean = false
|
private var mShowOTP: Boolean = false
|
||||||
private var mShowUUID: Boolean = false
|
private var mShowUUID: Boolean = false
|
||||||
private var mEntryFilters = arrayOf<Group.ChildFilter>()
|
private var mEntryFilters = arrayOf<Group.ChildFilter>()
|
||||||
|
private var mOldVirtualGroup = false
|
||||||
|
private var mVirtualGroup = false
|
||||||
|
|
||||||
private var mActionNodesList = LinkedList<Node>()
|
private var mActionNodesList = LinkedList<Node>()
|
||||||
private var mNodeClickCallback: NodeClickCallback? = null
|
private var mNodeClickCallback: NodeClickCallback? = null
|
||||||
@@ -87,9 +90,15 @@ class NodeAdapter (private val context: Context,
|
|||||||
@ColorInt
|
@ColorInt
|
||||||
private val mContentSelectionColor: Int
|
private val mContentSelectionColor: Int
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private val mIconGroupColor: Int
|
private val mTextColorPrimary: Int
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private val mIconEntryColor: Int
|
private val mTextColor: Int
|
||||||
|
@ColorInt
|
||||||
|
private val mTextColorSecondary: Int
|
||||||
|
@ColorInt
|
||||||
|
private val mColorAccentLight: Int
|
||||||
|
@ColorInt
|
||||||
|
private val mTextColorInverse: Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the adapter contains or not any element
|
* Determine if the adapter contains or not any element
|
||||||
@@ -110,12 +119,24 @@ class NodeAdapter (private val context: Context,
|
|||||||
this.mContentSelectionColor = ContextCompat.getColor(context, R.color.white)
|
this.mContentSelectionColor = ContextCompat.getColor(context, R.color.white)
|
||||||
// Retrieve the color to tint the icon
|
// Retrieve the color to tint the icon
|
||||||
val taTextColorPrimary = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary))
|
val taTextColorPrimary = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary))
|
||||||
this.mIconGroupColor = taTextColorPrimary.getColor(0, Color.BLACK)
|
this.mTextColorPrimary = taTextColorPrimary.getColor(0, Color.BLACK)
|
||||||
taTextColorPrimary.recycle()
|
taTextColorPrimary.recycle()
|
||||||
// In two times to fix bug compilation
|
// To get text color
|
||||||
val taTextColor = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor))
|
val taTextColor = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor))
|
||||||
this.mIconEntryColor = taTextColor.getColor(0, Color.BLACK)
|
this.mTextColor = taTextColor.getColor(0, Color.BLACK)
|
||||||
taTextColor.recycle()
|
taTextColor.recycle()
|
||||||
|
// To get text color secondary
|
||||||
|
val taTextColorSecondary = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColorSecondary))
|
||||||
|
this.mTextColorSecondary = taTextColorSecondary.getColor(0, Color.BLACK)
|
||||||
|
taTextColorSecondary.recycle()
|
||||||
|
// To get background color for selection
|
||||||
|
val taSelectionColor = context.theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccentLight))
|
||||||
|
this.mColorAccentLight = taSelectionColor.getColor(0, Color.GRAY)
|
||||||
|
taSelectionColor.recycle()
|
||||||
|
// To get text color for selection
|
||||||
|
val taSelectionTextColor = context.theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse))
|
||||||
|
this.mTextColorInverse = taSelectionTextColor.getColor(0, Color.WHITE)
|
||||||
|
taSelectionTextColor.recycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignPreferences() {
|
private fun assignPreferences() {
|
||||||
@@ -145,6 +166,8 @@ class NodeAdapter (private val context: Context,
|
|||||||
* Rebuild the list by clear and build children from the group
|
* Rebuild the list by clear and build children from the group
|
||||||
*/
|
*/
|
||||||
fun rebuildList(group: Group) {
|
fun rebuildList(group: Group) {
|
||||||
|
mOldVirtualGroup = mVirtualGroup
|
||||||
|
mVirtualGroup = group.isVirtual
|
||||||
assignPreferences()
|
assignPreferences()
|
||||||
mNodeSortedList.replaceAll(group.getFilteredChildren(mEntryFilters))
|
mNodeSortedList.replaceAll(group.getFilteredChildren(mEntryFilters))
|
||||||
}
|
}
|
||||||
@@ -155,14 +178,19 @@ class NodeAdapter (private val context: Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: Node, newItem: Node): Boolean {
|
override fun areContentsTheSame(oldItem: Node, newItem: Node): Boolean {
|
||||||
|
if (mOldVirtualGroup != mVirtualGroup)
|
||||||
|
return false
|
||||||
var typeContentTheSame = true
|
var typeContentTheSame = true
|
||||||
if (oldItem is Entry && newItem is Entry) {
|
if (oldItem is Entry && newItem is Entry) {
|
||||||
typeContentTheSame = oldItem.getVisualTitle() == newItem.getVisualTitle()
|
typeContentTheSame = oldItem.getVisualTitle() == newItem.getVisualTitle()
|
||||||
&& oldItem.username == newItem.username
|
&& oldItem.username == newItem.username
|
||||||
|
&& oldItem.backgroundColor == newItem.backgroundColor
|
||||||
|
&& oldItem.foregroundColor == newItem.foregroundColor
|
||||||
&& oldItem.getOtpElement() == newItem.getOtpElement()
|
&& oldItem.getOtpElement() == newItem.getOtpElement()
|
||||||
&& oldItem.containsAttachment() == newItem.containsAttachment()
|
&& oldItem.containsAttachment() == newItem.containsAttachment()
|
||||||
} else if (oldItem is Group && newItem is Group) {
|
} else if (oldItem is Group && newItem is Group) {
|
||||||
typeContentTheSame = oldItem.numberOfChildEntries == newItem.numberOfChildEntries
|
typeContentTheSame = oldItem.numberOfChildEntries == newItem.numberOfChildEntries
|
||||||
|
&& oldItem.notes == newItem.notes
|
||||||
}
|
}
|
||||||
return typeContentTheSame
|
return typeContentTheSame
|
||||||
&& oldItem.nodeId == newItem.nodeId
|
&& oldItem.nodeId == newItem.nodeId
|
||||||
@@ -327,8 +355,8 @@ class NodeAdapter (private val context: Context,
|
|||||||
val iconColor = if (holder.container.isSelected)
|
val iconColor = if (holder.container.isSelected)
|
||||||
mContentSelectionColor
|
mContentSelectionColor
|
||||||
else when (subNode.type) {
|
else when (subNode.type) {
|
||||||
Type.GROUP -> mIconGroupColor
|
Type.GROUP -> mTextColorPrimary
|
||||||
Type.ENTRY -> mIconEntryColor
|
Type.ENTRY -> mTextColor
|
||||||
}
|
}
|
||||||
holder.imageIdentifier?.setColorFilter(iconColor)
|
holder.imageIdentifier?.setColorFilter(iconColor)
|
||||||
holder.icon.apply {
|
holder.icon.apply {
|
||||||
@@ -348,14 +376,24 @@ class NodeAdapter (private val context: Context,
|
|||||||
}
|
}
|
||||||
// Add meta text to show UUID
|
// Add meta text to show UUID
|
||||||
holder.meta.apply {
|
holder.meta.apply {
|
||||||
if (mShowUUID) {
|
val nodeId = subNode.nodeId?.toVisualString()
|
||||||
text = subNode.nodeId.toString()
|
if (mShowUUID && nodeId != null) {
|
||||||
|
text = nodeId
|
||||||
setTextSize(mTextSizeUnit, mMetaTextDefaultDimension, mPrefSizeMultiplier)
|
setTextSize(mTextSizeUnit, mMetaTextDefaultDimension, mPrefSizeMultiplier)
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
visibility = View.GONE
|
visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Add path to virtual group
|
||||||
|
if (mVirtualGroup) {
|
||||||
|
holder.path?.apply {
|
||||||
|
text = subNode.getPathString()
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.path?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
// Specific elements for entry
|
// Specific elements for entry
|
||||||
if (subNode.type == Type.ENTRY) {
|
if (subNode.type == Type.ENTRY) {
|
||||||
@@ -398,6 +436,50 @@ class NodeAdapter (private val context: Context,
|
|||||||
holder.attachmentIcon?.visibility =
|
holder.attachmentIcon?.visibility =
|
||||||
if (entry.containsAttachment()) View.VISIBLE else View.GONE
|
if (entry.containsAttachment()) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
|
// Assign colors
|
||||||
|
val backgroundColor = entry.backgroundColor
|
||||||
|
if (!holder.container.isSelected) {
|
||||||
|
if (backgroundColor != null) {
|
||||||
|
holder.container.setBackgroundColor(backgroundColor)
|
||||||
|
} else {
|
||||||
|
holder.container.setBackgroundColor(Color.TRANSPARENT)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.container.setBackgroundColor(mColorAccentLight)
|
||||||
|
}
|
||||||
|
val foregroundColor = entry.foregroundColor
|
||||||
|
if (!holder.container.isSelected) {
|
||||||
|
if (foregroundColor != null) {
|
||||||
|
holder.text.setTextColor(foregroundColor)
|
||||||
|
holder.subText?.setTextColor(foregroundColor)
|
||||||
|
holder.otpToken?.setTextColor(foregroundColor)
|
||||||
|
holder.otpProgress?.setIndicatorColor(foregroundColor)
|
||||||
|
holder.attachmentIcon?.setColorFilter(foregroundColor)
|
||||||
|
holder.meta.setTextColor(foregroundColor)
|
||||||
|
holder.icon.apply {
|
||||||
|
database.iconDrawableFactory.assignDatabaseIcon(
|
||||||
|
this,
|
||||||
|
subNode.icon,
|
||||||
|
foregroundColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.text.setTextColor(mTextColor)
|
||||||
|
holder.subText?.setTextColor(mTextColorSecondary)
|
||||||
|
holder.otpToken?.setTextColor(mTextColorSecondary)
|
||||||
|
holder.otpProgress?.setIndicatorColor(mTextColorSecondary)
|
||||||
|
holder.attachmentIcon?.setColorFilter(mTextColorSecondary)
|
||||||
|
holder.meta.setTextColor(mTextColor)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.text.setTextColor(mTextColorInverse)
|
||||||
|
holder.subText?.setTextColor(mTextColorInverse)
|
||||||
|
holder.otpToken?.setTextColor(mTextColorInverse)
|
||||||
|
holder.otpProgress?.setIndicatorColor(mTextColorInverse)
|
||||||
|
holder.attachmentIcon?.setColorFilter(mTextColorInverse)
|
||||||
|
holder.meta.setTextColor(mTextColorInverse)
|
||||||
|
}
|
||||||
|
|
||||||
database.stopManageEntry(entry)
|
database.stopManageEntry(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,15 +512,16 @@ class NodeAdapter (private val context: Context,
|
|||||||
OtpType.HOTP -> {
|
OtpType.HOTP -> {
|
||||||
holder?.otpProgress?.apply {
|
holder?.otpProgress?.apply {
|
||||||
max = 100
|
max = 100
|
||||||
progress = 100
|
setProgressCompat(100, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OtpType.TOTP -> {
|
OtpType.TOTP -> {
|
||||||
holder?.otpProgress?.apply {
|
holder?.otpProgress?.apply {
|
||||||
max = otpElement.period
|
max = otpElement.period
|
||||||
progress = otpElement.secondsRemaining
|
setProgressCompat(otpElement.secondsRemaining, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
null -> {}
|
||||||
}
|
}
|
||||||
holder?.otpToken?.apply {
|
holder?.otpToken?.apply {
|
||||||
text = otpElement?.token
|
text = otpElement?.token
|
||||||
@@ -497,8 +580,9 @@ class NodeAdapter (private val context: Context,
|
|||||||
var text: TextView = itemView.findViewById(R.id.node_text)
|
var text: TextView = itemView.findViewById(R.id.node_text)
|
||||||
var subText: TextView? = itemView.findViewById(R.id.node_subtext)
|
var subText: TextView? = itemView.findViewById(R.id.node_subtext)
|
||||||
var meta: TextView = itemView.findViewById(R.id.node_meta)
|
var meta: TextView = itemView.findViewById(R.id.node_meta)
|
||||||
|
var path: TextView? = itemView.findViewById(R.id.node_path)
|
||||||
var otpContainer: ViewGroup? = itemView.findViewById(R.id.node_otp_container)
|
var otpContainer: ViewGroup? = itemView.findViewById(R.id.node_otp_container)
|
||||||
var otpProgress: ProgressBar? = itemView.findViewById(R.id.node_otp_progress)
|
var otpProgress: CircularProgressIndicator? = itemView.findViewById(R.id.node_otp_progress)
|
||||||
var otpToken: TextView? = itemView.findViewById(R.id.node_otp_token)
|
var otpToken: TextView? = itemView.findViewById(R.id.node_otp_token)
|
||||||
var otpRunnable: OtpRunnable = OtpRunnable(otpContainer)
|
var otpRunnable: OtpRunnable = OtpRunnable(otpContainer)
|
||||||
var numberChildren: TextView? = itemView.findViewById(R.id.node_child_numbers)
|
var numberChildren: TextView? = itemView.findViewById(R.id.node_child_numbers)
|
||||||
@@ -506,6 +590,6 @@ class NodeAdapter (private val context: Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = NodeAdapter::class.java.name
|
private val TAG = NodesAdapter::class.java.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,11 @@ abstract class ActionNodeDatabaseRunnable(
|
|||||||
abstract fun nodeAction()
|
abstract fun nodeAction()
|
||||||
|
|
||||||
override fun onStartRun() {
|
override fun onStartRun() {
|
||||||
nodeAction()
|
try {
|
||||||
|
nodeAction()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
setError(e)
|
||||||
|
}
|
||||||
super.onStartRun()
|
super.onStartRun()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ class UpdateGroupRunnable constructor(
|
|||||||
// Update group with new values
|
// Update group with new values
|
||||||
mNewGroup.touch(modified = true, touchParents = true)
|
mNewGroup.touch(modified = true, touchParents = true)
|
||||||
|
|
||||||
|
if (database.rootGroup == mOldGroup) {
|
||||||
|
database.rootGroup = mNewGroup
|
||||||
|
}
|
||||||
// Only change data in index
|
// Only change data in index
|
||||||
database.updateGroup(mNewGroup)
|
database.updateGroup(mNewGroup)
|
||||||
}
|
}
|
||||||
@@ -50,6 +53,9 @@ class UpdateGroupRunnable constructor(
|
|||||||
override fun nodeFinish(): ActionNodesValues {
|
override fun nodeFinish(): ActionNodesValues {
|
||||||
if (!result.isSuccess) {
|
if (!result.isSuccess) {
|
||||||
// If we fail to save, back out changes to global structure
|
// If we fail to save, back out changes to global structure
|
||||||
|
if (database.rootGroup == mNewGroup) {
|
||||||
|
database.rootGroup = mOldGroup
|
||||||
|
}
|
||||||
database.updateGroup(mOldGroup)
|
database.updateGroup(mOldGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ package com.kunzisoft.keepass.database.element
|
|||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
|
import com.kunzisoft.androidclearchroma.ChromaUtil
|
||||||
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
||||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||||
@@ -226,31 +226,33 @@ class Database {
|
|||||||
mDatabaseKDBX?.descriptionChanged = DateInstant()
|
mDatabaseKDBX?.descriptionChanged = DateInstant()
|
||||||
}
|
}
|
||||||
|
|
||||||
val allowDefaultUsername: Boolean
|
|
||||||
get() = mDatabaseKDBX != null
|
|
||||||
// TODO get() = mDatabaseKDB != null || mDatabaseKDBX != null
|
|
||||||
|
|
||||||
var defaultUsername: String
|
var defaultUsername: String
|
||||||
get() {
|
get() {
|
||||||
return mDatabaseKDBX?.defaultUserName ?: "" // TODO mDatabaseKDB default username
|
return mDatabaseKDB?.defaultUserName ?: mDatabaseKDBX?.defaultUserName ?: ""
|
||||||
}
|
}
|
||||||
set(username) {
|
set(username) {
|
||||||
|
mDatabaseKDB?.defaultUserName = username
|
||||||
mDatabaseKDBX?.defaultUserName = username
|
mDatabaseKDBX?.defaultUserName = username
|
||||||
mDatabaseKDBX?.defaultUserNameChanged = DateInstant()
|
mDatabaseKDBX?.defaultUserNameChanged = DateInstant()
|
||||||
}
|
}
|
||||||
|
|
||||||
val allowCustomColor: Boolean
|
var customColor: Int?
|
||||||
get() = mDatabaseKDBX != null
|
|
||||||
// TODO get() = mDatabaseKDB != null || mDatabaseKDBX != null
|
|
||||||
|
|
||||||
// with format "#000000"
|
|
||||||
var customColor: String
|
|
||||||
get() {
|
get() {
|
||||||
return mDatabaseKDBX?.color ?: "" // TODO mDatabaseKDB color
|
var colorInt: Int? = null
|
||||||
|
mDatabaseKDBX?.color?.let {
|
||||||
|
try {
|
||||||
|
colorInt = Color.parseColor(it)
|
||||||
|
} catch (e: Exception) {}
|
||||||
|
}
|
||||||
|
return mDatabaseKDB?.color ?: colorInt
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
// TODO Check color string
|
mDatabaseKDB?.color = value
|
||||||
mDatabaseKDBX?.color = value
|
mDatabaseKDBX?.color = if (value == null) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
ChromaUtil.getFormattedColorString(value, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val allowOTP: Boolean
|
val allowOTP: Boolean
|
||||||
@@ -364,7 +366,7 @@ class Database {
|
|||||||
mDatabaseKDBX?.masterKey = masterKey
|
mDatabaseKDBX?.masterKey = masterKey
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootGroup: Group?
|
var rootGroup: Group?
|
||||||
get() {
|
get() {
|
||||||
mDatabaseKDB?.rootGroup?.let {
|
mDatabaseKDB?.rootGroup?.let {
|
||||||
return Group(it)
|
return Group(it)
|
||||||
@@ -374,6 +376,25 @@ class Database {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
set(value) {
|
||||||
|
value?.groupKDB?.let { rootKDB ->
|
||||||
|
mDatabaseKDB?.rootGroup = rootKDB
|
||||||
|
}
|
||||||
|
value?.groupKDBX?.let { rootKDBX ->
|
||||||
|
mDatabaseKDBX?.rootGroup = rootKDBX
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val rootGroupIsVirtual: Boolean
|
||||||
|
get() {
|
||||||
|
mDatabaseKDB?.let {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
mDatabaseKDBX?.let {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do not modify groups here, used for read only
|
* Do not modify groups here, used for read only
|
||||||
|
|||||||
@@ -19,8 +19,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.element
|
package com.kunzisoft.keepass.database.element
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import com.kunzisoft.androidclearchroma.ChromaUtil
|
||||||
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
|
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||||
@@ -238,6 +240,42 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
|||||||
entryKDBX?.notes = value
|
entryKDBX?.notes = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var backgroundColor: Int?
|
||||||
|
get() {
|
||||||
|
var colorInt: Int? = null
|
||||||
|
entryKDBX?.backgroundColor?.let {
|
||||||
|
try {
|
||||||
|
colorInt = Color.parseColor(it)
|
||||||
|
} catch (e: Exception) {}
|
||||||
|
}
|
||||||
|
return colorInt
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
entryKDBX?.backgroundColor = if (value == null) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
ChromaUtil.getFormattedColorString(value, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var foregroundColor: Int?
|
||||||
|
get() {
|
||||||
|
var colorInt: Int? = null
|
||||||
|
entryKDBX?.foregroundColor?.let {
|
||||||
|
try {
|
||||||
|
colorInt = Color.parseColor(it)
|
||||||
|
} catch (e: Exception) {}
|
||||||
|
}
|
||||||
|
return colorInt
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
entryKDBX?.foregroundColor = if (value == null) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
ChromaUtil.getFormattedColorString(value, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun isTan(): Boolean {
|
private fun isTan(): Boolean {
|
||||||
return title == PMS_TAN_ENTRY && username.isNotEmpty()
|
return title == PMS_TAN_ENTRY && username.isNotEmpty()
|
||||||
}
|
}
|
||||||
@@ -419,6 +457,8 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
|||||||
entryInfo.expiryTime = expiryTime
|
entryInfo.expiryTime = expiryTime
|
||||||
entryInfo.url = url
|
entryInfo.url = url
|
||||||
entryInfo.notes = notes
|
entryInfo.notes = notes
|
||||||
|
entryInfo.backgroundColor = backgroundColor
|
||||||
|
entryInfo.foregroundColor = foregroundColor
|
||||||
entryInfo.customFields = getExtraFields().toMutableList()
|
entryInfo.customFields = getExtraFields().toMutableList()
|
||||||
// Add otpElement to generate token
|
// Add otpElement to generate token
|
||||||
entryInfo.otpModel = getOtpElement()?.otpModel
|
entryInfo.otpModel = getOtpElement()?.otpModel
|
||||||
@@ -453,6 +493,8 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
|||||||
expiryTime = newEntryInfo.expiryTime
|
expiryTime = newEntryInfo.expiryTime
|
||||||
url = newEntryInfo.url
|
url = newEntryInfo.url
|
||||||
notes = newEntryInfo.notes
|
notes = newEntryInfo.notes
|
||||||
|
backgroundColor = newEntryInfo.backgroundColor
|
||||||
|
foregroundColor = newEntryInfo.foregroundColor
|
||||||
addExtraFields(newEntryInfo.customFields)
|
addExtraFields(newEntryInfo.customFields)
|
||||||
database?.attachmentPool?.let { binaryPool ->
|
database?.attachmentPool?.let { binaryPool ->
|
||||||
newEntryInfo.attachments.forEach { attachment ->
|
newEntryInfo.attachments.forEach { attachment ->
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import com.kunzisoft.keepass.database.element.node.*
|
|||||||
import com.kunzisoft.keepass.model.EntryInfo
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
import com.kunzisoft.keepass.model.GroupInfo
|
import com.kunzisoft.keepass.model.GroupInfo
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
|
import com.kunzisoft.keepass.utils.UuidUtil
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
@@ -308,8 +309,9 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
|
|||||||
val withoutMetaStream = filters.contains(ChildFilter.META_STREAM)
|
val withoutMetaStream = filters.contains(ChildFilter.META_STREAM)
|
||||||
val showExpiredEntries = !filters.contains(ChildFilter.EXPIRED)
|
val showExpiredEntries = !filters.contains(ChildFilter.EXPIRED)
|
||||||
|
|
||||||
|
// TODO Change KDB parser to remove meta entries
|
||||||
return groupKDB?.getChildEntries()?.filter {
|
return groupKDB?.getChildEntries()?.filter {
|
||||||
(!withoutMetaStream || (withoutMetaStream && !it.isMetaStream))
|
(!withoutMetaStream || (withoutMetaStream && !it.isMetaStream()))
|
||||||
&& (!it.isCurrentlyExpires or showExpiredEntries)
|
&& (!it.isCurrentlyExpires or showExpiredEntries)
|
||||||
}?.map {
|
}?.map {
|
||||||
Entry(it)
|
Entry(it)
|
||||||
@@ -453,6 +455,7 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
|
|||||||
|
|
||||||
fun getGroupInfo(): GroupInfo {
|
fun getGroupInfo(): GroupInfo {
|
||||||
val groupInfo = GroupInfo()
|
val groupInfo = GroupInfo()
|
||||||
|
groupInfo.id = groupKDBX?.nodeId?.id
|
||||||
groupInfo.title = title
|
groupInfo.title = title
|
||||||
groupInfo.icon = icon
|
groupInfo.icon = icon
|
||||||
groupInfo.creationTime = creationTime
|
groupInfo.creationTime = creationTime
|
||||||
|
|||||||
@@ -44,15 +44,13 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
|||||||
get() = "V1"
|
get() = "V1"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
// New manual root because KDB contains multiple root groups (here available with getRootGroups())
|
||||||
|
rootGroup = createGroup().apply {
|
||||||
|
icon.standard = getStandardIcon(IconImageStandard.DATABASE_ID)
|
||||||
|
}
|
||||||
kdfListV3.add(KdfFactory.aesKdf)
|
kdfListV3.add(KdfFactory.aesKdf)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGroupById(groupId: Int): GroupKDB? {
|
|
||||||
if (groupId == -1)
|
|
||||||
return null
|
|
||||||
return getGroupById(NodeIdInt(groupId))
|
|
||||||
}
|
|
||||||
|
|
||||||
val backupGroup: GroupKDB?
|
val backupGroup: GroupKDB?
|
||||||
get() {
|
get() {
|
||||||
return retrieveBackup()
|
return retrieveBackup()
|
||||||
@@ -63,6 +61,10 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
|||||||
return listOf(BACKUP_FOLDER_TITLE)
|
return listOf(BACKUP_FOLDER_TITLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultUserName: String = ""
|
||||||
|
|
||||||
|
var color: Int? = null
|
||||||
|
|
||||||
override val kdfEngine: KdfEngine
|
override val kdfEngine: KdfEngine
|
||||||
get() = kdfListV3[0]
|
get() = kdfListV3[0]
|
||||||
|
|
||||||
@@ -77,11 +79,6 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootGroups: List<GroupKDB>
|
|
||||||
get() {
|
|
||||||
return rootGroup?.getChildGroups() ?: ArrayList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override val passwordEncoding: String
|
override val passwordEncoding: String
|
||||||
get() = "ISO-8859-1"
|
get() = "ISO-8859-1"
|
||||||
|
|
||||||
|
|||||||
@@ -414,37 +414,37 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getEntryByTitle(title: String, recursionLevel: Int): EntryKDBX? {
|
fun getEntryByTitle(title: String, recursionLevel: Int): EntryKDBX? {
|
||||||
return this.entryIndexes.values.find { entry ->
|
return findEntry { entry ->
|
||||||
entry.decodeTitleKey(recursionLevel).equals(title, true)
|
entry.decodeTitleKey(recursionLevel).equals(title, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEntryByUsername(username: String, recursionLevel: Int): EntryKDBX? {
|
fun getEntryByUsername(username: String, recursionLevel: Int): EntryKDBX? {
|
||||||
return this.entryIndexes.values.find { entry ->
|
return findEntry { entry ->
|
||||||
entry.decodeUsernameKey(recursionLevel).equals(username, true)
|
entry.decodeUsernameKey(recursionLevel).equals(username, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEntryByURL(url: String, recursionLevel: Int): EntryKDBX? {
|
fun getEntryByURL(url: String, recursionLevel: Int): EntryKDBX? {
|
||||||
return this.entryIndexes.values.find { entry ->
|
return findEntry { entry ->
|
||||||
entry.decodeUrlKey(recursionLevel).equals(url, true)
|
entry.decodeUrlKey(recursionLevel).equals(url, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEntryByPassword(password: String, recursionLevel: Int): EntryKDBX? {
|
fun getEntryByPassword(password: String, recursionLevel: Int): EntryKDBX? {
|
||||||
return this.entryIndexes.values.find { entry ->
|
return findEntry { entry ->
|
||||||
entry.decodePasswordKey(recursionLevel).equals(password, true)
|
entry.decodePasswordKey(recursionLevel).equals(password, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEntryByNotes(notes: String, recursionLevel: Int): EntryKDBX? {
|
fun getEntryByNotes(notes: String, recursionLevel: Int): EntryKDBX? {
|
||||||
return this.entryIndexes.values.find { entry ->
|
return findEntry { entry ->
|
||||||
entry.decodeNotesKey(recursionLevel).equals(notes, true)
|
entry.decodeNotesKey(recursionLevel).equals(notes, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEntryByCustomData(customDataValue: String): EntryKDBX? {
|
fun getEntryByCustomData(customDataValue: String): EntryKDBX? {
|
||||||
return entryIndexes.values.find { entry ->
|
return findEntry { entry ->
|
||||||
entry.customData.containsItemWithValue(customDataValue)
|
entry.customData.containsItemWithValue(customDataValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ abstract class DatabaseVersioned<
|
|||||||
var changeDuplicateId = false
|
var changeDuplicateId = false
|
||||||
|
|
||||||
private var groupIndexes = LinkedHashMap<NodeId<GroupId>, Group>()
|
private var groupIndexes = LinkedHashMap<NodeId<GroupId>, Group>()
|
||||||
protected var entryIndexes = LinkedHashMap<NodeId<EntryId>, Entry>()
|
private var entryIndexes = LinkedHashMap<NodeId<EntryId>, Entry>()
|
||||||
|
|
||||||
abstract val version: String
|
abstract val version: String
|
||||||
|
|
||||||
@@ -89,6 +89,7 @@ abstract class DatabaseVersioned<
|
|||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
value?.let {
|
value?.let {
|
||||||
|
removeGroupIndex(it)
|
||||||
addGroupIndex(it)
|
addGroupIndex(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,12 +199,6 @@ abstract class DatabaseVersioned<
|
|||||||
* -------------------------------------
|
* -------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun doForEachGroupInIndex(action: (Group) -> Unit) {
|
|
||||||
for (group in groupIndexes) {
|
|
||||||
action.invoke(group.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if an id number is already in use
|
* Determine if an id number is already in use
|
||||||
*
|
*
|
||||||
@@ -219,14 +214,7 @@ abstract class DatabaseVersioned<
|
|||||||
return groupIndexes.values
|
return groupIndexes.values
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setGroupIndexes(groupList: List<Group>) {
|
open fun getGroupById(id: NodeId<GroupId>): Group? {
|
||||||
this.groupIndexes.clear()
|
|
||||||
for (currentGroup in groupList) {
|
|
||||||
this.groupIndexes[currentGroup.nodeId] = currentGroup
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getGroupById(id: NodeId<GroupId>): Group? {
|
|
||||||
return this.groupIndexes[id]
|
return this.groupIndexes[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,16 +238,6 @@ abstract class DatabaseVersioned<
|
|||||||
this.groupIndexes.remove(group.nodeId)
|
this.groupIndexes.remove(group.nodeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun numberOfGroups(): Int {
|
|
||||||
return groupIndexes.size
|
|
||||||
}
|
|
||||||
|
|
||||||
fun doForEachEntryInIndex(action: (Entry) -> Unit) {
|
|
||||||
for (entry in entryIndexes) {
|
|
||||||
action.invoke(entry.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isEntryIdUsed(id: NodeId<EntryId>): Boolean {
|
fun isEntryIdUsed(id: NodeId<EntryId>): Boolean {
|
||||||
return entryIndexes.containsKey(id)
|
return entryIndexes.containsKey(id)
|
||||||
}
|
}
|
||||||
@@ -272,6 +250,10 @@ abstract class DatabaseVersioned<
|
|||||||
return this.entryIndexes[id]
|
return this.entryIndexes[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun findEntry(predicate: (Entry) -> Boolean): Entry? {
|
||||||
|
return this.entryIndexes.values.find(predicate)
|
||||||
|
}
|
||||||
|
|
||||||
fun addEntryIndex(entry: Entry) {
|
fun addEntryIndex(entry: Entry) {
|
||||||
val entryId = entry.nodeId
|
val entryId = entry.nodeId
|
||||||
if (entryIndexes.containsKey(entryId)) {
|
if (entryIndexes.containsKey(entryId)) {
|
||||||
@@ -292,10 +274,6 @@ abstract class DatabaseVersioned<
|
|||||||
this.entryIndexes.remove(entry.nodeId)
|
this.entryIndexes.remove(entry.nodeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun numberOfEntries(): Int {
|
|
||||||
return entryIndexes.size
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun clearCache() {
|
open fun clearCache() {
|
||||||
this.groupIndexes.clear()
|
this.groupIndexes.clear()
|
||||||
this.entryIndexes.clear()
|
this.entryIndexes.clear()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.kunzisoft.keepass.database.element.Attachment
|
|||||||
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
|
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
|
||||||
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
||||||
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
||||||
|
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.KEY_ID
|
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.KEY_ID
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||||
@@ -60,18 +61,43 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
|
|||||||
private var binaryDataId: Int? = null
|
private var binaryDataId: Int? = null
|
||||||
|
|
||||||
// Determine if this is a MetaStream entry
|
// Determine if this is a MetaStream entry
|
||||||
val isMetaStream: Boolean
|
fun isMetaStream(): Boolean {
|
||||||
get() {
|
if (notes.isEmpty()) return false
|
||||||
if (notes.isEmpty()) return false
|
if (binaryDescription != PMS_ID_BINDESC) return false
|
||||||
if (binaryDescription != PMS_ID_BINDESC) return false
|
if (title.isEmpty()) return false
|
||||||
if (title.isEmpty()) return false
|
if (title != PMS_ID_TITLE) return false
|
||||||
if (title != PMS_ID_TITLE) return false
|
if (username.isEmpty()) return false
|
||||||
if (username.isEmpty()) return false
|
if (username != PMS_ID_USER) return false
|
||||||
if (username != PMS_ID_USER) return false
|
if (url.isEmpty()) return false
|
||||||
if (url.isEmpty()) return false
|
if (url != PMS_ID_URL) return false
|
||||||
if (url != PMS_ID_URL) return false
|
return icon.standard.id == KEY_ID
|
||||||
return icon.standard.id == KEY_ID
|
}
|
||||||
}
|
|
||||||
|
fun isMetaStreamDefaultUsername(): Boolean {
|
||||||
|
return isMetaStream() && notes == PMS_STREAM_DEFAULTUSER
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setMetaStream() {
|
||||||
|
binaryDescription = PMS_ID_BINDESC
|
||||||
|
title = PMS_ID_TITLE
|
||||||
|
username = PMS_ID_USER
|
||||||
|
url = PMS_ID_URL
|
||||||
|
icon.standard = IconImageStandard(KEY_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMetaStreamDefaultUsername() {
|
||||||
|
notes = PMS_STREAM_DEFAULTUSER
|
||||||
|
setMetaStream()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isMetaStreamDatabaseColor(): Boolean {
|
||||||
|
return isMetaStream() && notes == PMS_STREAM_DBCOLOR
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMetaStreamDatabaseColor() {
|
||||||
|
notes = PMS_STREAM_DBCOLOR
|
||||||
|
setMetaStream()
|
||||||
|
}
|
||||||
|
|
||||||
override fun initNodeId(): NodeId<UUID> {
|
override fun initNodeId(): NodeId<UUID> {
|
||||||
return NodeIdUUID()
|
return NodeIdUUID()
|
||||||
@@ -184,6 +210,13 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
|
|||||||
private const val PMS_ID_USER = "SYSTEM"
|
private const val PMS_ID_USER = "SYSTEM"
|
||||||
private const val PMS_ID_URL = "$"
|
private const val PMS_ID_URL = "$"
|
||||||
|
|
||||||
|
const val PMS_STREAM_SIMPLESTATE = "Simple UI State"
|
||||||
|
const val PMS_STREAM_DEFAULTUSER = "Default User Name"
|
||||||
|
const val PMS_STREAM_SEARCHHISTORYITEM = "Search History Item"
|
||||||
|
const val PMS_STREAM_CUSTOMKVP = "Custom KVP"
|
||||||
|
const val PMS_STREAM_DBCOLOR = "Database Color"
|
||||||
|
const val PMS_STREAM_KPXICON2 = "KPX_CUSTOM_ICONS_2"
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val CREATOR: Parcelable.Creator<EntryKDB> = object : Parcelable.Creator<EntryKDB> {
|
val CREATOR: Parcelable.Creator<EntryKDB> = object : Parcelable.Creator<EntryKDB> {
|
||||||
override fun createFromParcel(parcel: Parcel): EntryKDB {
|
override fun createFromParcel(parcel: Parcel): EntryKDB {
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ class IconImageStandard : IconImageDraw {
|
|||||||
const val CREDIT_CARD_ID = 37
|
const val CREDIT_CARD_ID = 37
|
||||||
const val TRASH_ID = 43
|
const val TRASH_ID = 43
|
||||||
const val FOLDER_ID = 48
|
const val FOLDER_ID = 48
|
||||||
|
const val DATABASE_ID = 50
|
||||||
const val LIST_ID = 57
|
const val LIST_ID = 57
|
||||||
const val BUILD_ID = 59
|
const val BUILD_ID = 59
|
||||||
const val STAR_ID = 61
|
const val STAR_ID = 61
|
||||||
|
|||||||
@@ -32,6 +32,19 @@ interface Node: NodeVersionedInterface<Group> {
|
|||||||
fun removeParent() {
|
fun removeParent() {
|
||||||
parent = null
|
parent = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getPathString(): String {
|
||||||
|
val pathNodes = mutableListOf<Node>()
|
||||||
|
var currentNode = this
|
||||||
|
pathNodes.add(0, currentNode)
|
||||||
|
while (currentNode.containsParent()) {
|
||||||
|
currentNode.parent?.let { parent ->
|
||||||
|
currentNode = parent
|
||||||
|
pathNodes.add(0, currentNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pathNodes.joinToString("/") { it.title }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -44,4 +44,6 @@ abstract class NodeId<Id> : Parcelable {
|
|||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return id?.hashCode() ?: 0
|
return id?.hashCode() ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract fun toVisualString(): String?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ class NodeIdInt : NodeId<Int> {
|
|||||||
return id.toString()
|
return id.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toVisualString(): String? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
@JvmField
|
||||||
val CREATOR: Parcelable.Creator<NodeIdInt> = object : Parcelable.Creator<NodeIdInt> {
|
val CREATOR: Parcelable.Creator<NodeIdInt> = object : Parcelable.Creator<NodeIdInt> {
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ class NodeIdUUID : NodeId<UUID> {
|
|||||||
return UuidUtil.toHexString(id) ?: id.toString()
|
return UuidUtil.toHexString(id) ?: id.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toVisualString(): String {
|
||||||
|
return toString()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
@JvmField
|
||||||
val CREATOR: Parcelable.Creator<NodeIdUUID> = object : Parcelable.Creator<NodeIdUUID> {
|
val CREATOR: Parcelable.Creator<NodeIdUUID> = object : Parcelable.Creator<NodeIdUUID> {
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.file
|
package com.kunzisoft.keepass.database.file
|
||||||
|
|
||||||
import com.kunzisoft.keepass.utils.UnsignedInt
|
|
||||||
|
|
||||||
abstract class DatabaseHeader {
|
abstract class DatabaseHeader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,8 +31,4 @@ abstract class DatabaseHeader {
|
|||||||
*/
|
*/
|
||||||
var encryptionIV = ByteArray(16)
|
var encryptionIV = ByteArray(16)
|
||||||
|
|
||||||
companion object {
|
|
||||||
val PWM_DBSIG_1 = UnsignedInt(-0x655d26fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class DatabaseHeaderKDB : DatabaseHeader() {
|
|||||||
*/
|
*/
|
||||||
var transformSeed = ByteArray(32)
|
var transformSeed = ByteArray(32)
|
||||||
|
|
||||||
var signature1 = UnsignedInt(0) // = PWM_DBSIG_1
|
var signature1 = UnsignedInt(0) // = DBSIG_1
|
||||||
var signature2 = UnsignedInt(0) // = DBSIG_2
|
var signature2 = UnsignedInt(0) // = DBSIG_2
|
||||||
var flags= UnsignedInt(0)
|
var flags= UnsignedInt(0)
|
||||||
var version= UnsignedInt(0)
|
var version= UnsignedInt(0)
|
||||||
@@ -84,9 +84,9 @@ class DatabaseHeaderKDB : DatabaseHeader() {
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
// DB sig from KeePass 1.03
|
// DB sig from KeePass 1.03
|
||||||
val DBSIG_2 = UnsignedInt(-0x4ab4049b)
|
val DBSIG_1 = UnsignedInt(-0x655d26fd) // 0x9AA2D903
|
||||||
// DB sig from KeePass 1.03
|
val DBSIG_2 = UnsignedInt(-0x4ab4049b) // 0xB54BFB65
|
||||||
val DBVER_DW = UnsignedInt(0x00030003)
|
val DBVER_DW = UnsignedInt(0x00030004)
|
||||||
|
|
||||||
val FLAG_SHA2 = UnsignedInt(1)
|
val FLAG_SHA2 = UnsignedInt(1)
|
||||||
val FLAG_RIJNDAEL = UnsignedInt(2)
|
val FLAG_RIJNDAEL = UnsignedInt(2)
|
||||||
@@ -97,7 +97,7 @@ class DatabaseHeaderKDB : DatabaseHeader() {
|
|||||||
const val BUF_SIZE = 124
|
const val BUF_SIZE = 124
|
||||||
|
|
||||||
fun matchesHeader(sig1: UnsignedInt, sig2: UnsignedInt): Boolean {
|
fun matchesHeader(sig1: UnsignedInt, sig2: UnsignedInt): Boolean {
|
||||||
return sig1.toKotlinInt() == PWM_DBSIG_1.toKotlinInt() && sig2.toKotlinInt() == DBSIG_2.toKotlinInt()
|
return sig1.toKotlinInt() == DBSIG_1.toKotlinInt() && sig2.toKotlinInt() == DBSIG_2.toKotlinInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun compatibleHeaders(one: UnsignedInt, two: UnsignedInt): Boolean {
|
fun compatibleHeaders(one: UnsignedInt, two: UnsignedInt): Boolean {
|
||||||
|
|||||||
@@ -311,8 +311,9 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val DBSIG_PRE2 = UnsignedInt(-0x4ab4049a)
|
val DBSIG_1 = UnsignedInt(-0x655d26fd) // 0x9AA2D903
|
||||||
val DBSIG_2 = UnsignedInt(-0x4ab40499)
|
val DBSIG_PRE2 = UnsignedInt(-0x4ab4049a) // 0xB54BFB66
|
||||||
|
val DBSIG_2 = UnsignedInt(-0x4ab40499) // 0xB54BFB67
|
||||||
|
|
||||||
private val FILE_VERSION_CRITICAL_MASK = UnsignedInt(-0x10000)
|
private val FILE_VERSION_CRITICAL_MASK = UnsignedInt(-0x10000)
|
||||||
val FILE_VERSION_31 = UnsignedInt(0x00030001)
|
val FILE_VERSION_31 = UnsignedInt(0x00030001)
|
||||||
@@ -335,7 +336,7 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun matchesHeader(sig1: UnsignedInt, sig2: UnsignedInt): Boolean {
|
fun matchesHeader(sig1: UnsignedInt, sig2: UnsignedInt): Boolean {
|
||||||
return sig1 == PWM_DBSIG_1 && (sig2 == DBSIG_PRE2 || sig2 == DBSIG_2)
|
return sig1 == DBSIG_1 && (sig2 == DBSIG_PRE2 || sig2 == DBSIG_2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
package com.kunzisoft.keepass.database.file.input
|
package com.kunzisoft.keepass.database.file.input
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
import com.kunzisoft.encrypt.HashManager
|
import com.kunzisoft.encrypt.HashManager
|
||||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
@@ -30,7 +31,6 @@ import com.kunzisoft.keepass.database.element.group.GroupKDB
|
|||||||
import com.kunzisoft.keepass.database.element.node.NodeIdInt
|
import com.kunzisoft.keepass.database.element.node.NodeIdInt
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||||
import com.kunzisoft.keepass.database.exception.*
|
import com.kunzisoft.keepass.database.exception.*
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeader
|
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB
|
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB
|
||||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||||
import com.kunzisoft.keepass.utils.*
|
import com.kunzisoft.keepass.utils.*
|
||||||
@@ -98,7 +98,7 @@ class DatabaseInputKDB(cacheDirectory: File,
|
|||||||
if (fileSize != (contentSize + DatabaseHeaderKDB.BUF_SIZE))
|
if (fileSize != (contentSize + DatabaseHeaderKDB.BUF_SIZE))
|
||||||
throw IOException("Header corrupted")
|
throw IOException("Header corrupted")
|
||||||
|
|
||||||
if (header.signature1 != DatabaseHeader.PWM_DBSIG_1
|
if (header.signature1 != DatabaseHeaderKDB.DBSIG_1
|
||||||
|| header.signature2 != DatabaseHeaderKDB.DBSIG_2) {
|
|| header.signature2 != DatabaseHeaderKDB.DBSIG_2) {
|
||||||
throw SignatureDatabaseException()
|
throw SignatureDatabaseException()
|
||||||
}
|
}
|
||||||
@@ -153,10 +153,6 @@ class DatabaseInputKDB(cacheDirectory: File,
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// New manual root because KDB contains multiple root groups (here available with getRootGroups())
|
|
||||||
val newRoot = mDatabase.createGroup()
|
|
||||||
mDatabase.rootGroup = newRoot
|
|
||||||
|
|
||||||
// Import all nodes
|
// Import all nodes
|
||||||
val groupLevelList = HashMap<GroupKDB, Int>()
|
val groupLevelList = HashMap<GroupKDB, Int>()
|
||||||
var newGroup: GroupKDB? = null
|
var newGroup: GroupKDB? = null
|
||||||
@@ -303,7 +299,34 @@ class DatabaseInputKDB(cacheDirectory: File,
|
|||||||
newGroup = null
|
newGroup = null
|
||||||
}
|
}
|
||||||
newEntry?.let { entry ->
|
newEntry?.let { entry ->
|
||||||
mDatabase.addEntryIndex(entry)
|
// Parse meta info
|
||||||
|
when {
|
||||||
|
entry.isMetaStreamDefaultUsername() -> {
|
||||||
|
var defaultUser = ""
|
||||||
|
entry.getBinary(mDatabase.attachmentPool)
|
||||||
|
?.getInputDataStream(mDatabase.binaryCache)?.use {
|
||||||
|
defaultUser = String(it.readBytes())
|
||||||
|
}
|
||||||
|
mDatabase.defaultUserName = defaultUser
|
||||||
|
}
|
||||||
|
entry.isMetaStreamDatabaseColor() -> {
|
||||||
|
var color: Int? = null
|
||||||
|
entry.getBinary(mDatabase.attachmentPool)
|
||||||
|
?.getInputDataStream(mDatabase.binaryCache)?.use {
|
||||||
|
val reverseColor = UnsignedInt(it.readBytes4ToUInt()).toKotlinInt()
|
||||||
|
color = Color.rgb(
|
||||||
|
Color.blue(reverseColor),
|
||||||
|
Color.green(reverseColor),
|
||||||
|
Color.red(reverseColor)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mDatabase.color = color
|
||||||
|
}
|
||||||
|
// TODO manager other meta stream
|
||||||
|
else -> {
|
||||||
|
mDatabase.addEntryIndex(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
currentEntryNumber++
|
currentEntryNumber++
|
||||||
newEntry = null
|
newEntry = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ constructor(private val databaseKDBX: DatabaseKDBX,
|
|||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun output() {
|
fun output() {
|
||||||
|
|
||||||
mos.write4BytesUInt(DatabaseHeader.PWM_DBSIG_1)
|
mos.write4BytesUInt(DatabaseHeaderKDBX.DBSIG_1)
|
||||||
mos.write4BytesUInt(DatabaseHeaderKDBX.DBSIG_2)
|
mos.write4BytesUInt(DatabaseHeaderKDBX.DBSIG_2)
|
||||||
mos.write4BytesUInt(header.version)
|
mos.write4BytesUInt(header.version)
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.file.output
|
package com.kunzisoft.keepass.database.file.output
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
import com.kunzisoft.encrypt.HashManager
|
import com.kunzisoft.encrypt.HashManager
|
||||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDB
|
import com.kunzisoft.keepass.database.element.database.DatabaseKDB
|
||||||
|
import com.kunzisoft.keepass.database.element.entry.EntryKDB
|
||||||
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
||||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||||
import com.kunzisoft.keepass.database.file.DatabaseHeader
|
import com.kunzisoft.keepass.database.file.DatabaseHeader
|
||||||
@@ -34,7 +36,6 @@ import java.io.ByteArrayOutputStream
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.security.*
|
import java.security.*
|
||||||
import java.util.*
|
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.CipherOutputStream
|
import javax.crypto.CipherOutputStream
|
||||||
|
|
||||||
@@ -44,6 +45,9 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
|||||||
|
|
||||||
private var headerHashBlock: ByteArray? = null
|
private var headerHashBlock: ByteArray? = null
|
||||||
|
|
||||||
|
private var mGroupList = mutableListOf<GroupKDB>()
|
||||||
|
private var mEntryList = mutableListOf<EntryKDB>()
|
||||||
|
|
||||||
@Throws(DatabaseOutputException::class)
|
@Throws(DatabaseOutputException::class)
|
||||||
fun getFinalKey(header: DatabaseHeader): ByteArray? {
|
fun getFinalKey(header: DatabaseHeader): ByteArray? {
|
||||||
try {
|
try {
|
||||||
@@ -61,7 +65,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
|||||||
// and remove any orphaned nodes that are no longer part of the tree hierarchy
|
// and remove any orphaned nodes that are no longer part of the tree hierarchy
|
||||||
// also remove the virtual root not present in kdb
|
// also remove the virtual root not present in kdb
|
||||||
val rootGroup = mDatabaseKDB.rootGroup
|
val rootGroup = mDatabaseKDB.rootGroup
|
||||||
sortGroupsForOutput()
|
sortNodesForOutput()
|
||||||
|
|
||||||
val header = outputHeader(mOutputStream)
|
val header = outputHeader(mOutputStream)
|
||||||
|
|
||||||
@@ -91,6 +95,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
|||||||
} finally {
|
} finally {
|
||||||
// Add again the virtual root group for better management
|
// Add again the virtual root group for better management
|
||||||
mDatabaseKDB.rootGroup = rootGroup
|
mDatabaseKDB.rootGroup = rootGroup
|
||||||
|
clearParser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +110,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
|||||||
override fun outputHeader(outputStream: OutputStream): DatabaseHeaderKDB {
|
override fun outputHeader(outputStream: OutputStream): DatabaseHeaderKDB {
|
||||||
// Build header
|
// Build header
|
||||||
val header = DatabaseHeaderKDB()
|
val header = DatabaseHeaderKDB()
|
||||||
header.signature1 = DatabaseHeader.PWM_DBSIG_1
|
header.signature1 = DatabaseHeaderKDB.DBSIG_1
|
||||||
header.signature2 = DatabaseHeaderKDB.DBSIG_2
|
header.signature2 = DatabaseHeaderKDB.DBSIG_2
|
||||||
header.flags = DatabaseHeaderKDB.FLAG_SHA2
|
header.flags = DatabaseHeaderKDB.FLAG_SHA2
|
||||||
|
|
||||||
@@ -120,8 +125,9 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
|||||||
}
|
}
|
||||||
|
|
||||||
header.version = DatabaseHeaderKDB.DBVER_DW
|
header.version = DatabaseHeaderKDB.DBVER_DW
|
||||||
header.numGroups = UnsignedInt(mDatabaseKDB.numberOfGroups())
|
// To remove root
|
||||||
header.numEntries = UnsignedInt(mDatabaseKDB.numberOfEntries())
|
header.numGroups = UnsignedInt(mGroupList.size)
|
||||||
|
header.numEntries = UnsignedInt(mEntryList.size)
|
||||||
header.numKeyEncRounds = UnsignedInt.fromKotlinLong(mDatabaseKDB.numberKeyEncryptionRounds)
|
header.numKeyEncRounds = UnsignedInt.fromKotlinLong(mDatabaseKDB.numberKeyEncryptionRounds)
|
||||||
|
|
||||||
setIVs(header)
|
setIVs(header)
|
||||||
@@ -194,31 +200,89 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Groups
|
// Groups
|
||||||
mDatabaseKDB.doForEachGroupInIndex { group ->
|
mGroupList.forEach { group ->
|
||||||
GroupOutputKDB(group, outputStream).output()
|
if (group != mDatabaseKDB.rootGroup) {
|
||||||
|
GroupOutputKDB(group, outputStream).output()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Entries
|
// Entries
|
||||||
mDatabaseKDB.doForEachEntryInIndex { entry ->
|
mEntryList.forEach { entry ->
|
||||||
EntryOutputKDB(mDatabaseKDB, entry, outputStream).output()
|
EntryOutputKDB(mDatabaseKDB, entry, outputStream).output()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortGroupsForOutput() {
|
private fun clearParser() {
|
||||||
val groupList = ArrayList<GroupKDB>()
|
mGroupList.clear()
|
||||||
// Rebuild list according to sorting order removing any orphaned groups
|
mEntryList.clear()
|
||||||
for (rootGroup in mDatabaseKDB.rootGroups) {
|
|
||||||
sortGroup(rootGroup, groupList)
|
|
||||||
}
|
|
||||||
mDatabaseKDB.setGroupIndexes(groupList)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortGroup(group: GroupKDB, groupList: MutableList<GroupKDB>) {
|
private fun sortNodesForOutput() {
|
||||||
|
clearParser()
|
||||||
|
// Rebuild list according to sorting order removing any orphaned groups
|
||||||
|
// Do not keep root
|
||||||
|
mDatabaseKDB.rootGroup?.getChildGroups()?.let { rootSubGroups ->
|
||||||
|
for (rootGroup in rootSubGroups) {
|
||||||
|
sortGroup(rootGroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sortGroup(group: GroupKDB) {
|
||||||
// Add current tree
|
// Add current tree
|
||||||
groupList.add(group)
|
mGroupList.add(group)
|
||||||
|
|
||||||
|
for (childEntry in group.getChildEntries()) {
|
||||||
|
if (!childEntry.isMetaStreamDefaultUsername()
|
||||||
|
&& !childEntry.isMetaStreamDatabaseColor()) {
|
||||||
|
mEntryList.add(childEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add MetaStream
|
||||||
|
if (mDatabaseKDB.defaultUserName.isNotEmpty()) {
|
||||||
|
val metaEntry = EntryKDB().apply {
|
||||||
|
setMetaStreamDefaultUsername()
|
||||||
|
setDefaultUsername(this)
|
||||||
|
}
|
||||||
|
mDatabaseKDB.addEntryTo(metaEntry, group)
|
||||||
|
mEntryList.add(metaEntry)
|
||||||
|
}
|
||||||
|
if (mDatabaseKDB.color != null) {
|
||||||
|
val metaEntry = EntryKDB().apply {
|
||||||
|
setMetaStreamDatabaseColor()
|
||||||
|
setDatabaseColor(this)
|
||||||
|
}
|
||||||
|
mDatabaseKDB.addEntryTo(metaEntry, group)
|
||||||
|
mEntryList.add(metaEntry)
|
||||||
|
}
|
||||||
|
|
||||||
// Recurse over children
|
// Recurse over children
|
||||||
for (childGroup in group.getChildGroups()) {
|
for (childGroup in group.getChildGroups()) {
|
||||||
sortGroup(childGroup, groupList)
|
sortGroup(childGroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDefaultUsername(entryKDB: EntryKDB) {
|
||||||
|
val binaryData = mDatabaseKDB.buildNewAttachment()
|
||||||
|
entryKDB.putBinary(binaryData, mDatabaseKDB.attachmentPool)
|
||||||
|
BufferedOutputStream(binaryData.getOutputDataStream(mDatabaseKDB.binaryCache)).use { outputStream ->
|
||||||
|
outputStream.write(mDatabaseKDB.defaultUserName.toByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDatabaseColor(entryKDB: EntryKDB) {
|
||||||
|
val binaryData = mDatabaseKDB.buildNewAttachment()
|
||||||
|
entryKDB.putBinary(binaryData, mDatabaseKDB.attachmentPool)
|
||||||
|
BufferedOutputStream(binaryData.getOutputDataStream(mDatabaseKDB.binaryCache)).use { outputStream ->
|
||||||
|
var reversColor = Color.BLACK
|
||||||
|
mDatabaseKDB.color?.let {
|
||||||
|
reversColor = Color.rgb(
|
||||||
|
Color.blue(it),
|
||||||
|
Color.green(it),
|
||||||
|
Color.red(it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
outputStream.write4BytesUInt(UnsignedInt(reversColor))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ import com.kunzisoft.keepass.database.file.DateKDBXUtil
|
|||||||
import com.kunzisoft.keepass.stream.HashedBlockOutputStream
|
import com.kunzisoft.keepass.stream.HashedBlockOutputStream
|
||||||
import com.kunzisoft.keepass.stream.HmacBlockOutputStream
|
import com.kunzisoft.keepass.stream.HmacBlockOutputStream
|
||||||
import com.kunzisoft.keepass.utils.*
|
import com.kunzisoft.keepass.utils.*
|
||||||
import org.joda.time.DateTime
|
|
||||||
import org.xmlpull.v1.XmlSerializer
|
import org.xmlpull.v1.XmlSerializer
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ class PasswordActivityEducation(activity: Activity)
|
|||||||
activity.getString(R.string.education_unlock_summary))
|
activity.getString(R.string.education_unlock_summary))
|
||||||
.outerCircleColorInt(getCircleColor())
|
.outerCircleColorInt(getCircleColor())
|
||||||
.outerCircleAlpha(getCircleAlpha())
|
.outerCircleAlpha(getCircleAlpha())
|
||||||
.icon(ContextCompat.getDrawable(activity, R.mipmap.ic_launcher_round))
|
.icon(ContextCompat.getDrawable(activity, R.drawable.ic_lock_open_white_24dp))
|
||||||
.textColorInt(getTextColor())
|
.textColorInt(getTextColor())
|
||||||
.tintTarget(false)
|
.tintTarget(true)
|
||||||
.cancelable(true),
|
.cancelable(true),
|
||||||
object : TapTargetView.Listener() {
|
object : TapTargetView.Listener() {
|
||||||
override fun onTargetClick(view: TapTargetView) {
|
override fun onTargetClick(view: TapTargetView) {
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class EntryInfo : NodeInfo {
|
|||||||
var password: String = ""
|
var password: String = ""
|
||||||
var url: String = ""
|
var url: String = ""
|
||||||
var notes: String = ""
|
var notes: String = ""
|
||||||
|
var backgroundColor: Int? = null
|
||||||
|
var foregroundColor: Int? = null
|
||||||
var customFields: MutableList<Field> = mutableListOf()
|
var customFields: MutableList<Field> = mutableListOf()
|
||||||
var attachments: MutableList<Attachment> = mutableListOf()
|
var attachments: MutableList<Attachment> = mutableListOf()
|
||||||
var otpModel: OtpModel? = null
|
var otpModel: OtpModel? = null
|
||||||
@@ -53,6 +55,10 @@ class EntryInfo : NodeInfo {
|
|||||||
password = parcel.readString() ?: password
|
password = parcel.readString() ?: password
|
||||||
url = parcel.readString() ?: url
|
url = parcel.readString() ?: url
|
||||||
notes = parcel.readString() ?: notes
|
notes = parcel.readString() ?: notes
|
||||||
|
val readBgColor = parcel.readInt()
|
||||||
|
backgroundColor = if (readBgColor == -1) null else readBgColor
|
||||||
|
val readFgColor = parcel.readInt()
|
||||||
|
foregroundColor = if (readFgColor == -1) null else readFgColor
|
||||||
parcel.readList(customFields, Field::class.java.classLoader)
|
parcel.readList(customFields, Field::class.java.classLoader)
|
||||||
parcel.readList(attachments, Attachment::class.java.classLoader)
|
parcel.readList(attachments, Attachment::class.java.classLoader)
|
||||||
otpModel = parcel.readParcelable(OtpModel::class.java.classLoader) ?: otpModel
|
otpModel = parcel.readParcelable(OtpModel::class.java.classLoader) ?: otpModel
|
||||||
@@ -70,6 +76,8 @@ class EntryInfo : NodeInfo {
|
|||||||
parcel.writeString(password)
|
parcel.writeString(password)
|
||||||
parcel.writeString(url)
|
parcel.writeString(url)
|
||||||
parcel.writeString(notes)
|
parcel.writeString(notes)
|
||||||
|
parcel.writeInt(backgroundColor ?: -1)
|
||||||
|
parcel.writeInt(foregroundColor ?: -1)
|
||||||
parcel.writeList(customFields)
|
parcel.writeList(customFields)
|
||||||
parcel.writeList(attachments)
|
parcel.writeList(attachments)
|
||||||
parcel.writeParcelable(otpModel, flags)
|
parcel.writeParcelable(otpModel, flags)
|
||||||
@@ -196,6 +204,8 @@ class EntryInfo : NodeInfo {
|
|||||||
if (password != other.password) return false
|
if (password != other.password) return false
|
||||||
if (url != other.url) return false
|
if (url != other.url) return false
|
||||||
if (notes != other.notes) return false
|
if (notes != other.notes) return false
|
||||||
|
if (backgroundColor != other.backgroundColor) return false
|
||||||
|
if (foregroundColor != other.foregroundColor) return false
|
||||||
if (customFields != other.customFields) return false
|
if (customFields != other.customFields) return false
|
||||||
if (attachments != other.attachments) return false
|
if (attachments != other.attachments) return false
|
||||||
if (otpModel != other.otpModel) return false
|
if (otpModel != other.otpModel) return false
|
||||||
@@ -211,6 +221,8 @@ class EntryInfo : NodeInfo {
|
|||||||
result = 31 * result + password.hashCode()
|
result = 31 * result + password.hashCode()
|
||||||
result = 31 * result + url.hashCode()
|
result = 31 * result + url.hashCode()
|
||||||
result = 31 * result + notes.hashCode()
|
result = 31 * result + notes.hashCode()
|
||||||
|
result = 31 * result + backgroundColor.hashCode()
|
||||||
|
result = 31 * result + foregroundColor.hashCode()
|
||||||
result = 31 * result + customFields.hashCode()
|
result = 31 * result + customFields.hashCode()
|
||||||
result = 31 * result + attachments.hashCode()
|
result = 31 * result + attachments.hashCode()
|
||||||
result = 31 * result + (otpModel?.hashCode() ?: 0)
|
result = 31 * result + (otpModel?.hashCode() ?: 0)
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package com.kunzisoft.keepass.model
|
package com.kunzisoft.keepass.model
|
||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
|
import android.os.ParcelUuid
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.FOLDER_ID
|
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.FOLDER_ID
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class GroupInfo : NodeInfo {
|
class GroupInfo : NodeInfo {
|
||||||
|
|
||||||
|
var id: UUID? = null
|
||||||
var notes: String? = null
|
var notes: String? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -16,11 +19,14 @@ class GroupInfo : NodeInfo {
|
|||||||
constructor(): super()
|
constructor(): super()
|
||||||
|
|
||||||
constructor(parcel: Parcel): super(parcel) {
|
constructor(parcel: Parcel): super(parcel) {
|
||||||
|
id = parcel.readParcelable<ParcelUuid>(ParcelUuid::class.java.classLoader)?.uuid ?: id
|
||||||
notes = parcel.readString()
|
notes = parcel.readString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
super.writeToParcel(parcel, flags)
|
super.writeToParcel(parcel, flags)
|
||||||
|
val uuid = if (id != null) ParcelUuid(id) else null
|
||||||
|
parcel.writeParcelable(uuid, flags)
|
||||||
parcel.writeString(notes)
|
parcel.writeString(notes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +35,7 @@ class GroupInfo : NodeInfo {
|
|||||||
if (other !is GroupInfo) return false
|
if (other !is GroupInfo) return false
|
||||||
if (!super.equals(other)) return false
|
if (!super.equals(other)) return false
|
||||||
|
|
||||||
|
if (id != other.id) return false
|
||||||
if (notes != other.notes) return false
|
if (notes != other.notes) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -36,6 +43,7 @@ class GroupInfo : NodeInfo {
|
|||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = super.hashCode()
|
var result = super.hashCode()
|
||||||
|
result = 31 * result + (id?.hashCode() ?: 0)
|
||||||
result = 31 * result + (notes?.hashCode() ?: 0)
|
result = 31 * result + (notes?.hashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import android.os.Parcel
|
|||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||||
|
import com.kunzisoft.keepass.utils.UuidUtil
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
open class NodeInfo() : Parcelable {
|
open class NodeInfo() : Parcelable {
|
||||||
|
|
||||||
|
|||||||
@@ -452,7 +452,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
getString(R.string.show_uuid_key),
|
getString(R.string.show_uuid_key),
|
||||||
getString(R.string.enable_education_screens_key),
|
getString(R.string.enable_education_screens_key),
|
||||||
getString(R.string.reset_education_screens_key) -> {
|
getString(R.string.reset_education_screens_key) -> {
|
||||||
DATABASE_APPEARANCE_PREFERENCE_CHANGED = true
|
DATABASE_PREFERENCE_CHANGED = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.onPreferenceTreeClick(preference)
|
return super.onPreferenceTreeClick(preference)
|
||||||
@@ -516,6 +516,6 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
companion object {
|
companion object {
|
||||||
private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
|
private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
|
||||||
|
|
||||||
var DATABASE_APPEARANCE_PREFERENCE_CHANGED = false
|
var DATABASE_PREFERENCE_CHANGED = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,8 +30,8 @@ import androidx.preference.PreferenceCategory
|
|||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
import com.kunzisoft.androidclearchroma.ChromaUtil
|
import com.kunzisoft.androidclearchroma.ChromaUtil
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
|
||||||
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
||||||
|
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
||||||
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
|
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
|
||||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||||
@@ -165,28 +165,19 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
|
|
||||||
// Database default username
|
// Database default username
|
||||||
dbDefaultUsernamePref = findPreference(getString(R.string.database_default_username_key))
|
dbDefaultUsernamePref = findPreference(getString(R.string.database_default_username_key))
|
||||||
if (database.allowDefaultUsername) {
|
dbDefaultUsernamePref?.summary = database.defaultUsername
|
||||||
dbDefaultUsernamePref?.summary = database.defaultUsername
|
|
||||||
} else {
|
|
||||||
dbDefaultUsernamePref?.isEnabled = false
|
|
||||||
// TODO dbGeneralPrefCategory?.removePreference(dbDefaultUsername)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Database custom color
|
// Database custom color
|
||||||
dbCustomColorPref = findPreference(getString(R.string.database_custom_color_key))
|
dbCustomColorPref = findPreference(getString(R.string.database_custom_color_key))
|
||||||
if (database.allowCustomColor) {
|
dbCustomColorPref?.apply {
|
||||||
dbCustomColorPref?.apply {
|
val customColor = database.customColor
|
||||||
try {
|
if (customColor != null) {
|
||||||
color = Color.parseColor(database.customColor)
|
color = customColor
|
||||||
summary = database.customColor
|
summary = ChromaUtil.getFormattedColorString(customColor, false)
|
||||||
} catch (e: Exception) {
|
} else{
|
||||||
color = DialogColorPreference.DISABLE_COLOR
|
color = DialogColorPreference.DISABLE_COLOR
|
||||||
summary = ""
|
summary = ""
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
dbCustomColorPref?.isEnabled = false
|
|
||||||
// TODO dbGeneralPrefCategory?.removePreference(dbCustomColorPref)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
@@ -348,12 +339,13 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val colorSelectedListener: ((Boolean, Int)-> Unit) = { enable, color ->
|
private val colorSelectedListener: ((Int?)-> Unit) = { color ->
|
||||||
dbCustomColorPref?.summary = ChromaUtil.getFormattedColorString(color, false)
|
if (color != null) {
|
||||||
if (enable) {
|
|
||||||
dbCustomColorPref?.color = color
|
dbCustomColorPref?.color = color
|
||||||
|
dbCustomColorPref?.summary = ChromaUtil.getFormattedColorString(color, false)
|
||||||
} else {
|
} else {
|
||||||
dbCustomColorPref?.color = DialogColorPreference.DISABLE_COLOR
|
dbCustomColorPref?.color = DialogColorPreference.DISABLE_COLOR
|
||||||
|
dbCustomColorPref?.summary = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,7 +418,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
newColor
|
newColor
|
||||||
} else {
|
} else {
|
||||||
mDatabase?.customColor = oldColor
|
mDatabase?.customColor = Color.parseColor(oldColor)
|
||||||
oldColor
|
oldColor
|
||||||
}
|
}
|
||||||
dbCustomColorPref?.summary = defaultColorToShow
|
dbCustomColorPref?.summary = defaultColorToShow
|
||||||
@@ -681,6 +673,27 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||||
|
// To reload group when database settings are modified
|
||||||
|
when (preference?.key) {
|
||||||
|
getString(R.string.database_name_key),
|
||||||
|
getString(R.string.database_description_key),
|
||||||
|
getString(R.string.database_default_username_key),
|
||||||
|
getString(R.string.database_custom_color_key),
|
||||||
|
getString(R.string.database_data_compression_key),
|
||||||
|
getString(R.string.database_data_remove_unlinked_attachments_key),
|
||||||
|
getString(R.string.recycle_bin_enable_key),
|
||||||
|
getString(R.string.recycle_bin_group_key),
|
||||||
|
getString(R.string.templates_group_enable_key),
|
||||||
|
getString(R.string.templates_group_uuid_key),
|
||||||
|
getString(R.string.max_history_items_key),
|
||||||
|
getString(R.string.max_history_size_key) -> {
|
||||||
|
NestedAppSettingsFragment.DATABASE_PREFERENCE_CHANGED = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onPreferenceTreeClick(preference)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
|
private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class DialogColorPreference @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getDialogLayoutResource(): Int {
|
override fun getDialogLayoutResource(): Int {
|
||||||
return R.layout.pref_dialog_input_color
|
return R.layout.fragment_color_picker
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -30,27 +30,57 @@ import android.view.Window
|
|||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import com.kunzisoft.androidclearchroma.ChromaUtil
|
|
||||||
import com.kunzisoft.androidclearchroma.IndicatorMode
|
import com.kunzisoft.androidclearchroma.IndicatorMode
|
||||||
import com.kunzisoft.androidclearchroma.colormode.ColorMode
|
import com.kunzisoft.androidclearchroma.colormode.ColorMode
|
||||||
import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment
|
import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment
|
||||||
import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment.*
|
import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment.*
|
||||||
|
import com.kunzisoft.androidclearchroma.view.ChromaColorView
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
import com.kunzisoft.keepass.activities.dialogs.ColorPickerDialogFragment
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
|
||||||
class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||||
|
|
||||||
private lateinit var rootView: View
|
private lateinit var rootView: View
|
||||||
private lateinit var enableSwitchView: CompoundButton
|
private lateinit var enableSwitchView: CompoundButton
|
||||||
private var chromaColorFragment: ChromaColorFragment? = null
|
private lateinit var chromaColorView: ChromaColorView
|
||||||
|
|
||||||
var onColorSelectedListener: ((enable: Boolean, color: Int) -> Unit)? = null
|
var onColorSelectedListener: ((color: Int?) -> Unit)? = null
|
||||||
|
|
||||||
|
private var mDefaultColor = Color.WHITE
|
||||||
|
private var mActivated = false
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val alertDialogBuilder = AlertDialog.Builder(requireActivity())
|
val alertDialogBuilder = AlertDialog.Builder(requireActivity())
|
||||||
|
|
||||||
rootView = requireActivity().layoutInflater.inflate(R.layout.pref_dialog_input_color, null)
|
rootView = requireActivity().layoutInflater.inflate(R.layout.fragment_color_picker, null)
|
||||||
enableSwitchView = rootView.findViewById(R.id.switch_element)
|
enableSwitchView = rootView.findViewById(R.id.switch_element)
|
||||||
|
chromaColorView = rootView.findViewById(R.id.chroma_color_view)
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
if (savedInstanceState.containsKey(ARG_INITIAL_COLOR)) {
|
||||||
|
mDefaultColor = savedInstanceState.getInt(ARG_INITIAL_COLOR)
|
||||||
|
}
|
||||||
|
if (savedInstanceState.containsKey(ARG_ACTIVATED)) {
|
||||||
|
mActivated = savedInstanceState.getBoolean(ARG_ACTIVATED)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arguments?.apply {
|
||||||
|
if (containsKey(ARG_INITIAL_COLOR)) {
|
||||||
|
mDefaultColor = getInt(ARG_INITIAL_COLOR)
|
||||||
|
}
|
||||||
|
if (containsKey(ARG_ACTIVATED)) {
|
||||||
|
mActivated = getBoolean(ARG_ACTIVATED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enableSwitchView.isChecked = mActivated
|
||||||
|
chromaColorView.currentColor = mDefaultColor
|
||||||
|
|
||||||
|
chromaColorView.setOnColorChangedListener {
|
||||||
|
if (!enableSwitchView.isChecked)
|
||||||
|
enableSwitchView.isChecked = true
|
||||||
|
}
|
||||||
|
|
||||||
alertDialogBuilder.setPositiveButton(android.R.string.ok) { _, _ ->
|
alertDialogBuilder.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
onDialogClosed(true)
|
onDialogClosed(true)
|
||||||
@@ -68,8 +98,6 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog
|
|||||||
// request a window without the title
|
// request a window without the title
|
||||||
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
|
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
|
||||||
|
|
||||||
dialog.setOnShowListener { measureLayout(it as Dialog) }
|
|
||||||
|
|
||||||
return dialog
|
return dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,73 +105,48 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog
|
|||||||
super.onDatabaseRetrieved(database)
|
super.onDatabaseRetrieved(database)
|
||||||
|
|
||||||
database?.let {
|
database?.let {
|
||||||
val initColor = try {
|
var initColor = it.customColor
|
||||||
|
if (initColor != null) {
|
||||||
enableSwitchView.isChecked = true
|
enableSwitchView.isChecked = true
|
||||||
Color.parseColor(it.customColor)
|
} else {
|
||||||
} catch (e: Exception) {
|
|
||||||
enableSwitchView.isChecked = false
|
enableSwitchView.isChecked = false
|
||||||
DEFAULT_COLOR
|
initColor = DEFAULT_COLOR
|
||||||
}
|
}
|
||||||
|
chromaColorView.currentColor = initColor
|
||||||
arguments?.putInt(ARG_INITIAL_COLOR, initColor)
|
arguments?.putInt(ARG_INITIAL_COLOR, initColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
val fragmentManager = childFragmentManager
|
|
||||||
chromaColorFragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_COLORS) as ChromaColorFragment?
|
|
||||||
|
|
||||||
if (chromaColorFragment == null) {
|
|
||||||
chromaColorFragment = newInstance(arguments)
|
|
||||||
fragmentManager.beginTransaction().apply {
|
|
||||||
add(com.kunzisoft.androidclearchroma.R.id.color_dialog_container, chromaColorFragment!!, TAG_FRAGMENT_COLORS)
|
|
||||||
commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDialogClosed(database: Database?, positiveResult: Boolean) {
|
override fun onDialogClosed(database: Database?, positiveResult: Boolean) {
|
||||||
super.onDialogClosed(database, positiveResult)
|
super.onDialogClosed(database, positiveResult)
|
||||||
if (positiveResult) {
|
if (positiveResult) {
|
||||||
val customColorEnable = enableSwitchView.isChecked
|
val newColor: Int? = if (enableSwitchView.isChecked)
|
||||||
chromaColorFragment?.currentColor?.let { currentColor ->
|
chromaColorView.currentColor
|
||||||
onColorSelectedListener?.invoke(customColorEnable, currentColor)
|
else
|
||||||
database?.let {
|
null
|
||||||
val newColor = if (customColorEnable) {
|
onColorSelectedListener?.invoke(newColor)
|
||||||
ChromaUtil.getFormattedColorString(currentColor, false)
|
database?.let {
|
||||||
} else {
|
val oldColor = database.customColor
|
||||||
""
|
database.customColor = newColor
|
||||||
}
|
saveColor(oldColor, newColor)
|
||||||
val oldColor = database.customColor
|
|
||||||
database.customColor = newColor
|
|
||||||
saveColor(oldColor, newColor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set new dimensions to dialog
|
|
||||||
* @param ad dialog
|
|
||||||
*/
|
|
||||||
private fun measureLayout(ad: Dialog) {
|
|
||||||
val typedValue = TypedValue()
|
|
||||||
resources.getValue(com.kunzisoft.androidclearchroma.R.dimen.chroma_dialog_height_multiplier, typedValue, true)
|
|
||||||
val heightMultiplier = typedValue.float
|
|
||||||
val height = (ad.context.resources.displayMetrics.heightPixels * heightMultiplier).toInt()
|
|
||||||
|
|
||||||
resources.getValue(com.kunzisoft.androidclearchroma.R.dimen.chroma_dialog_width_multiplier, typedValue, true)
|
|
||||||
val widthMultiplier = typedValue.float
|
|
||||||
val width = (ad.context.resources.displayMetrics.widthPixels * widthMultiplier).toInt()
|
|
||||||
|
|
||||||
ad.window?.setLayout(width, height)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
super.onCreateView(inflater, container, savedInstanceState)
|
super.onCreateView(inflater, container, savedInstanceState)
|
||||||
return rootView
|
return rootView
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
private const val TAG_FRAGMENT_COLORS = "TAG_FRAGMENT_COLORS"
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putInt(ARG_INITIAL_COLOR, chromaColorView.currentColor)
|
||||||
|
outState.putBoolean(ARG_ACTIVATED, mActivated)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val ARG_INITIAL_COLOR = "ARG_INITIAL_COLOR"
|
||||||
|
private const val ARG_ACTIVATED = "ARG_ACTIVATED"
|
||||||
@ColorInt
|
@ColorInt
|
||||||
const val DEFAULT_COLOR: Int = Color.WHITE
|
const val DEFAULT_COLOR: Int = Color.WHITE
|
||||||
|
|
||||||
@@ -151,9 +154,7 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog
|
|||||||
val fragment = DatabaseColorPreferenceDialogFragmentCompat()
|
val fragment = DatabaseColorPreferenceDialogFragmentCompat()
|
||||||
val bundle = Bundle(1)
|
val bundle = Bundle(1)
|
||||||
bundle.putString(ARG_KEY, key)
|
bundle.putString(ARG_KEY, key)
|
||||||
bundle.putInt(ARG_INITIAL_COLOR, Color.BLACK)
|
bundle.putInt(ARG_INITIAL_COLOR, DEFAULT_COLOR)
|
||||||
bundle.putInt(ARG_COLOR_MODE, ColorMode.RGB.ordinal)
|
|
||||||
bundle.putInt(ARG_INDICATOR_MODE, IndicatorMode.HEX.ordinal)
|
|
||||||
fragment.arguments = bundle
|
fragment.arguments = bundle
|
||||||
|
|
||||||
return fragment
|
return fragment
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import com.kunzisoft.androidclearchroma.ChromaUtil
|
||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
||||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||||
@@ -76,9 +77,17 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat
|
|||||||
// To inherit to save element in database
|
// To inherit to save element in database
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun saveColor(oldColor: String,
|
protected fun saveColor(oldColor: Int?,
|
||||||
newColor: String) {
|
newColor: Int?) {
|
||||||
mDatabaseViewModel.saveColor(oldColor, newColor, mDatabaseAutoSaveEnable)
|
val oldColorString = if (oldColor != null)
|
||||||
|
ChromaUtil.getFormattedColorString(oldColor, false)
|
||||||
|
else
|
||||||
|
""
|
||||||
|
val newColorString = if (newColor != null)
|
||||||
|
ChromaUtil.getFormattedColorString(newColor, false)
|
||||||
|
else
|
||||||
|
""
|
||||||
|
mDatabaseViewModel.saveColor(oldColorString, newColorString, mDatabaseAutoSaveEnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun saveCompression(oldCompression: CompressionAlgorithm,
|
protected fun saveCompression(oldCompression: CompressionAlgorithm,
|
||||||
|
|||||||
@@ -19,7 +19,13 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||||
|
|
||||||
|
import android.app.AlarmManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
|
||||||
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.NumberPicker
|
import android.widget.NumberPicker
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
@@ -62,6 +68,30 @@ class DurationDialogFragmentCompat : InputPreferenceDialogFragmentCompat() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
|
||||||
|
(context?.applicationContext?.getSystemService(Context.ALARM_SERVICE) as AlarmManager?)?.let { alarmManager ->
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||||
|
&& !alarmManager.canScheduleExactAlarms()) {
|
||||||
|
setExplanationText(R.string.warning_exact_alarm)
|
||||||
|
setExplanationButton(R.string.permission) {
|
||||||
|
// Open the exact alarm permission screen
|
||||||
|
try {
|
||||||
|
startActivity(Intent().apply {
|
||||||
|
action = ACTION_REQUEST_SCHEDULE_EXACT_ALARM
|
||||||
|
})
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to open exact alarm permission screen", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
explanationText = ""
|
||||||
|
setExplanationButton("") {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun durationToDaysHoursMinutesSeconds(duration: Long) {
|
private fun durationToDaysHoursMinutesSeconds(duration: Long) {
|
||||||
if (duration < 0) {
|
if (duration < 0) {
|
||||||
mEnabled = false
|
mEnabled = false
|
||||||
@@ -164,6 +194,7 @@ class DurationDialogFragmentCompat : InputPreferenceDialogFragmentCompat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val TAG = "DurationDialogFrgCmpt"
|
||||||
private const val ENABLE_KEY = "ENABLE_KEY"
|
private const val ENABLE_KEY = "ENABLE_KEY"
|
||||||
private const val DAYS_KEY = "DAYS_KEY"
|
private const val DAYS_KEY = "DAYS_KEY"
|
||||||
private const val HOURS_KEY = "HOURS_KEY"
|
private const val HOURS_KEY = "HOURS_KEY"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.Button
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
@@ -35,6 +36,7 @@ abstract class InputPreferenceDialogFragmentCompat : PreferenceDialogFragmentCom
|
|||||||
private var inputTextView: EditText? = null
|
private var inputTextView: EditText? = null
|
||||||
private var textUnitView: TextView? = null
|
private var textUnitView: TextView? = null
|
||||||
private var textExplanationView: TextView? = null
|
private var textExplanationView: TextView? = null
|
||||||
|
private var explanationButton: Button? = null
|
||||||
private var switchElementView: CompoundButton? = null
|
private var switchElementView: CompoundButton? = null
|
||||||
|
|
||||||
private var mOnInputTextEditorActionListener: TextView.OnEditorActionListener? = null
|
private var mOnInputTextEditorActionListener: TextView.OnEditorActionListener? = null
|
||||||
@@ -100,6 +102,27 @@ abstract class InputPreferenceDialogFragmentCompat : PreferenceDialogFragmentCom
|
|||||||
explanationText = getString(explanationTextId)
|
explanationText = getString(explanationTextId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val explanationButtonText: String?
|
||||||
|
get() = explanationButton?.text?.toString() ?: ""
|
||||||
|
|
||||||
|
fun setExplanationButton(explanationButtonText: String?, clickListener: View.OnClickListener) {
|
||||||
|
explanationButton?.apply {
|
||||||
|
if (explanationButtonText != null && explanationButtonText.isNotEmpty()) {
|
||||||
|
text = explanationButtonText
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
setOnClickListener(clickListener)
|
||||||
|
} else {
|
||||||
|
text = ""
|
||||||
|
visibility = View.GONE
|
||||||
|
setOnClickListener(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setExplanationButton(@StringRes explanationButtonTextId: Int, clickListener: View.OnClickListener) {
|
||||||
|
setExplanationButton(getString(explanationButtonTextId), clickListener)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBindDialogView(view: View) {
|
override fun onBindDialogView(view: View) {
|
||||||
super.onBindDialogView(view)
|
super.onBindDialogView(view)
|
||||||
|
|
||||||
@@ -128,6 +151,8 @@ abstract class InputPreferenceDialogFragmentCompat : PreferenceDialogFragmentCom
|
|||||||
textUnitView?.visibility = View.GONE
|
textUnitView?.visibility = View.GONE
|
||||||
textExplanationView = view.findViewById(R.id.explanation_text)
|
textExplanationView = view.findViewById(R.id.explanation_text)
|
||||||
textExplanationView?.visibility = View.GONE
|
textExplanationView?.visibility = View.GONE
|
||||||
|
explanationButton = view.findViewById(R.id.explanation_button)
|
||||||
|
explanationButton?.visibility = View.GONE
|
||||||
switchElementView = view.findViewById(R.id.switch_element)
|
switchElementView = view.findViewById(R.id.switch_element)
|
||||||
switchElementView?.visibility = View.GONE
|
switchElementView?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,9 +66,26 @@ object TimeoutHelper {
|
|||||||
val triggerTime = System.currentTimeMillis() + timeout
|
val triggerTime = System.currentTimeMillis() + timeout
|
||||||
Log.d(TAG, "TimeoutHelper start")
|
Log.d(TAG, "TimeoutHelper start")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
alarmManager.setExact(AlarmManager.RTC, triggerTime, getLockPendingIntent(context))
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||||
|
&& !alarmManager.canScheduleExactAlarms()) {
|
||||||
|
alarmManager.set(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
triggerTime,
|
||||||
|
getLockPendingIntent(context)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
alarmManager.setExact(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
triggerTime,
|
||||||
|
getLockPendingIntent(context)
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
alarmManager.set(AlarmManager.RTC, triggerTime, getLockPendingIntent(context))
|
alarmManager.set(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
triggerTime,
|
||||||
|
getLockPendingIntent(context)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,11 +72,29 @@ class LockReceiver(var lockAction: () -> Unit) : BroadcastReceiver() {
|
|||||||
)
|
)
|
||||||
// Launch the effective action after a small time
|
// Launch the effective action after a small time
|
||||||
val first: Long = System.currentTimeMillis() + context.getString(R.string.timeout_screen_off).toLong()
|
val first: Long = System.currentTimeMillis() + context.getString(R.string.timeout_screen_off).toLong()
|
||||||
val alarmManager = context.getSystemService(ALARM_SERVICE) as AlarmManager?
|
(context.getSystemService(ALARM_SERVICE) as AlarmManager?)?.let { alarmManager ->
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
alarmManager?.setExact(AlarmManager.RTC_WAKEUP, first, mLockPendingIntent)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||||
} else {
|
&& !alarmManager.canScheduleExactAlarms()) {
|
||||||
alarmManager?.set(AlarmManager.RTC_WAKEUP, first, mLockPendingIntent)
|
alarmManager.set(
|
||||||
|
AlarmManager.RTC_WAKEUP,
|
||||||
|
first,
|
||||||
|
mLockPendingIntent
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
alarmManager.setExact(
|
||||||
|
AlarmManager.RTC_WAKEUP,
|
||||||
|
first,
|
||||||
|
mLockPendingIntent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alarmManager.set(
|
||||||
|
AlarmManager.RTC_WAKEUP,
|
||||||
|
first,
|
||||||
|
mLockPendingIntent
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cancelLockPendingIntent(context)
|
cancelLockPendingIntent(context)
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ abstract class TemplateAbstractView<
|
|||||||
|
|
||||||
protected var headerContainerView: ViewGroup
|
protected var headerContainerView: ViewGroup
|
||||||
protected var entryIconView: ImageView
|
protected var entryIconView: ImageView
|
||||||
|
protected var backgroundColorView: View
|
||||||
|
protected var foregroundColorView: View
|
||||||
|
protected var backgroundColorButton: ImageView
|
||||||
|
protected var foregroundColorButton: ImageView
|
||||||
private var titleContainerView: ViewGroup
|
private var titleContainerView: ViewGroup
|
||||||
protected var templateContainerView: ViewGroup
|
protected var templateContainerView: ViewGroup
|
||||||
private var customFieldsContainerView: SectionView
|
private var customFieldsContainerView: SectionView
|
||||||
@@ -57,6 +61,10 @@ abstract class TemplateAbstractView<
|
|||||||
|
|
||||||
headerContainerView = findViewById(R.id.template_header_container)
|
headerContainerView = findViewById(R.id.template_header_container)
|
||||||
entryIconView = findViewById(R.id.template_icon_button)
|
entryIconView = findViewById(R.id.template_icon_button)
|
||||||
|
backgroundColorView = findViewById(R.id.template_background_color)
|
||||||
|
foregroundColorView = findViewById(R.id.template_foreground_color)
|
||||||
|
backgroundColorButton = findViewById(R.id.template_background_color_button)
|
||||||
|
foregroundColorButton = findViewById(R.id.template_foreground_color_button)
|
||||||
titleContainerView = findViewById(R.id.template_title_container)
|
titleContainerView = findViewById(R.id.template_title_container)
|
||||||
templateContainerView = findViewById(R.id.template_fields_container)
|
templateContainerView = findViewById(R.id.template_fields_container)
|
||||||
// To fix card view margin below Marshmallow
|
// To fix card view margin below Marshmallow
|
||||||
@@ -396,37 +404,61 @@ abstract class TemplateAbstractView<
|
|||||||
|
|
||||||
// Icon already populate
|
// Icon already populate
|
||||||
|
|
||||||
val titleView: TEntryFieldView? = findViewWithTag(FIELD_TITLE_TAG)
|
try {
|
||||||
titleView?.value?.let {
|
val titleView: TEntryFieldView? = findViewWithTag(FIELD_TITLE_TAG)
|
||||||
mEntryInfo?.title = it
|
titleView?.value?.let {
|
||||||
|
mEntryInfo?.title = it
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to populate title view", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
val userNameView: TEntryFieldView? = findViewWithTag(FIELD_USERNAME_TAG)
|
try {
|
||||||
userNameView?.value?.let {
|
val userNameView: TEntryFieldView? = findViewWithTag(FIELD_USERNAME_TAG)
|
||||||
mEntryInfo?.username = it
|
userNameView?.value?.let {
|
||||||
|
mEntryInfo?.username = it
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to populate username view", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
val passwordView: TEntryFieldView? = findViewWithTag(FIELD_PASSWORD_TAG)
|
try {
|
||||||
passwordView?.value?.let {
|
val passwordView: TEntryFieldView? = findViewWithTag(FIELD_PASSWORD_TAG)
|
||||||
mEntryInfo?.password = it
|
passwordView?.value?.let {
|
||||||
|
mEntryInfo?.password = it
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to populate password view", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
val urlView: TEntryFieldView? = findViewWithTag(FIELD_URL_TAG)
|
try {
|
||||||
urlView?.value?.let {
|
val urlView: TEntryFieldView? = findViewWithTag(FIELD_URL_TAG)
|
||||||
mEntryInfo?.url = it
|
urlView?.value?.let {
|
||||||
|
mEntryInfo?.url = it
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to populate url view", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
val expirationView: TDateTimeView? = findViewWithTag(FIELD_EXPIRES_TAG)
|
try {
|
||||||
expirationView?.activation?.let {
|
val expirationView: TDateTimeView? = findViewWithTag(FIELD_EXPIRES_TAG)
|
||||||
mEntryInfo?.expires = it
|
expirationView?.activation?.let {
|
||||||
}
|
mEntryInfo?.expires = it
|
||||||
expirationView?.dateTime?.let {
|
}
|
||||||
mEntryInfo?.expiryTime = it
|
expirationView?.dateTime?.let {
|
||||||
|
mEntryInfo?.expiryTime = it
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to populate expiration view", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
val notesView: TEntryFieldView? = findViewWithTag(FIELD_NOTES_TAG)
|
try {
|
||||||
notesView?.value?.let {
|
val notesView: TEntryFieldView? = findViewWithTag(FIELD_NOTES_TAG)
|
||||||
mEntryInfo?.notes = it
|
notesView?.value?.let {
|
||||||
|
mEntryInfo?.notes = it
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to populate notes view", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
retrieveCustomFieldsFromView(templateFieldNotEmpty, retrieveDefaultValues)
|
retrieveCustomFieldsFromView(templateFieldNotEmpty, retrieveDefaultValues)
|
||||||
|
|||||||
@@ -5,13 +5,17 @@ import android.os.Build
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
|
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||||
|
import androidx.core.graphics.BlendModeCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
import com.kunzisoft.keepass.database.element.Field
|
import com.kunzisoft.keepass.database.element.Field
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||||
import com.kunzisoft.keepass.database.element.template.*
|
import com.kunzisoft.keepass.database.element.template.TemplateAttribute
|
||||||
|
import com.kunzisoft.keepass.database.element.template.TemplateAttributeAction
|
||||||
|
import com.kunzisoft.keepass.database.element.template.TemplateField
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields
|
import com.kunzisoft.keepass.otp.OtpEntryFields
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
|
|
||||||
@@ -51,7 +55,53 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
|
|
||||||
fun setIcon(iconImage: IconImage) {
|
fun setIcon(iconImage: IconImage) {
|
||||||
mEntryInfo?.icon = iconImage
|
mEntryInfo?.icon = iconImage
|
||||||
populateIconMethod?.invoke(entryIconView, iconImage)
|
refreshIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnBackgroundColorClickListener(onClickListener: OnClickListener) {
|
||||||
|
backgroundColorButton.setOnClickListener(onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBackgroundColor(): Int? {
|
||||||
|
return mEntryInfo?.backgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBackgroundColor(color: Int?) {
|
||||||
|
applyBackgroundColor(color)
|
||||||
|
mEntryInfo?.backgroundColor = color
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyBackgroundColor(color: Int?) {
|
||||||
|
if (color != null) {
|
||||||
|
backgroundColorView.background.colorFilter = BlendModeColorFilterCompat
|
||||||
|
.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_ATOP)
|
||||||
|
backgroundColorView.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
backgroundColorView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnForegroundColorClickListener(onClickListener: OnClickListener) {
|
||||||
|
foregroundColorButton.setOnClickListener(onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getForegroundColor(): Int? {
|
||||||
|
return mEntryInfo?.foregroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setForegroundColor(color: Int?) {
|
||||||
|
applyForegroundColor(color)
|
||||||
|
mEntryInfo?.foregroundColor = color
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyForegroundColor(color: Int?) {
|
||||||
|
if (color != null) {
|
||||||
|
foregroundColorView.background.colorFilter = BlendModeColorFilterCompat
|
||||||
|
.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_ATOP)
|
||||||
|
foregroundColorView.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
foregroundColorView.visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun preProcessTemplate() {
|
override fun preProcessTemplate() {
|
||||||
@@ -196,6 +246,8 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
|
|
||||||
override fun populateViewsWithEntryInfo(showEmptyFields: Boolean): List<ViewField> {
|
override fun populateViewsWithEntryInfo(showEmptyFields: Boolean): List<ViewField> {
|
||||||
refreshIcon()
|
refreshIcon()
|
||||||
|
applyBackgroundColor(mEntryInfo?.backgroundColor)
|
||||||
|
applyForegroundColor(mEntryInfo?.foregroundColor)
|
||||||
return super.populateViewsWithEntryInfo(showEmptyFields)
|
return super.populateViewsWithEntryInfo(showEmptyFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
private val valueView = AppCompatTextView(context).apply {
|
private val valueView = AppCompatTextView(context).apply {
|
||||||
setTextAppearance(context,
|
setTextAppearance(context,
|
||||||
R.style.KeepassDXStyle_TextAppearance_TextEntryItem)
|
R.style.KeepassDXStyle_TextAppearance_TextNode)
|
||||||
layoutParams = LayoutParams(
|
layoutParams = LayoutParams(
|
||||||
LayoutParams.MATCH_PARENT,
|
LayoutParams.MATCH_PARENT,
|
||||||
LayoutParams.WRAP_CONTENT).also {
|
LayoutParams.WRAP_CONTENT).also {
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ class ToolbarAction @JvmOverloads constructor(context: Context,
|
|||||||
mActionModeCallback = null
|
mActionModeCallback = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invalidateMenu() {
|
override fun invalidateMenu() {
|
||||||
|
super.invalidateMenu()
|
||||||
open()
|
open()
|
||||||
mActionModeCallback?.onPrepareActionMode(actionMode, menu)
|
mActionModeCallback?.onPrepareActionMode(actionMode, menu)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ import android.animation.Animator
|
|||||||
import android.animation.AnimatorSet
|
import android.animation.AnimatorSet
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.*
|
||||||
import android.graphics.Paint
|
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.text.Selection
|
import android.text.Selection
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
@@ -37,6 +35,7 @@ import android.view.View
|
|||||||
import android.view.animation.AccelerateDecelerateInterpolator
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
@@ -44,6 +43,16 @@ import com.google.android.material.snackbar.Snackbar
|
|||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
|
import androidx.appcompat.view.menu.ActionMenuItemView
|
||||||
|
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.appcompat.widget.ActionMenuView
|
||||||
|
|
||||||
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace font by monospace, must be called after setText()
|
* Replace font by monospace, must be called after setText()
|
||||||
@@ -207,4 +216,50 @@ fun CoordinatorLayout.showActionErrorIfNeeded(result: ActionRunnable.Result) {
|
|||||||
Snackbar.make(this, message, Snackbar.LENGTH_LONG).asError().show()
|
Snackbar.make(this, message, Snackbar.LENGTH_LONG).asError().show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Toolbar.changeControlColor(color: Int) {
|
||||||
|
val colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||||
|
for (i in 0 until childCount) {
|
||||||
|
val view: View = getChildAt(i)
|
||||||
|
// Change the color of back button (or open drawer button).
|
||||||
|
if (view is ImageView) {
|
||||||
|
//Action Bar back button
|
||||||
|
view.drawable.colorFilter = colorFilter
|
||||||
|
}
|
||||||
|
if (view is ActionMenuView) {
|
||||||
|
view.post {
|
||||||
|
for (j in 0 until view.childCount) {
|
||||||
|
// Change the color of any ActionMenuViews - icons that
|
||||||
|
// are not back button, nor text, nor overflow menu icon.
|
||||||
|
val innerView: View = view.getChildAt(j)
|
||||||
|
if (innerView is ActionMenuItemView) {
|
||||||
|
innerView.compoundDrawables.forEach { drawable ->
|
||||||
|
//Important to set the color filter in separate thread,
|
||||||
|
//by adding it to the message queue
|
||||||
|
//Won't work otherwise.
|
||||||
|
drawable?.colorFilter = colorFilter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Change the color of title and subtitle.
|
||||||
|
setTitleTextColor(color)
|
||||||
|
setSubtitleTextColor(color)
|
||||||
|
// Change the color of the Overflow Menu icon.
|
||||||
|
var drawable: Drawable? = overflowIcon
|
||||||
|
if (drawable != null) {
|
||||||
|
drawable = DrawableCompat.wrap(drawable)
|
||||||
|
DrawableCompat.setTint(drawable.mutate(), color)
|
||||||
|
overflowIcon = drawable
|
||||||
|
}
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CollapsingToolbarLayout.changeTitleColor(color: Int) {
|
||||||
|
setCollapsedTitleTextColor(color)
|
||||||
|
setExpandedTitleColor(color)
|
||||||
|
invalidate()
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.kunzisoft.keepass.viewmodels
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class ColorPickerViewModel: ViewModel() {
|
||||||
|
|
||||||
|
val colorPicked : LiveData<Int?> get() = _colorPicked
|
||||||
|
private val _colorPicked = MutableLiveData<Int?>()
|
||||||
|
|
||||||
|
fun pickColor(color: Int?) {
|
||||||
|
_colorPicked.value = color
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,14 @@ abstract class NodeEditViewModel : ViewModel() {
|
|||||||
val onIconSelected : LiveData<IconImage> get() = _onIconSelected
|
val onIconSelected : LiveData<IconImage> get() = _onIconSelected
|
||||||
private val _onIconSelected = SingleLiveEvent<IconImage>()
|
private val _onIconSelected = SingleLiveEvent<IconImage>()
|
||||||
|
|
||||||
|
private var mColorRequest: ColorRequest = ColorRequest.BACKGROUND
|
||||||
|
val requestColorSelection : LiveData<Int?> get() = _requestColorSelection
|
||||||
|
private val _requestColorSelection = SingleLiveEvent<Int?>()
|
||||||
|
val onBackgroundColorSelected : LiveData<Int?> get() = _onBackgroundColorSelected
|
||||||
|
private val _onBackgroundColorSelected = SingleLiveEvent<Int?>()
|
||||||
|
val onForegroundColorSelected : LiveData<Int?> get() = _onForegroundColorSelected
|
||||||
|
private val _onForegroundColorSelected = SingleLiveEvent<Int?>()
|
||||||
|
|
||||||
val requestDateTimeSelection : LiveData<DateInstant> get() = _requestDateTimeSelection
|
val requestDateTimeSelection : LiveData<DateInstant> get() = _requestDateTimeSelection
|
||||||
private val _requestDateTimeSelection = SingleLiveEvent<DateInstant>()
|
private val _requestDateTimeSelection = SingleLiveEvent<DateInstant>()
|
||||||
val onDateSelected : LiveData<DataDate> get() = _onDateSelected
|
val onDateSelected : LiveData<DataDate> get() = _onDateSelected
|
||||||
@@ -29,6 +37,23 @@ abstract class NodeEditViewModel : ViewModel() {
|
|||||||
_onIconSelected.value = iconImage
|
_onIconSelected.value = iconImage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun requestBackgroundColorSelection(initialColor: Int?) {
|
||||||
|
mColorRequest = ColorRequest.BACKGROUND
|
||||||
|
_requestColorSelection.value = initialColor
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestForegroundColorSelection(initialColor: Int?) {
|
||||||
|
mColorRequest = ColorRequest.FOREGROUND
|
||||||
|
_requestColorSelection.value = initialColor
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectColor(color: Int?) {
|
||||||
|
when (mColorRequest) {
|
||||||
|
ColorRequest.BACKGROUND -> _onBackgroundColorSelected.value = color
|
||||||
|
ColorRequest.FOREGROUND -> _onForegroundColorSelected.value = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun requestDateTimeSelection(dateInstant: DateInstant) {
|
fun requestDateTimeSelection(dateInstant: DateInstant) {
|
||||||
_requestDateTimeSelection.value = dateInstant
|
_requestDateTimeSelection.value = dateInstant
|
||||||
}
|
}
|
||||||
@@ -40,4 +65,8 @@ abstract class NodeEditViewModel : ViewModel() {
|
|||||||
fun selectTime(hours: Int, minutes: Int) {
|
fun selectTime(hours: Int, minutes: Int) {
|
||||||
_onTimeSelected.value = DataTime(hours, minutes)
|
_onTimeSelected.value = DataTime(hours, minutes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum class ColorRequest {
|
||||||
|
BACKGROUND, FOREGROUND
|
||||||
|
}
|
||||||
}
|
}
|
||||||
13
app/src/main/res/drawable/ic_color_background_white_24dp.xml
Normal file
13
app/src/main/res/drawable/ic_color_background_white_24dp.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group>
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:strokeWidth="0.99630105"
|
||||||
|
android:pathData="M 10,4 C 10,4 3.4004241,12.16731 4.0449219,16 4.4765841,18.566843 7.4158222,21 10,21 12.584196,21 15.523434,18.566843 15.955078,16 16.599576,12.16731 10,4 10,4 Z m 4,0 c 0,0 -0.59708,0.7527282 -1.386719,1.8476562 0.462543,0.6601114 0.953468,1.3798579 1.455078,2.1855469 0.803823,1.2911023 1.594188,2.7131219 2.15625,4.1015629 0.562116,1.388441 0.935799,2.740544 0.71875,4.03125 -0.268817,1.598597 -1.257809,3.011777 -2.517578,4.064453 -0.31833,0.265996 -0.657402,0.509805 -1.009765,0.726562 C 13.611687,20.98319 13.807101,21 14,21 16.584196,21 19.523434,18.566843 19.955078,16 20.599576,12.16731 14,4 14,4 Z m -4,2.5 c 0,0 0.497912,0.6283173 1.144531,1.53125 C 9.4508061,10.655343 7.697485,13.933867 8.0449219,16 8.262611,17.29447 9.126046,18.542028 10.253906,19.470703 10.169439,19.477241 10.083657,19.5 10,19.5 8.0238755,19.5 5.7754103,17.638661 5.4453125,15.675781 4.9524592,12.744903 10,6.5 10,6.5 Z" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/ic_color_foreground_white_24dp.xml
Normal file
13
app/src/main/res/drawable/ic_color_foreground_white_24dp.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group>
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:strokeWidth="0.99630105"
|
||||||
|
android:pathData="M 10,4 C 10,4 3.4004241,12.16731 4.0449219,16 4.4765841,18.566843 7.4158222,21 10,21 12.584196,21 15.523434,18.566843 15.955078,16 16.599576,12.16731 10,4 10,4 Z m 4,0 c 0,0 -0.597156,0.7527282 -1.386719,1.8476562 0.284669,0.4062608 0.575588,0.8441093 0.882813,1.3125 C 13.714676,6.8748215 14,6.5 14,6.5 c 0,0 5.04754,6.244903 4.554688,9.175781 -0.249068,1.481044 -1.591665,2.900275 -3.083985,3.509766 -0.31745,0.376905 -0.666337,0.728573 -1.044922,1.044922 -0.31833,0.265996 -0.657402,0.509805 -1.009765,0.726562 C 13.61168,20.98319 13.807101,21 14,21 16.584196,21 19.523434,18.566843 19.955078,16 20.599576,12.16731 14,4 14,4 Z" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar"
|
android:id="@+id/app_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/toolbar_parallax_height">
|
android:layout_height="@dimen/toolbar_parallax_height"
|
||||||
|
android:background="?attr/colorPrimary">
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
android:id="@+id/toolbar_layout"
|
android:id="@+id/toolbar_layout"
|
||||||
|
|||||||
@@ -31,11 +31,80 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:theme="?attr/toolbarSpecialAppearance" />
|
android:theme="?attr/toolbarSpecialAppearance" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:title="@string/app_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:layout_below="@+id/special_mode_view"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:theme="?attr/toolbarAppearance" >
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/database_name_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/database_color"
|
||||||
|
android:layout_width="12dp"
|
||||||
|
android:layout_height="12dp"
|
||||||
|
android:src="@drawable/background_rounded_square"
|
||||||
|
android:layout_marginRight="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:contentDescription="@string/content_description_database_color"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/database_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="Database"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Title.TextOnPrimary" />
|
||||||
|
</LinearLayout>
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/search_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/search_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/search_results"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Default.TextOnPrimary" />
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/search_numbers"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="3"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:layout_below="@+id/search_title"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Info" />
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/search_string"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/search"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginLeft="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginRight="4dp"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_below="@+id/search_title"
|
||||||
|
android:layout_toRightOf="@+id/search_numbers"
|
||||||
|
android:layout_toEndOf="@+id/search_numbers"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Title.TextOnPrimary" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/group_coordinator"
|
android:id="@+id/group_coordinator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_below="@+id/special_mode_view"
|
android:layout_below="@+id/toolbar"
|
||||||
android:layout_above="@+id/toolbar_action">
|
android:layout_above="@+id/toolbar_action">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
@@ -43,84 +112,22 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
tools:targetApi="lollipop"
|
tools:targetApi="lollipop"
|
||||||
android:elevation="4dp"
|
|
||||||
android:fitsSystemWindows="true">
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar_breadcrumb"
|
||||||
android:title="@string/app_name"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:background="?attr/colorPrimary"
|
android:background="?attr/colorPrimary"
|
||||||
android:theme="?attr/toolbarAppearance"
|
android:theme="?attr/toolbarAppearance"
|
||||||
android:elevation="4dp"
|
app:layout_scrollFlags="scroll|snap|enterAlways"
|
||||||
tools:targetApi="lollipop">
|
tools:targetApi="lollipop">
|
||||||
<LinearLayout
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/group_header"
|
android:id="@+id/breadcrumb_list"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
android:layout_alignParentStart="true"
|
android:orientation="horizontal" />
|
||||||
android:layout_below="@+id/toolbar"
|
|
||||||
android:orientation="vertical">
|
|
||||||
<TextView android:id="@+id/search_title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/search_results"
|
|
||||||
android:visibility="gone"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Default.TextOnPrimary" />
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:baselineAligned="false">
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/group_icon"
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:layout_gravity="end|center_vertical"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:scaleType="fitXY" />
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/group_numbers"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
tools:text="3"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Info" />
|
|
||||||
</RelativeLayout>
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_gravity="start|center_vertical"
|
|
||||||
android:layout_marginLeft="14dp"
|
|
||||||
android:layout_marginStart="14dp"
|
|
||||||
android:layout_weight="1">
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/group_name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/root"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:ellipsize="end"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Title.TextOnPrimary" />
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/group_meta"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C"
|
|
||||||
android:lines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Meta.TextOnPrimary" />
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
@@ -159,6 +166,7 @@
|
|||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/loading"
|
android:id="@+id/loading"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -44,7 +44,8 @@
|
|||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar"
|
android:id="@+id/app_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary">
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
android:id="@+id/toolbar_layout"
|
android:id="@+id/toolbar_layout"
|
||||||
@@ -57,7 +58,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:minHeight="144dp"
|
android:minHeight="144dp"
|
||||||
android:layout_marginTop="?attr/actionBarSize">
|
android:layout_marginTop="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary">
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="96dp"
|
android:layout_width="96dp"
|
||||||
android:layout_height="96dp"
|
android:layout_height="96dp"
|
||||||
@@ -219,6 +221,7 @@
|
|||||||
android:id="@+id/activity_password_info_container"
|
android:id="@+id/activity_password_info_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/activity_password_info_text"
|
android:id="@+id/activity_password_info_text"
|
||||||
|
|||||||
@@ -35,6 +35,6 @@
|
|||||||
android:layout_margin="@dimen/button_margin"
|
android:layout_margin="@dimen/button_margin"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_horizontal"
|
android:text="@string/file_browser"
|
||||||
android:text="@string/file_browser" />
|
style="@style/Widget.AppCompat.Button.Small"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2019 Jeremy Jamet / Kunzisoft.
|
Copyright 2022 Jeremy Jamet / Kunzisoft.
|
||||||
|
|
||||||
This file is part of KeePassDX.
|
This file is part of KeePassDX.
|
||||||
|
|
||||||
@@ -17,8 +17,7 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/edit"
|
android:id="@+id/edit"
|
||||||
@@ -27,10 +26,13 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:importantForAutofill="noExcludeDescendants"
|
android:importantForAutofill="noExcludeDescendants"
|
||||||
tools:targetApi="o">
|
tools:targetApi="o">
|
||||||
<FrameLayout
|
<com.kunzisoft.androidclearchroma.view.ChromaColorView
|
||||||
android:id="@+id/color_dialog_container"
|
android:id="@+id/chroma_color_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="300dp"
|
||||||
|
app:chromaInitialColor="@color/white"
|
||||||
|
app:chromaColorMode="RGB"
|
||||||
|
app:chromaIndicatorMode="HEX"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
@@ -41,7 +43,7 @@
|
|||||||
android:layout_margin="20dp"
|
android:layout_margin="20dp"
|
||||||
android:text="@string/enable"
|
android:text="@string/enable"
|
||||||
android:background="@drawable/background_button_small"
|
android:background="@drawable/background_button_small"
|
||||||
android:textColor="?attr/textColorInverse"
|
android:textColor="@color/white"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:minHeight="48dp"/>
|
android:minHeight="48dp"/>
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
android:id="@+id/entry_created"
|
android:id="@+id/entry_created"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem" />
|
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||||
|
|
||||||
<!-- Modified -->
|
<!-- Modified -->
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
android:id="@+id/entry_modified"
|
android:id="@+id/entry_modified"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem" />
|
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||||
|
|
||||||
<!-- Accessed -->
|
<!-- Accessed -->
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
@@ -127,16 +127,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/entry_UUID"
|
android:text="@string/entry_UUID"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||||
<HorizontalScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/entry_UUID"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
|
||||||
</HorizontalScrollView>
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@@ -145,7 +135,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textIsSelectable="true"
|
android:textIsSelectable="true"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem" />
|
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2019 Jeremy Jamet / Kunzisoft.
|
Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||||
|
|
||||||
This file is part of KeePassDX.
|
This file is part of KeePassDX.
|
||||||
|
|
||||||
KeePassDX is free software: you can redistribute it and/or modify
|
KeePassDX is free software: you can redistribute it and/or modify
|
||||||
@@ -17,38 +17,128 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<FrameLayout
|
<androidx.core.widget.NestedScrollView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="vertical"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/nodes_list"
|
<LinearLayout
|
||||||
android:contentDescription="@string/content_description_node_children"
|
android:orientation="vertical"
|
||||||
android:scrollbars="vertical"
|
android:layout_width="match_parent"
|
||||||
android:scrollbarStyle="insideOverlay"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:importantForAutofill="noExcludeDescendants">
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="?android:attr/windowBackground"
|
<FrameLayout
|
||||||
android:paddingBottom="?attr/actionBarSize"
|
android:layout_width="match_parent"
|
||||||
android:clipToPadding="false" />
|
android:layout_height="wrap_content"
|
||||||
<LinearLayout
|
android:background="?attr/colorPrimary">
|
||||||
android:id="@+id/not_found_container"
|
<LinearLayout
|
||||||
android:layout_gravity="center"
|
android:id="@+id/title_block"
|
||||||
android:orientation="vertical"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:orientation="vertical"
|
||||||
android:visibility="gone">
|
android:padding="@dimen/default_margin"
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
android:background="@drawable/background_repeat"
|
||||||
android:id="@+id/not_found_img"
|
android:gravity="center"
|
||||||
android:layout_width="wrap_content"
|
style="@style/KeepassDXStyle.TextAppearance.Default">
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
<!-- Icon -->
|
||||||
android:src="@drawable/img_not_found"/>
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
<TextView
|
android:id="@+id/group_icon"
|
||||||
android:id="@+id/not_found_text"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_gravity="center"
|
||||||
android:text="@string/no_results"/>
|
android:layout_margin="@dimen/default_margin"
|
||||||
</LinearLayout>
|
android:src="@drawable/ic_blank_32dp"
|
||||||
</FrameLayout>
|
style="@style/KeepassDXStyle.Icon"/>
|
||||||
|
|
||||||
|
<!-- Name -->
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
style="@style/KeepassDXStyle.Expanded.Title" />
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/default_margin"
|
||||||
|
tools:targetApi="o">
|
||||||
|
|
||||||
|
<!-- Note -->
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_note_label"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/entry_notes"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_note"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||||
|
|
||||||
|
<!-- Expiration -->
|
||||||
|
<com.kunzisoft.keepass.view.DateTimeFieldView
|
||||||
|
android:id="@+id/group_expiration"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<!-- Created -->
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_created_label"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/entry_created"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_created"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||||
|
|
||||||
|
<!-- Modified -->
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_modified_label"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/entry_modified"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_modified"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||||
|
|
||||||
|
<!-- UUID -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/group_UUID_container"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_UUID_label"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/entry_UUID"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_UUID_reference"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||||
|
</HorizontalScrollView>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|||||||
54
app/src/main/res/layout/fragment_nodes.xml
Normal file
54
app/src/main/res/layout/fragment_nodes.xml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||||
|
|
||||||
|
This file is part of KeePassDX.
|
||||||
|
|
||||||
|
KeePassDX is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
KeePassDX is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/nodes_list"
|
||||||
|
android:contentDescription="@string/content_description_node_children"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:scrollbarStyle="insideOverlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
android:paddingBottom="?attr/actionBarSize"
|
||||||
|
android:clipToPadding="false" />
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/not_found_container"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone">
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/not_found_img"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/img_not_found"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/not_found_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/no_results"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/item_attachment_title"
|
android:id="@+id/item_attachment_title"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
style="@style/KeepassDXStyle.TextAppearance.TextNode"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_toStartOf="@+id/item_attachment_size_container"
|
android:layout_toStartOf="@+id/item_attachment_size_container"
|
||||||
|
|||||||
66
app/src/main/res/layout/item_breadcrumb.xml
Normal file
66
app/src/main/res/layout/item_breadcrumb.xml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||||
|
|
||||||
|
This file is part of KeePassDX.
|
||||||
|
|
||||||
|
KeePassDX is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
KeePassDX is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/breadcrumb_group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:baselineAligned="false"
|
||||||
|
android:background="?android:attr/selectableItemBackground">
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/group_icon"
|
||||||
|
android:layout_width="26dp"
|
||||||
|
android:layout_height="26dp"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginLeft="6dp"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_marginRight="6dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:contentDescription="@string/hint_icon_name"
|
||||||
|
android:scaleType="fitXY" />
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start|center_vertical"
|
||||||
|
android:text="@string/root"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:ellipsize="end"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.SubTitle.TextOnPrimary"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"/>
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/group_separator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_arrow_right_white_24dp"
|
||||||
|
android:tint="?attr/textColorInverse"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_marginRight="6dp"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.SubTitle.TextOnPrimary"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:targetApi="jelly_bean" />
|
||||||
|
</LinearLayout>
|
||||||
76
app/src/main/res/layout/item_group.xml
Normal file
76
app/src/main/res/layout/item_group.xml
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||||
|
|
||||||
|
This file is part of KeePassDX.
|
||||||
|
|
||||||
|
KeePassDX is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
KeePassDX is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/breadcrumb_group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:baselineAligned="false"
|
||||||
|
android:background="?android:attr/selectableItemBackground">
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_marginRight="6dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/group_icon"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_gravity="end|center_vertical"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:scaleType="fitXY" />
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_numbers"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="3"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Info" />
|
||||||
|
</RelativeLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_gravity="start|center_vertical"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_marginRight="6dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/root"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:ellipsize="end"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Title.TextOnPrimary" />
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/group_meta"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C"
|
||||||
|
android:lines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Meta.TextOnPrimary" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
android:id="@+id/icon_container"
|
android:id="@+id/icon_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="80dp"
|
android:layout_height="80dp"
|
||||||
android:background="@drawable/background_item_selection">
|
style="@style/KeepassDXStyle.Selectable.Item">
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/icon_image"
|
android:id="@+id/icon_image"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/entry_history_last_modified"
|
android:id="@+id/entry_history_last_modified"
|
||||||
tools:text = "Last Modified"
|
tools:text = "Last Modified"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
style="@style/KeepassDXStyle.TextAppearance.TextNode"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/entry_history_title"
|
android:id="@+id/entry_history_title"
|
||||||
tools:text = "Title"
|
tools:text = "Title"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
style="@style/KeepassDXStyle.TextAppearance.TextNode"
|
||||||
android:maxLines="6"
|
android:maxLines="6"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/entry_history_username"
|
android:id="@+id/entry_history_username"
|
||||||
tools:text = "Username"
|
tools:text = "Username"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
style="@style/KeepassDXStyle.TextAppearance.TextNode"
|
||||||
android:maxLines="6"
|
android:maxLines="6"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
|||||||
@@ -27,52 +27,53 @@
|
|||||||
style="@style/KeepassDXStyle.Selectable.Item">
|
style="@style/KeepassDXStyle.Selectable.Item">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/constraintLayout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:minHeight="48dp"
|
android:minHeight="48dp"
|
||||||
app:layout_constraintWidth_percent="@dimen/content_percent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:background="?android:attr/selectableItemBackground" >
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintWidth_percent="@dimen/content_percent">
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/node_icon"
|
android:id="@+id/node_icon"
|
||||||
android:layout_width="32dp"
|
android:layout_width="32dp"
|
||||||
android:layout_height="32dp"
|
android:layout_height="32dp"
|
||||||
android:layout_marginTop="@dimen/image_list_margin_horizontal"
|
|
||||||
android:layout_marginBottom="@dimen/image_list_margin_horizontal"
|
|
||||||
android:layout_marginLeft="@dimen/image_list_margin_vertical"
|
|
||||||
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
|
||||||
android:layout_marginRight="@dimen/image_list_margin_vertical"
|
|
||||||
android:layout_marginEnd="@dimen/image_list_margin_vertical"
|
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
||||||
|
android:layout_marginLeft="@dimen/image_list_margin_vertical"
|
||||||
|
android:layout_marginTop="@dimen/image_list_margin_horizontal"
|
||||||
|
android:layout_marginEnd="@dimen/image_list_margin_vertical"
|
||||||
|
android:layout_marginRight="@dimen/image_list_margin_vertical"
|
||||||
|
android:layout_marginBottom="@dimen/image_list_margin_horizontal"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
android:src="@drawable/ic_blank_32dp"
|
android:src="@drawable/ic_blank_32dp"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/node_container_info"
|
android:id="@+id/node_container_info"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="4dp"
|
|
||||||
android:paddingBottom="4dp"
|
|
||||||
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
||||||
android:layout_marginLeft="@dimen/image_list_margin_vertical"
|
android:layout_marginLeft="@dimen/image_list_margin_vertical"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:layout_marginRight="12dp"
|
android:layout_marginRight="12dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/node_icon"
|
|
||||||
app:layout_constraintLeft_toRightOf="@+id/node_icon"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/node_options"
|
app:layout_constraintEnd_toStartOf="@+id/node_options"
|
||||||
app:layout_constraintRight_toLeftOf="@+id/node_options">
|
app:layout_constraintLeft_toRightOf="@+id/node_icon"
|
||||||
|
app:layout_constraintRight_toLeftOf="@+id/node_options"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/node_icon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/node_text"
|
android:id="@+id/node_text"
|
||||||
@@ -101,32 +102,41 @@
|
|||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C" />
|
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/node_path"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Entry.Meta"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="Database / Group A / Group B" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/node_options"
|
android:id="@+id/node_options"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
|
||||||
android:gravity="end"
|
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginLeft="12dp"
|
android:layout_marginLeft="12dp"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:gravity="end"
|
||||||
|
android:orientation="vertical"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/node_otp_container"
|
android:id="@+id/node_otp_container"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginRight="12dp"
|
android:layout_marginRight="8dp"
|
||||||
android:padding="4dp"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:orientation="horizontal"
|
||||||
|
android:padding="4dp"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/node_attachment_icon"
|
app:layout_constraintBottom_toTopOf="@+id/node_attachment_icon"
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/node_otp_token"
|
android:id="@+id/node_otp_token"
|
||||||
@@ -135,20 +145,22 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
tools:text="5136" />
|
tools:text="5136" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginLeft="4dp">
|
android:layout_marginLeft="4dp">
|
||||||
<ProgressBar
|
|
||||||
|
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
android:id="@+id/node_otp_progress"
|
android:id="@+id/node_otp_progress"
|
||||||
style="@style/KeepassDXStyle.ProgressBar.Circle.Secondary"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="18dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="18dp"
|
app:indicatorSize="16dp"
|
||||||
android:layout_gravity="center"
|
app:trackThickness="2dp"
|
||||||
android:max="100"
|
app:indicatorDirectionCircular="counterclockwise"
|
||||||
android:progress="60" />
|
android:layout_gravity="center" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -170,12 +182,12 @@
|
|||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:background="?android:attr/listDivider"
|
|
||||||
android:layout_marginLeft="72dp"
|
|
||||||
android:layout_marginStart="72dp"
|
android:layout_marginStart="72dp"
|
||||||
android:layout_marginRight="48dp"
|
android:layout_marginLeft="72dp"
|
||||||
android:layout_marginEnd="48dp"
|
android:layout_marginEnd="48dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
android:layout_marginRight="48dp"
|
||||||
|
android:background="?android:attr/listDivider"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -17,133 +17,155 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
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"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/edit"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="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_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.SmallTitle"/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
|
||||||
android:id="@+id/switch_element"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/enable"
|
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
android:layout_marginLeft="20dp"
|
|
||||||
android:layout_marginEnd="20dp"
|
|
||||||
android:layout_marginRight="20dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/explanation_text" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/edit"
|
||||||
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/switch_element">
|
android:importantForAutofill="noExcludeDescendants"
|
||||||
|
tools:targetApi="o">
|
||||||
<LinearLayout
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/duration_days_picker"
|
android:id="@+id/explanation_text"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:layout_width="0dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/duration_hours_picker"
|
|
||||||
app:layout_constraintRight_toLeftOf="@+id/duration_hours_picker">
|
|
||||||
<NumberPicker
|
|
||||||
android:id="@+id/days_picker"
|
|
||||||
android:scrollbarFadeDuration="0"
|
|
||||||
android:scrollbarDefaultDelayBeforeFade="0"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:src="@drawable/ic_day_white_24dp"
|
|
||||||
app:tint="?android:attr/textColor"
|
|
||||||
android:contentDescription="@string/digits" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/duration_hours_picker"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
tools:ignore="HardcodedText"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/duration_days_picker"
|
|
||||||
app:layout_constraintLeft_toRightOf="@+id/duration_days_picker"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/duration_time_picker"
|
|
||||||
app:layout_constraintRight_toLeftOf="@+id/duration_time_picker">
|
|
||||||
<NumberPicker
|
|
||||||
android:id="@+id/hours_picker"
|
|
||||||
android:scrollbarFadeDuration="0"
|
|
||||||
android:scrollbarDefaultDelayBeforeFade="0"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:text=":" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/duration_time_picker"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
tools:ignore="HardcodedText"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/duration_hours_picker"
|
|
||||||
app:layout_constraintLeft_toRightOf="@+id/duration_hours_picker"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent">
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
<NumberPicker
|
style="@style/KeepassDXStyle.TextAppearance.SmallTitle"/>
|
||||||
android:id="@+id/minutes_picker"
|
|
||||||
android:scrollbarFadeDuration="0"
|
|
||||||
android:scrollbarDefaultDelayBeforeFade="0"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:text="'"/>
|
|
||||||
<NumberPicker
|
|
||||||
android:id="@+id/seconds_picker"
|
|
||||||
android:scrollbarFadeDuration="0"
|
|
||||||
android:scrollbarDefaultDelayBeforeFade="0"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:text="''"/>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/explanation_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
tools:text="permission"
|
||||||
|
style="@style/Widget.AppCompat.Button.Small"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/explanation_text" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.SwitchCompat
|
||||||
|
android:id="@+id/switch_element"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/enable"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/explanation_button" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/switch_element">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/duration_days_picker"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/duration_hours_picker"
|
||||||
|
app:layout_constraintRight_toLeftOf="@+id/duration_hours_picker">
|
||||||
|
<NumberPicker
|
||||||
|
android:id="@+id/days_picker"
|
||||||
|
android:scrollbarFadeDuration="0"
|
||||||
|
android:scrollbarDefaultDelayBeforeFade="0"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@drawable/ic_day_white_24dp"
|
||||||
|
app:tint="?android:attr/textColor"
|
||||||
|
android:contentDescription="@string/digits" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/duration_hours_picker"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:ignore="HardcodedText"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/duration_days_picker"
|
||||||
|
app:layout_constraintLeft_toRightOf="@+id/duration_days_picker"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/duration_time_picker"
|
||||||
|
app:layout_constraintRight_toLeftOf="@+id/duration_time_picker">
|
||||||
|
<NumberPicker
|
||||||
|
android:id="@+id/hours_picker"
|
||||||
|
android:scrollbarFadeDuration="0"
|
||||||
|
android:scrollbarDefaultDelayBeforeFade="0"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text=":" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/duration_time_picker"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:ignore="HardcodedText"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/duration_hours_picker"
|
||||||
|
app:layout_constraintLeft_toRightOf="@+id/duration_hours_picker"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent">
|
||||||
|
<NumberPicker
|
||||||
|
android:id="@+id/minutes_picker"
|
||||||
|
android:scrollbarFadeDuration="0"
|
||||||
|
android:scrollbarDefaultDelayBeforeFade="0"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:text="'"/>
|
||||||
|
<NumberPicker
|
||||||
|
android:id="@+id/seconds_picker"
|
||||||
|
android:scrollbarFadeDuration="0"
|
||||||
|
android:scrollbarDefaultDelayBeforeFade="0"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:text="''"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</ScrollView>
|
||||||
@@ -26,6 +26,6 @@
|
|||||||
android:id="@+id/date_time_value"
|
android:id="@+id/date_time_value"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem" />
|
style="@style/KeepassDXStyle.TextAppearance.TextNode" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:cursorVisible="false"
|
android:cursorVisible="false"
|
||||||
android:focusableInTouchMode="false"
|
android:focusableInTouchMode="false"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
style="@style/KeepassDXStyle.TextAppearance.TextNode"
|
||||||
tools:text="2020-03-04 05:00" />
|
tools:text="2020-03-04 05:00" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
<androidx.appcompat.widget.SwitchCompat
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<LinearLayout
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
tools:targetApi="o"
|
tools:targetApi="o"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -29,30 +29,70 @@
|
|||||||
android:id="@+id/template_header_container"
|
android:id="@+id/template_header_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="?attr/cardViewStyle"
|
style="?attr/cardViewStyle">
|
||||||
android:visibility="gone">
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="@dimen/card_view_padding">
|
|
||||||
|
|
||||||
<!-- Icon -->
|
<FrameLayout
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
android:layout_width="match_parent"
|
||||||
android:id="@+id/template_icon_button"
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/template_background_color"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_margin="9dp"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:background="@drawable/background_rounded_square" />
|
||||||
|
<View
|
||||||
|
android:id="@+id/template_foreground_color"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_margin="32dp"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:background="@drawable/background_rounded_square" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/template_background_color_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="4dp"
|
android:layout_gravity="top|end"
|
||||||
android:src="@drawable/ic_blank_32dp"
|
android:layout_margin="3dp"
|
||||||
android:contentDescription="@string/content_description_entry_icon"
|
android:src="@drawable/ic_color_background_white_24dp"
|
||||||
android:layout_gravity="center"/>
|
android:contentDescription="@string/content_description_entry_background_color"
|
||||||
|
style="@style/KeepassDXStyle.ImageButton.Simple.Mini" />
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/template_foreground_color_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:layout_margin="25dp"
|
||||||
|
android:src="@drawable/ic_color_foreground_white_24dp"
|
||||||
|
android:contentDescription="@string/content_description_entry_background_color"
|
||||||
|
style="@style/KeepassDXStyle.ImageButton.Simple.Mini" />
|
||||||
|
|
||||||
<!-- Title -->
|
<LinearLayout
|
||||||
<FrameLayout
|
android:orientation="vertical"
|
||||||
android:id="@+id/template_title_container"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
</LinearLayout>
|
android:layout_gravity="center"
|
||||||
|
android:layout_margin="@dimen/card_view_padding">
|
||||||
|
|
||||||
|
<!-- Icon -->
|
||||||
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
android:id="@+id/template_icon_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:elevation="8dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:src="@drawable/ic_blank_32dp"
|
||||||
|
android:contentDescription="@string/content_description_entry_icon"/>
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/template_title_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
<string name="space">المساحة</string>
|
<string name="space">المساحة</string>
|
||||||
<string name="search_label">البحث</string>
|
<string name="search_label">البحث</string>
|
||||||
<string name="sort_menu">الفرز</string>
|
<string name="sort_menu">الفرز</string>
|
||||||
<string name="sort_ascending">تصاعدياً</string>
|
<string name="sort_ascending">تصاعدي ↓</string>
|
||||||
<string name="sort_title">العنوان</string>
|
<string name="sort_title">العنوان</string>
|
||||||
<string name="sort_username">اسم المستخدِم</string>
|
<string name="sort_username">اسم المستخدِم</string>
|
||||||
<string name="sort_creation_time">تاريخ الإنشاء</string>
|
<string name="sort_creation_time">تاريخ الإنشاء</string>
|
||||||
@@ -472,7 +472,7 @@
|
|||||||
<string name="error_import_app_properties">خطأ أثناء استيراد خصائص التطبيق</string>
|
<string name="error_import_app_properties">خطأ أثناء استيراد خصائص التطبيق</string>
|
||||||
<string name="error_export_app_properties">خطأ أثناء تصدير خصائص التطبيق</string>
|
<string name="error_export_app_properties">خطأ أثناء تصدير خصائص التطبيق</string>
|
||||||
<string name="warning_database_info_changed">غُيِّرت معلومات قاعدة البيانات من خارج هذا التطبيق.</string>
|
<string name="warning_database_info_changed">غُيِّرت معلومات قاعدة البيانات من خارج هذا التطبيق.</string>
|
||||||
<string name="warning_database_info_changed_options">اكتب فوق التعديلات الخارجية عن طريق حفظ قاعدة البيانات أو أعد تحميلها لتضمين هذه التغييرات.</string>
|
<string name="warning_database_info_changed_options">اكتب فوق التعديلات الخارجية عن طريق حفظ قاعدة البيانات أو أعد تحميلها لجلب آخر التغييرات.</string>
|
||||||
<string name="open_advanced_unlock_prompt_store_credential">افتح محث فك القفل المتقدم لتخزين بيانات الاعتماد</string>
|
<string name="open_advanced_unlock_prompt_store_credential">افتح محث فك القفل المتقدم لتخزين بيانات الاعتماد</string>
|
||||||
<string name="open_advanced_unlock_prompt_unlock_database">افتح محث فك القفل المتقدم لفتح قاعدة البيانات</string>
|
<string name="open_advanced_unlock_prompt_unlock_database">افتح محث فك القفل المتقدم لفتح قاعدة البيانات</string>
|
||||||
<string name="credential_before_click_advanced_unlock_button">اكتب كلمة السر، وأنقر هذا الزر.</string>
|
<string name="credential_before_click_advanced_unlock_button">اكتب كلمة السر، وأنقر هذا الزر.</string>
|
||||||
@@ -483,4 +483,10 @@
|
|||||||
<string name="keyboard_previous_fill_in_title">إجراء لمس تلقائي</string>
|
<string name="keyboard_previous_fill_in_title">إجراء لمس تلقائي</string>
|
||||||
<string name="keyboard_previous_lock_title">اقفل قاعدة البيانات</string>
|
<string name="keyboard_previous_lock_title">اقفل قاعدة البيانات</string>
|
||||||
<string name="education_advanced_unlock_title">فك القفل المتقدم لقاعدة البيانات</string>
|
<string name="education_advanced_unlock_title">فك القفل المتقدم لقاعدة البيانات</string>
|
||||||
|
<string name="hint_icon_name">اسم الأيقونة</string>
|
||||||
|
<string name="autofill_manual_selection_title">اختيار يدوي</string>
|
||||||
|
<string name="description_app_properties">خصائص KeePassDX لإدارة إعدادات التطبيقات</string>
|
||||||
|
<string name="warning_empty_recycle_bin">هل تريد حذف جميع العقد نهائيًا من سلة المهملات؟</string>
|
||||||
|
<string name="autofill_save_search_info_title">احفظ معلومات البحث</string>
|
||||||
|
<string name="autofill_ask_to_save_data_title">اسأل لحفظ البيانات</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -29,4 +29,16 @@
|
|||||||
<string name="discard">বাতিল</string>
|
<string name="discard">বাতিল</string>
|
||||||
<string name="validate">সত্যায়ন</string>
|
<string name="validate">সত্যায়ন</string>
|
||||||
<string name="content_description_background">পটভূমি</string>
|
<string name="content_description_background">পটভূমি</string>
|
||||||
|
<string name="entry_cancel">বাতিল</string>
|
||||||
|
<string name="entry_notes">দ্রষ্টব্য</string>
|
||||||
|
<string name="entry_created">তৈরিকৃত</string>
|
||||||
|
<string name="entry_expires">মেয়াদ</string>
|
||||||
|
<string name="entry_UUID">বিশেষ শনাক্তকরণ সংকেত(ইউইউআইডি)</string>
|
||||||
|
<string name="entry_history">ইতিহাস</string>
|
||||||
|
<string name="entry_attachments">সংযুক্তি</string>
|
||||||
|
<string name="entry_keyfile">চাবিনথি(কিফাইল)</string>
|
||||||
|
<string name="entry_modified">পরিবর্তিত</string>
|
||||||
|
<string name="clipboard_cleared">ক্লিপবোর্ড পরিষ্কার করা হয়েছে</string>
|
||||||
|
<string name="entry_accessed">শেষ ব্যবহার</string>
|
||||||
|
<string name="clipboard_error">কিছু ডিভাইস অ্যাপ্লিকেশনগুলোকে ক্লিপবোর্ড ব্যবহার করতে দেয় না।</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -602,4 +602,7 @@
|
|||||||
<string name="autofill_select_entry">Vyberte položku…</string>
|
<string name="autofill_select_entry">Vyberte položku…</string>
|
||||||
<string name="autofill_manual_selection_summary">Zobrazit možnosti umožňující uživateli si vybrat položku z databáze</string>
|
<string name="autofill_manual_selection_summary">Zobrazit možnosti umožňující uživateli si vybrat položku z databáze</string>
|
||||||
<string name="autofill_manual_selection_title">Ruční výběr</string>
|
<string name="autofill_manual_selection_title">Ruční výběr</string>
|
||||||
|
<string name="hint_icon_name">Jméno symbolu</string>
|
||||||
|
<string name="warning_exact_alarm">Nepovolili jste aplikaci použít přesný alarm. Výsledkem je, že funkce požadující časovač nebudou provedeny v přesný okamžik.</string>
|
||||||
|
<string name="permission">Povolení</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -449,7 +449,7 @@
|
|||||||
<string name="autofill_application_id_blocklist_summary">Blokeringsliste der forhindrer automatisk udfyldning af programmer</string>
|
<string name="autofill_application_id_blocklist_summary">Blokeringsliste der forhindrer automatisk udfyldning af programmer</string>
|
||||||
<string name="autofill_application_id_blocklist_title">Blokeringsliste for program</string>
|
<string name="autofill_application_id_blocklist_title">Blokeringsliste for program</string>
|
||||||
<string name="keyboard_previous_fill_in_summary">Skift automatisk tilbage til det forrige tastatur efter udførelse af \"Automatisk tastehandling\"</string>
|
<string name="keyboard_previous_fill_in_summary">Skift automatisk tilbage til det forrige tastatur efter udførelse af \"Automatisk tastehandling\"</string>
|
||||||
<string name="keyboard_previous_fill_in_title">Auto nøglehandling</string>
|
<string name="keyboard_previous_fill_in_title">Automatisk tastehandling</string>
|
||||||
<string name="keyboard_previous_database_credentials_summary">Skift automatisk tilbage til det forrige tastatur på databasens legitimationsskærm</string>
|
<string name="keyboard_previous_database_credentials_summary">Skift automatisk tilbage til det forrige tastatur på databasens legitimationsskærm</string>
|
||||||
<string name="keyboard_previous_database_credentials_title">Skærmbilledet til databaselegitimationsoplysninger</string>
|
<string name="keyboard_previous_database_credentials_title">Skærmbilledet til databaselegitimationsoplysninger</string>
|
||||||
<string name="keyboard_change">Skifte tastatur</string>
|
<string name="keyboard_change">Skifte tastatur</string>
|
||||||
|
|||||||
@@ -160,7 +160,7 @@
|
|||||||
<string name="clipboard_notifications_title">Zwischenablage-Benachrichtigung</string>
|
<string name="clipboard_notifications_title">Zwischenablage-Benachrichtigung</string>
|
||||||
<string name="clipboard_notifications_summary">Benachrichtigungen zur Zwischenablage anzeigen, um beim Betrachten eines Eintrags Felder kopieren zu können</string>
|
<string name="clipboard_notifications_summary">Benachrichtigungen zur Zwischenablage anzeigen, um beim Betrachten eines Eintrags Felder kopieren zu können</string>
|
||||||
<string name="lock_database_screen_off_title">Bildschirmsperre</string>
|
<string name="lock_database_screen_off_title">Bildschirmsperre</string>
|
||||||
<string name="lock_database_screen_off_summary">Datenbank sperren, einige Sekunden nachdem der Bildschirm ausgeschaltet wird</string>
|
<string name="lock_database_screen_off_summary">Datenbank einige Sekunden, nachdem der Bildschirm ausgeschaltet ist, sperren</string>
|
||||||
<string name="create_keepass_file">Neue Datenbank erstellen</string>
|
<string name="create_keepass_file">Neue Datenbank erstellen</string>
|
||||||
<string name="assign_master_key">Hauptschlüssel zuweisen</string>
|
<string name="assign_master_key">Hauptschlüssel zuweisen</string>
|
||||||
<string name="path">Pfad</string>
|
<string name="path">Pfad</string>
|
||||||
@@ -253,7 +253,7 @@
|
|||||||
<string name="education_sort_title">Sortierung der Einträge</string>
|
<string name="education_sort_title">Sortierung der Einträge</string>
|
||||||
<string name="education_sort_summary">Auswählen, wie Einträge und Gruppen sortiert werden.</string>
|
<string name="education_sort_summary">Auswählen, wie Einträge und Gruppen sortiert werden.</string>
|
||||||
<string name="education_donation_title">Mitmachen</string>
|
<string name="education_donation_title">Mitmachen</string>
|
||||||
<string name="education_donation_summary">Mithelfen, um Stabilität und Sicherheit zu verbessern und weitere Funktionen zu ermöglichen.</string>
|
<string name="education_donation_summary">Mithelfen, um Stabilität und Sicherheit zu verbessern sowie weitere Funktionen zu ermöglichen.</string>
|
||||||
<string name="html_text_ad_free">Anders als viele andere Passwortmanager ist dieser <strong>werbefrei</strong>, <strong>quelloffen</strong> und unter einer <strong>Copyleft-Lizenz</strong>. Es werden keine persönlichen Daten gesammelt, in welcher Form auch immer, unabhängig von der verwendeten Version (kostenlos oder Pro).</string>
|
<string name="html_text_ad_free">Anders als viele andere Passwortmanager ist dieser <strong>werbefrei</strong>, <strong>quelloffen</strong> und unter einer <strong>Copyleft-Lizenz</strong>. Es werden keine persönlichen Daten gesammelt, in welcher Form auch immer, unabhängig von der verwendeten Version (kostenlos oder Pro).</string>
|
||||||
<string name="html_text_buy_pro">Mit dem Kauf der Pro-Version erhalten Sie Zugriff auf diesen <strong>visuellen Stil</strong> und unterstützen insbesondere <strong>die Umsetzung gemeinschaftlicher Projekte.</strong></string>
|
<string name="html_text_buy_pro">Mit dem Kauf der Pro-Version erhalten Sie Zugriff auf diesen <strong>visuellen Stil</strong> und unterstützen insbesondere <strong>die Umsetzung gemeinschaftlicher Projekte.</strong></string>
|
||||||
<string name="html_text_feature_generosity">Dieser <strong>visuelle Stil</strong> wurde wegen Ihrer Großzügigkeit freigeschaltet.</string>
|
<string name="html_text_feature_generosity">Dieser <strong>visuelle Stil</strong> wurde wegen Ihrer Großzügigkeit freigeschaltet.</string>
|
||||||
@@ -343,7 +343,7 @@
|
|||||||
<string name="list_groups_show_number_entries_summary">Anzahl der Einträge in einer Gruppe anzeigen</string>
|
<string name="list_groups_show_number_entries_summary">Anzahl der Einträge in einer Gruppe anzeigen</string>
|
||||||
<string name="content_description_add_node">Knoten hinzufügen</string>
|
<string name="content_description_add_node">Knoten hinzufügen</string>
|
||||||
<string name="lock_database_back_root_title">„Zurück“ drücken, um zu sperren</string>
|
<string name="lock_database_back_root_title">„Zurück“ drücken, um zu sperren</string>
|
||||||
<string name="clear_clipboard_notification_summary">Die Datenbank sperren, wenn die Dauer der Zwischenablage abläuft oder die Benachrichtigung geschlossen wird, nachdem Sie sie zu benutzen begonnen haben</string>
|
<string name="clear_clipboard_notification_summary">Die Datenbank sperren, wenn die Dauer der Zwischenablage abläuft oder die Benachrichtigung geschlossen wird, nachdem Sie sie benutzt haben</string>
|
||||||
<string name="content_description_node_children">Untergeordneter Knotenpunkt</string>
|
<string name="content_description_node_children">Untergeordneter Knotenpunkt</string>
|
||||||
<string name="content_description_keyfile_checkbox">Schlüsseldatei-Kontrollkästchen</string>
|
<string name="content_description_keyfile_checkbox">Schlüsseldatei-Kontrollkästchen</string>
|
||||||
<string name="error_move_entry_here">Hierher kann kein Eintrag verschoben werden.</string>
|
<string name="error_move_entry_here">Hierher kann kein Eintrag verschoben werden.</string>
|
||||||
@@ -450,7 +450,7 @@
|
|||||||
<string name="autofill_auto_search_summary">Suchergebnisse automatisch nach Web-Domain oder Anwendungs-ID vorschlagen</string>
|
<string name="autofill_auto_search_summary">Suchergebnisse automatisch nach Web-Domain oder Anwendungs-ID vorschlagen</string>
|
||||||
<string name="autofill_auto_search_title">Automatische Suche</string>
|
<string name="autofill_auto_search_title">Automatische Suche</string>
|
||||||
<string name="autofill_manual_selection_title">Manuelle Auswahl</string>
|
<string name="autofill_manual_selection_title">Manuelle Auswahl</string>
|
||||||
<string name="autofill_manual_selection_summary">Manuelle Auswahl des Datenbank-Eintrags ermöglichen</string>
|
<string name="autofill_manual_selection_summary">Manuelle Auswahl des Datenbankeintrags ermöglichen</string>
|
||||||
<string name="lock_database_show_button_summary">Zeigt die Sperrtaste in der Benutzeroberfläche an</string>
|
<string name="lock_database_show_button_summary">Zeigt die Sperrtaste in der Benutzeroberfläche an</string>
|
||||||
<string name="lock_database_show_button_title">Sperrtaste anzeigen</string>
|
<string name="lock_database_show_button_title">Sperrtaste anzeigen</string>
|
||||||
<string name="autofill_preference_title">Einstellungen für automatisches Ausfüllen</string>
|
<string name="autofill_preference_title">Einstellungen für automatisches Ausfüllen</string>
|
||||||
@@ -488,13 +488,13 @@
|
|||||||
\nMit diesem Upload könnte Ihre Datenbank sehr groß werden und an Geschwindigkeit verlieren.</string>
|
\nMit diesem Upload könnte Ihre Datenbank sehr groß werden und an Geschwindigkeit verlieren.</string>
|
||||||
<string name="upload_attachment">%1$s hochladen</string>
|
<string name="upload_attachment">%1$s hochladen</string>
|
||||||
<string name="education_add_attachment_title">Anhang hinzufügen</string>
|
<string name="education_add_attachment_title">Anhang hinzufügen</string>
|
||||||
<string name="database_data_remove_unlinked_attachments_summary">Entfernt Anhänge die in der Datenbank enthalten, aber keinem Eintrag zugeordnet sind</string>
|
<string name="database_data_remove_unlinked_attachments_summary">Entfernt Anhänge, die in der Datenbank enthalten, aber keinem Eintrag zugeordnet sind</string>
|
||||||
<string name="warning_sure_add_file">Soll die Datei trotzdem hinzugefügt werden\?</string>
|
<string name="warning_sure_add_file">Soll die Datei trotzdem hinzugefügt werden\?</string>
|
||||||
<string name="show_uuid_summary">Zeigt die mit einem Eintrag oder einer Gruppe verknüpfte UUID an</string>
|
<string name="show_uuid_summary">Zeigt die mit einem Eintrag oder einer Gruppe verknüpfte UUID an</string>
|
||||||
<string name="show_uuid_title">UUID anzeigen</string>
|
<string name="show_uuid_title">UUID anzeigen</string>
|
||||||
<string name="autofill_read_only_save">Das Speichern von Daten ist für eine als schreibgeschützt geöffnete Datenbank nicht zulässig.</string>
|
<string name="autofill_read_only_save">Das Speichern von Daten ist für eine als schreibgeschützt geöffnete Datenbank nicht zulässig.</string>
|
||||||
<string name="autofill_close_database_title">Datenbank schließen</string>
|
<string name="autofill_close_database_title">Datenbank schließen</string>
|
||||||
<string name="keyboard_previous_lock_summary">Automatisches Zurückschalten zur vorherigen Tastatur nach dem Sperren der Datenbank</string>
|
<string name="keyboard_previous_lock_summary">Nach dem Sperren der Datenbank automatisch zur vorherigen Tastatur zurückschalten</string>
|
||||||
<string name="keyboard_previous_lock_title">Datenbank sperren</string>
|
<string name="keyboard_previous_lock_title">Datenbank sperren</string>
|
||||||
<string name="notification">Benachrichtigung</string>
|
<string name="notification">Benachrichtigung</string>
|
||||||
<string name="biometric_security_update_required">Biometrisches Sicherheitsupdate erforderlich.</string>
|
<string name="biometric_security_update_required">Biometrisches Sicherheitsupdate erforderlich.</string>
|
||||||
@@ -503,12 +503,12 @@
|
|||||||
<string name="save_mode">Speichermodus</string>
|
<string name="save_mode">Speichermodus</string>
|
||||||
<string name="search_mode">Suchmodus</string>
|
<string name="search_mode">Suchmodus</string>
|
||||||
<string name="error_registration_read_only">Das Speichern eines neuen Elements in einer schreibgeschützten Datenbank ist nicht zulässig</string>
|
<string name="error_registration_read_only">Das Speichern eines neuen Elements in einer schreibgeschützten Datenbank ist nicht zulässig</string>
|
||||||
<string name="autofill_save_search_info_summary">Suchdaten bei manueller Auswahl einer Eingabe wenn möglich speichern</string>
|
<string name="autofill_save_search_info_summary">Suchdaten bei manueller Auswahl einer Eingabe, wenn möglich, speichern</string>
|
||||||
<string name="autofill_ask_to_save_data_summary">Nachfragen, ob die Daten gespeichert werden sollen, wenn ein Formular ausgefüllt ist</string>
|
<string name="autofill_ask_to_save_data_summary">Nachfragen, ob die Daten gespeichert werden sollen, wenn ein Formular ausgefüllt ist</string>
|
||||||
<string name="autofill_ask_to_save_data_title">Speichern von Daten abfragen</string>
|
<string name="autofill_ask_to_save_data_title">Speichern von Daten abfragen</string>
|
||||||
<string name="autofill_save_search_info_title">Suchinformationen speichern</string>
|
<string name="autofill_save_search_info_title">Suchinformationen speichern</string>
|
||||||
<string name="autofill_close_database_summary">Datenbank nach Auswahl eines Eintrags zum automatischen Ausfüllen schließen</string>
|
<string name="autofill_close_database_summary">Datenbank nach Auswahl eines Eintrags zum automatischen Ausfüllen schließen</string>
|
||||||
<string name="keyboard_save_search_info_summary">Nachdem eine URL mit KeePassDX geteilt und ein Eintrag gewählt wurde, wird versucht sich diesen Eintrag für weitere Nutzungen zu merken</string>
|
<string name="keyboard_save_search_info_summary">Nachdem eine URL mit KeePassDX geteilt und ein Eintrag gewählt wurde, wird versucht, sich diesen Eintrag für die weitere Nutzung zu merken</string>
|
||||||
<string name="keyboard_save_search_info_title">Gemeinsam genutzte Informationen speichern</string>
|
<string name="keyboard_save_search_info_title">Gemeinsam genutzte Informationen speichern</string>
|
||||||
<string name="warning_empty_recycle_bin">Sollen alle ausgewählten Knoten wirklich aus dem Papierkorb gelöscht werden\?</string>
|
<string name="warning_empty_recycle_bin">Sollen alle ausgewählten Knoten wirklich aus dem Papierkorb gelöscht werden\?</string>
|
||||||
<string name="error_field_name_already_exists">Der Feldname existiert bereits.</string>
|
<string name="error_field_name_already_exists">Der Feldname existiert bereits.</string>
|
||||||
@@ -519,8 +519,8 @@
|
|||||||
<string name="advanced_unlock_prompt_store_credential_title">Moderne Entsperrerkennung</string>
|
<string name="advanced_unlock_prompt_store_credential_title">Moderne Entsperrerkennung</string>
|
||||||
<string name="education_advanced_unlock_summary">Verknüpfen Sie Ihr Passwort mit Ihren gescannten Biometriedaten oder Daten zur Geräteanmeldung, um schnell Ihre Datenbank zu entsperren.</string>
|
<string name="education_advanced_unlock_summary">Verknüpfen Sie Ihr Passwort mit Ihren gescannten Biometriedaten oder Daten zur Geräteanmeldung, um schnell Ihre Datenbank zu entsperren.</string>
|
||||||
<string name="education_advanced_unlock_title">Moderne Entsperrung der Datenbank</string>
|
<string name="education_advanced_unlock_title">Moderne Entsperrung der Datenbank</string>
|
||||||
<string name="advanced_unlock_timeout">Verfallzeit der modernen Entsperrung</string>
|
<string name="advanced_unlock_timeout">Verfallszeit der modernen Entsperrung</string>
|
||||||
<string name="temp_advanced_unlock_timeout_summary">Laufzeit der modernen Entsperrung bevor ihr Inhalt gelöscht wird</string>
|
<string name="temp_advanced_unlock_timeout_summary">Laufzeit der modernen Entsperrung, bevor ihr Inhalt gelöscht wird</string>
|
||||||
<string name="temp_advanced_unlock_timeout_title">Verfall der modernen Entsperrung</string>
|
<string name="temp_advanced_unlock_timeout_title">Verfall der modernen Entsperrung</string>
|
||||||
<string name="temp_advanced_unlock_enable_summary">Keine zur modernen Entsperrung verwendeten, verschlüsselten Inhalte speichern</string>
|
<string name="temp_advanced_unlock_enable_summary">Keine zur modernen Entsperrung verwendeten, verschlüsselten Inhalte speichern</string>
|
||||||
<string name="temp_advanced_unlock_enable_title">Befristete moderne Entsperrung</string>
|
<string name="temp_advanced_unlock_enable_title">Befristete moderne Entsperrung</string>
|
||||||
@@ -530,7 +530,7 @@
|
|||||||
<string name="advanced_unlock_prompt_extract_credential_title">Datenbank mit moderner Entsperrerkennung öffnen</string>
|
<string name="advanced_unlock_prompt_extract_credential_title">Datenbank mit moderner Entsperrerkennung öffnen</string>
|
||||||
<string name="enter">Eingabetaste</string>
|
<string name="enter">Eingabetaste</string>
|
||||||
<string name="backspace">Rücktaste</string>
|
<string name="backspace">Rücktaste</string>
|
||||||
<string name="select_entry">Wähle Eintrag</string>
|
<string name="select_entry">Eintrag auswählen</string>
|
||||||
<string name="back_to_previous_keyboard">Zurück zur vorherigen Tastatur</string>
|
<string name="back_to_previous_keyboard">Zurück zur vorherigen Tastatur</string>
|
||||||
<string name="custom_fields">Benutzerdefinierte Felder</string>
|
<string name="custom_fields">Benutzerdefinierte Felder</string>
|
||||||
<string name="advanced_unlock_delete_all_key_warning">Alle Verschlüsselungsschlüssel, die mit der modernen Entsperrerkennung zusammenhängen, löschen\?</string>
|
<string name="advanced_unlock_delete_all_key_warning">Alle Verschlüsselungsschlüssel, die mit der modernen Entsperrerkennung zusammenhängen, löschen\?</string>
|
||||||
@@ -576,7 +576,7 @@
|
|||||||
<string name="unit_byte">B</string>
|
<string name="unit_byte">B</string>
|
||||||
<string name="download_canceled">Abgebrochen!</string>
|
<string name="download_canceled">Abgebrochen!</string>
|
||||||
<string name="autofill_inline_suggestions_keyboard">Vorschläge für automatisches Ausfüllen hinzugefügt.</string>
|
<string name="autofill_inline_suggestions_keyboard">Vorschläge für automatisches Ausfüllen hinzugefügt.</string>
|
||||||
<string name="autofill_inline_suggestions_summary">Wenn möglich unmittelbar Vorschläge zum automatischen Ausfüllen auf einer kompatiblen Tastatur anzeigen</string>
|
<string name="autofill_inline_suggestions_summary">Wenn möglich, Vorschläge zum automatischen Ausfüllen direkt auf einer kompatiblen Tastatur anzeigen</string>
|
||||||
<string name="properties">Eigenschaften</string>
|
<string name="properties">Eigenschaften</string>
|
||||||
<string name="description_app_properties">KeePassDX-Eigenschaften zur Verwaltung der App-Einstellungen</string>
|
<string name="description_app_properties">KeePassDX-Eigenschaften zur Verwaltung der App-Einstellungen</string>
|
||||||
<string name="secure_note">Sicherer Hinweis</string>
|
<string name="secure_note">Sicherer Hinweis</string>
|
||||||
@@ -613,4 +613,7 @@
|
|||||||
<string name="menu_external_icon">Externes Symbol</string>
|
<string name="menu_external_icon">Externes Symbol</string>
|
||||||
<string name="show_otp_token_summary">Zeigt OTP-Tokens in der Liste der Einträge an</string>
|
<string name="show_otp_token_summary">Zeigt OTP-Tokens in der Liste der Einträge an</string>
|
||||||
<string name="show_otp_token_title">OTP-Token anzeigen</string>
|
<string name="show_otp_token_title">OTP-Token anzeigen</string>
|
||||||
|
<string name="hint_icon_name">Symbolname</string>
|
||||||
|
<string name="permission">Erlaubnis</string>
|
||||||
|
<string name="warning_exact_alarm">Sie haben der App nicht erlaubt, einen genauen Alarm zu verwenden. Deshalb werden die Funktionen, die einen Timer erfordern, nicht zu einer genauen Zeit ausgeführt.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -602,4 +602,7 @@
|
|||||||
<string name="autofill_manual_selection_summary">Εμφάνισης επιλογής για να επιτρέψει στον χρήστη να επιλέξει την καταχώρηση βάσης δεδομένων</string>
|
<string name="autofill_manual_selection_summary">Εμφάνισης επιλογής για να επιτρέψει στον χρήστη να επιλέξει την καταχώρηση βάσης δεδομένων</string>
|
||||||
<string name="autofill_manual_selection_title">Χειροκίνητη επιλογή</string>
|
<string name="autofill_manual_selection_title">Χειροκίνητη επιλογή</string>
|
||||||
<string name="autofill_select_entry">Επιλογή καταχώρησης…</string>
|
<string name="autofill_select_entry">Επιλογή καταχώρησης…</string>
|
||||||
|
<string name="hint_icon_name">Όνομα εικονιδίου</string>
|
||||||
|
<string name="warning_exact_alarm">Δεν έχετε επιτρέψει στην εφαρμογή να χρησιμοποιεί ακριβή συναγερμό. Ως αποτέλεσμα, οι λειτουργίες που απαιτούν χρονόμετρο δεν θα γίνονται με ακριβή χρόνο.</string>
|
||||||
|
<string name="permission">Άδεια</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -604,4 +604,7 @@
|
|||||||
<string name="autofill_manual_selection_summary">Mostrar opción para permitir al usuario seleccionar la entrada de la base de datos</string>
|
<string name="autofill_manual_selection_summary">Mostrar opción para permitir al usuario seleccionar la entrada de la base de datos</string>
|
||||||
<string name="autofill_manual_selection_title">Selección manual</string>
|
<string name="autofill_manual_selection_title">Selección manual</string>
|
||||||
<string name="autofill_select_entry">Seleccionar entrada…</string>
|
<string name="autofill_select_entry">Seleccionar entrada…</string>
|
||||||
|
<string name="hint_icon_name">Nombre del icono</string>
|
||||||
|
<string name="warning_exact_alarm">No ha permitido que la app use una alarma exacta. Como resultado, las funciones que requieren un temporizador no se harán con una hora exacta.</string>
|
||||||
|
<string name="permission">Permiso</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -569,9 +569,9 @@
|
|||||||
<string name="success_import_app_properties">Propriétés de l\'application importées</string>
|
<string name="success_import_app_properties">Propriétés de l\'application importées</string>
|
||||||
<string name="description_app_properties">Propriétés KeePassDX pour gérer les paramètres de l\'application</string>
|
<string name="description_app_properties">Propriétés KeePassDX pour gérer les paramètres de l\'application</string>
|
||||||
<string name="export_app_properties_summary">Créer un fichier pour exporter les propriétés de l\'application</string>
|
<string name="export_app_properties_summary">Créer un fichier pour exporter les propriétés de l\'application</string>
|
||||||
<string name="export_app_properties_title">Export des propriétés de l\'app</string>
|
<string name="export_app_properties_title">Exporter les propriétés de l\'application</string>
|
||||||
<string name="import_app_properties_summary">Sélectionner un fichier pour importer les propriétés de l\'application</string>
|
<string name="import_app_properties_summary">Sélectionner un fichier pour importer les propriétés de l\'application</string>
|
||||||
<string name="import_app_properties_title">Importation des propriétés de l\'appli</string>
|
<string name="import_app_properties_title">Importer les propriétés de l\'application</string>
|
||||||
<string name="error_start_database_action">Une erreur s\'est produite lors de l\'exécution d\'une action sur la base de données.</string>
|
<string name="error_start_database_action">Une erreur s\'est produite lors de l\'exécution d\'une action sur la base de données.</string>
|
||||||
<string name="error_move_group_here">Vous ne pouvez pas déplacer un groupe ici.</string>
|
<string name="error_move_group_here">Vous ne pouvez pas déplacer un groupe ici.</string>
|
||||||
<string name="error_word_reserved">Ce mot est réservé et ne peut pas être utilisé.</string>
|
<string name="error_word_reserved">Ce mot est réservé et ne peut pas être utilisé.</string>
|
||||||
@@ -609,4 +609,5 @@
|
|||||||
<string name="menu_external_icon">Icône externe</string>
|
<string name="menu_external_icon">Icône externe</string>
|
||||||
<string name="autofill_manual_selection_summary">Afficher l\'option permettant à l\'utilisateur de sélectionner l\'entrée de la base de données</string>
|
<string name="autofill_manual_selection_summary">Afficher l\'option permettant à l\'utilisateur de sélectionner l\'entrée de la base de données</string>
|
||||||
<string name="autofill_manual_selection_title">Sélection manuelle</string>
|
<string name="autofill_manual_selection_title">Sélection manuelle</string>
|
||||||
|
<string name="hint_icon_name">Nom de l\'icône</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -597,4 +597,5 @@
|
|||||||
<string name="autofill_select_entry">Odaberi unos …</string>
|
<string name="autofill_select_entry">Odaberi unos …</string>
|
||||||
<string name="menu_external_icon">Vanjska ikona</string>
|
<string name="menu_external_icon">Vanjska ikona</string>
|
||||||
<string name="seed">Tajna fraza</string>
|
<string name="seed">Tajna fraza</string>
|
||||||
|
<string name="hint_icon_name">Ime ikone</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -455,4 +455,37 @@
|
|||||||
<string name="public_key">Kunci publik</string>
|
<string name="public_key">Kunci publik</string>
|
||||||
<string name="private_key">Kunci pribadi</string>
|
<string name="private_key">Kunci pribadi</string>
|
||||||
<string name="membership">Keanggotaan</string>
|
<string name="membership">Keanggotaan</string>
|
||||||
|
<string name="hint_icon_name">Nama ikon</string>
|
||||||
|
<string name="card_verification_value">CVV</string>
|
||||||
|
<string name="personal_identification_number">PIN</string>
|
||||||
|
<string name="id_card">Kartu ID</string>
|
||||||
|
<string name="email">Surel</string>
|
||||||
|
<string name="email_address">Alamat surel</string>
|
||||||
|
<string name="ssid">SSID</string>
|
||||||
|
<string name="token">Token</string>
|
||||||
|
<string name="seed">Bibit</string>
|
||||||
|
<string name="bank_identifier_code">SWIFT/BIC</string>
|
||||||
|
<string name="secure_note">Catatan Aman</string>
|
||||||
|
<string name="menu_external_icon">Ikon eksternal</string>
|
||||||
|
<string name="holder">Penyangga</string>
|
||||||
|
<string name="autofill_select_entry">Pilih entri…</string>
|
||||||
|
<string name="biometric_auto_open_prompt_summary">Secara otomatis meminta buka kunci lanjutan jika basis data diatur menggunakan itu</string>
|
||||||
|
<string name="temp_advanced_unlock_enable_title">Buka kunci lanjutan sementara</string>
|
||||||
|
<string name="temp_advanced_unlock_enable_summary">Jangan simpan konten terenkripsi apa pun untuk menggunakan buka kunci lanjutan</string>
|
||||||
|
<string name="temp_advanced_unlock_timeout_summary">Durasi penggunaan buka kunci lanjutan sebelum menghapus konten tersebut</string>
|
||||||
|
<string name="advanced_unlock_timeout">Batas waktu membuka kunci lanjutan</string>
|
||||||
|
<string name="advanced_unlock_delete_all_key_warning">Hapus semua kunci enkripsi yang terkait pengenalan buka kunci lanjutan\?</string>
|
||||||
|
<string name="templates_group_enable_title">Penggunaan templat</string>
|
||||||
|
<string name="biometric_delete_all_key_summary">Hapus semua kunci enkripsi yang terkait pengenalan buka kunci lanjutan</string>
|
||||||
|
<string name="max_history_items_summary">Batasi jumlah item riwayat per entri</string>
|
||||||
|
<string name="template_group_name">Templat</string>
|
||||||
|
<string name="date_of_issue">Tanggal diterbitkan</string>
|
||||||
|
<string name="template">Templat</string>
|
||||||
|
<string name="place_of_issue">Tempat diterbitkan</string>
|
||||||
|
<string name="device_credential_unlock_enable_summary">Membiarkan Anda menggunakan kredensial perangkat Anda untuk membuka basis data</string>
|
||||||
|
<string name="unavailable_feature_text">Tidak bisa memulai fitur ini.</string>
|
||||||
|
<string name="templates_group_uuid_title">Grup templat</string>
|
||||||
|
<string name="max_history_size_summary">Batasi ukuran riwayat per entri</string>
|
||||||
|
<string name="max_history_size_title">Ukuran maksimum</string>
|
||||||
|
<string name="biometric_delete_all_key_title">Hapus kunci enkripsi</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -605,4 +605,5 @@
|
|||||||
<string name="autofill_manual_selection_summary">Mostra l\'opzione che permette all\'utente di scegliere la voce nel database</string>
|
<string name="autofill_manual_selection_summary">Mostra l\'opzione che permette all\'utente di scegliere la voce nel database</string>
|
||||||
<string name="autofill_manual_selection_title">Selezione manuale</string>
|
<string name="autofill_manual_selection_title">Selezione manuale</string>
|
||||||
<string name="autofill_select_entry">Seleziona voce…</string>
|
<string name="autofill_select_entry">Seleziona voce…</string>
|
||||||
|
<string name="hint_icon_name">Nome icona</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -600,4 +600,5 @@
|
|||||||
<string name="templates_group_uuid_title">テンプレート グループ</string>
|
<string name="templates_group_uuid_title">テンプレート グループ</string>
|
||||||
<string name="menu_external_icon">外部アイコン</string>
|
<string name="menu_external_icon">外部アイコン</string>
|
||||||
<string name="template">テンプレート</string>
|
<string name="template">テンプレート</string>
|
||||||
|
<string name="hint_icon_name">アイコン名</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -499,4 +499,105 @@
|
|||||||
<string name="content_description_otp_information">Éngangspassordsinfo</string>
|
<string name="content_description_otp_information">Éngangspassordsinfo</string>
|
||||||
<string name="template_group_name">Maler</string>
|
<string name="template_group_name">Maler</string>
|
||||||
<string name="content_description_credentials_information">Identitetdetaljsinfo</string>
|
<string name="content_description_credentials_information">Identitetdetaljsinfo</string>
|
||||||
|
<string name="templates_group_enable_summary">Bruk dynamiske maler for å fylle inn feltene for en oppføring</string>
|
||||||
|
<string name="templates">Maler</string>
|
||||||
|
<string name="error_word_reserved">Dette ordet er reservert og kan ikke brukes.</string>
|
||||||
|
<string name="enter">Enter-tast</string>
|
||||||
|
<string name="hint_icon_name">Ikonnavn</string>
|
||||||
|
<string name="menu_reload_database">Last inn databasen igjen</string>
|
||||||
|
<string name="menu_keystore_remove_key">Slett avansert opplåsningsnøkkel</string>
|
||||||
|
<string name="remember_database_locations_summary">Holder øye med hvor databaser lagres</string>
|
||||||
|
<string name="remember_keyfile_locations_title">Husk hvor lagringsfiler er lagret</string>
|
||||||
|
<string name="remember_keyfile_locations_summary">Holder øye med hvor nøkkelfiler er lagret</string>
|
||||||
|
<string name="show_recent_files_summary">Vis hvor nylige databaser er lagret</string>
|
||||||
|
<string name="description_app_properties">KeePassDX-egenskaper for håndtering av programinnstillinger</string>
|
||||||
|
<string name="success_import_app_properties">Programegenskaper importert</string>
|
||||||
|
<string name="filter">Filter</string>
|
||||||
|
<string name="warning_database_info_changed">Informasjonen som er å finne i databsefilen din har blitt endret utenfor programmet.</string>
|
||||||
|
<string name="warning_database_info_changed_options">Overskriv eksisterende endringer ved å lagre databasen, eller last den inn igjen med de siste endringene.</string>
|
||||||
|
<string name="warning_file_too_big">En KeePass-database er ment å kun inneholde små bruksfiler (som f-eks PGP-nøkkelfiler).
|
||||||
|
\n
|
||||||
|
\nDatabasen din kan bli veldig stor og få redusert ytelse med denne opplastingen.</string>
|
||||||
|
<string name="warning_empty_keyfile_explanation">Innholdet i nøkkelfilen bør aldri endre seg, og skal helst inneholde kun tilfeldig generert data.</string>
|
||||||
|
<string name="warning_database_revoked">Tilgang til filen ble tilbakekalt av filbehandleren. Lukk databasen og åpne den igjen fra dens posisjon.</string>
|
||||||
|
<string name="error_import_app_properties">Feil under importering av programegenskaper</string>
|
||||||
|
<string name="success_export_app_properties">Programegenskaper eksportert</string>
|
||||||
|
<string name="error_export_app_properties">Feil under eksportering av programegenskaper</string>
|
||||||
|
<string name="place_of_issue">Utsteder</string>
|
||||||
|
<string name="cryptocurrency">Kryptovaluta-lommebok</string>
|
||||||
|
<string name="seed">Utgangspunkt</string>
|
||||||
|
<string name="debit_credit_card">Bankkort</string>
|
||||||
|
<string name="secure_note">Sikkehetsnotis</string>
|
||||||
|
<string name="standard">Forvalg</string>
|
||||||
|
<string name="bank_identifier_code">SWIFT/BIC</string>
|
||||||
|
<string name="error_registration_read_only">Å lagre et nytt element tillates ikke i skrivebeskyttet database.</string>
|
||||||
|
<string name="error_database_uri_null">Database-URI kan ikke innhentes.</string>
|
||||||
|
<string name="error_rebuild_list">Kunne ikke gjenoppbygge listen på riktig måte.</string>
|
||||||
|
<string name="error_upload_file">En feil inntraff under opplasting av fildataen.</string>
|
||||||
|
<string name="error_remove_file">En feil inntraff under fjerning av fildataen.</string>
|
||||||
|
<string name="properties">Egenskaper</string>
|
||||||
|
<string name="advanced_unlock_prompt_extract_credential_title">Åpne databasen med avansert opplåsningsgjenkjennelse</string>
|
||||||
|
<string name="advanced_unlock_prompt_extract_credential_message">Pakk ut database-identitetsdetalj med avansert opplåsningsdata</string>
|
||||||
|
<string name="advanced_unlock_invalid_key">Kan ikke lese avansert opplåsningsnøkkel. Slett den og gjenta gjenkjenning av opplåsningsprosedyre.</string>
|
||||||
|
<string name="advanced_unlock_prompt_not_initialized">Kunne ikke igangsette avansert opplåsningsspørring.</string>
|
||||||
|
<string name="permission">Tilgang</string>
|
||||||
|
<string name="biometric_security_update_required">Biometrisk sikkerhetsoppdatering kreves.</string>
|
||||||
|
<string name="warning_exact_alarm">Du har ikke tillat programmet å bruke en eksakt alarm. Som resultat vil funksjoner som krever et tidsur ikke gjøres til eksakt tid.</string>
|
||||||
|
<string name="open_advanced_unlock_prompt_unlock_database">Åpne avansert opplåsningsspørring for å låse opp databasen</string>
|
||||||
|
<string name="open_advanced_unlock_prompt_store_credential">Åpne avansert opplåsningsspørring for å lagre identitetsdetaljer</string>
|
||||||
|
<string name="advanced_unlock_prompt_store_credential_message">Advarsel: Du vil fremdeles måtte huske hovedpassordet ditt hvis du bruker avansert opplåsningsgjenkjennelse.</string>
|
||||||
|
<string name="advanced_unlock_not_recognized">Kunne ikke gjenkjenne avansert opplåsningsskrift</string>
|
||||||
|
<string name="credential_before_click_advanced_unlock_button">Skriv inn passordet, og klikk deretter på denne knappen.</string>
|
||||||
|
<string name="device_credential">Enhets-identitetsdetalj</string>
|
||||||
|
<string name="autofill_select_entry">Velg oppføring …</string>
|
||||||
|
<string name="temp_advanced_unlock_enable_title">Midlertidig avansert opplåsing</string>
|
||||||
|
<string name="temp_advanced_unlock_timeout_title">Utløp av avansert opplåsing</string>
|
||||||
|
<string name="device_credential_unlock_enable_summary">Lar deg bruke din enhets-identitetsdetalj for å åpne databasen</string>
|
||||||
|
<string name="temp_advanced_unlock_timeout_summary">Varighet av avansert opplåsningsbruk for sletting av dens innhold</string>
|
||||||
|
<string name="advanced_unlock_explanation_summary">Bruk avansert databaseopplåsing for enklere åpning av databaser</string>
|
||||||
|
<string name="device_credential_unlock_enable_title">Opplåsning av enhetsidentitetsdetalj</string>
|
||||||
|
<string name="advanced_unlock_timeout">Tidsavbrudd for avansert opplåsing</string>
|
||||||
|
<string name="templates_group_enable_title">Mal-bruk</string>
|
||||||
|
<string name="database_data_remove_unlinked_attachments_summary">Fjerner vedlegg som er å finne i databasen, men som ikke er lenket til en oppføring</string>
|
||||||
|
<string name="keyboard_save_search_info_summary">Etter deling av en nettadresse til KeePassDX, vil oppføringer som velges bli forsøkt husket for fremtidig bruk</string>
|
||||||
|
<string name="keyboard_previous_database_credentials_title">Skjem for database-identitetsdetaljer</string>
|
||||||
|
<string name="keyboard_previous_database_credentials_summary">Bytt tilbake til forrige tastatur atuomatisk på skjermen for database-identitetsdetaljer</string>
|
||||||
|
<string name="autofill_inline_suggestions_title">Forslag på linjen</string>
|
||||||
|
<string name="autofill_inline_suggestions_summary">Prøv å vise autofullføringsforslag direkte fra et kompatibelt tastatur</string>
|
||||||
|
<string name="autofill_manual_selection_title">Manuellt utvalg</string>
|
||||||
|
<string name="autofill_manual_selection_summary">Vis alternativ for å la brukeren velge databaseoppføring</string>
|
||||||
|
<string name="keyboard_previous_fill_in_title">Automatisk tastehandling</string>
|
||||||
|
<string name="autofill_application_id_blocklist_title">Programsvarteliste</string>
|
||||||
|
<string name="autofill_application_id_blocklist_summary">Svartelisten som forhindrer automatisk innfylling i programmer</string>
|
||||||
|
<string name="autofill_web_domain_blocklist_title">Svarteliste for vev-domene</string>
|
||||||
|
<string name="autofill_web_domain_blocklist_summary">Svarteliste som forhindrer automatisk innfylling av vev-domener</string>
|
||||||
|
<string name="autofill_ask_to_save_data_summary">Spør om å lagre data når et skjema bekreftes</string>
|
||||||
|
<string name="education_add_attachment_summary">Last opp et vedlegg til din oppføring for å lagre viktig ekstern data.</string>
|
||||||
|
<string name="education_setup_OTP_summary">Sett opp engangspassordhåndtering (HOTP/TOTP) for å generere et symbol forespurt for to-faktoridentitetsbekreftelse (2FA).</string>
|
||||||
|
<string name="show_otp_token_title">Vis OTP-symbol</string>
|
||||||
|
<string name="keyboard_auto_go_action_summary">«Start»-nøkkelhandling etter at en «Felt»-nøkkel trykkes</string>
|
||||||
|
<string name="keyboard_auto_go_action_title">Automatisk nøkkelhandling</string>
|
||||||
|
<string name="keyboard_search_share_summary">Filtrer oppføringene ved bruk av nettadressedomenet ved deling av en nettadresse til KeePassDX</string>
|
||||||
|
<string name="settings_database_force_changing_master_key_next_time_summary">Krev endring av hovednøkkelen neste gang (én gang)</string>
|
||||||
|
<string name="settings_database_force_changing_master_key_next_time_title">Påtving fornyelse neste gang</string>
|
||||||
|
<string name="database_data_remove_unlinked_attachments_title">Fjen ulenket data</string>
|
||||||
|
<string name="biometric_auto_open_prompt_summary">Forespør opplasting automatisk hvis databasen er satt opp til å bruke det</string>
|
||||||
|
<string name="menu_external_icon">Eksternt ikon</string>
|
||||||
|
<string name="subdomain_search_title">Underdomenesøk</string>
|
||||||
|
<string name="error_otp_type">Eksisterende OTP-type gjenkjennes ikke av dette skjemaet. Bekreftelse av trenger ikke nødvendigvis å generere symbolet på riktig måte.</string>
|
||||||
|
<string name="error_start_database_action">En feil inntraff under utføring av en databasehandling.</string>
|
||||||
|
<string name="temp_advanced_unlock_enable_summary">Ikke lagre noe kryptert innhold for å bruke avansert opplåsing</string>
|
||||||
|
<string name="subdomain_search_summary">Søk i vev-domener med underdomene-begrensninger</string>
|
||||||
|
<string name="max_history_items_summary">Begrens antall historikkelementer per oppføring</string>
|
||||||
|
<string name="show_otp_token_summary">Viser OTP-symboler i listen over oppføringer</string>
|
||||||
|
<string name="warning_replace_file">Opplasting av denne filen erstatter den som finnes.</string>
|
||||||
|
<string name="max_history_size_summary">Begrens historikkstørrelse per oppføring</string>
|
||||||
|
<string name="import_app_properties_summary">Velg en fil for å importere programegenskaper</string>
|
||||||
|
<string name="export_app_properties_summary">Opprett en fil for å eksportere programegenskaper</string>
|
||||||
|
<string name="warning_remove_unlinked_attachment">Fjerning av ulenket data kan redusere databasestørrelsen din, men kan også slette data bruk av KeePass-programtillegg.</string>
|
||||||
|
<string name="settings_database_force_changing_master_key_summary">Krev endring av hovednøkkelen (dager)</string>
|
||||||
|
<string name="biometric_auto_open_prompt_title">Åpne spørring automatisk</string>
|
||||||
|
<string name="settings_database_recommend_changing_master_key_summary">Anbefal endring av hovednøkkel (dager)</string>
|
||||||
|
<string name="keystore_not_accessible">Nøkkellageret er ikke igangsatt på riktig vis.</string>
|
||||||
|
<string name="keyboard_previous_fill_in_summary">Bytt tilbake til forrige tastatur automatisk etter kjøring av «Automatisk tastehandling»</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -603,4 +603,5 @@
|
|||||||
<string name="number">Nummer</string>
|
<string name="number">Nummer</string>
|
||||||
<string name="debit_credit_card">Betaalkaart</string>
|
<string name="debit_credit_card">Betaalkaart</string>
|
||||||
<string name="template_group_name">Sjablonen</string>
|
<string name="template_group_name">Sjablonen</string>
|
||||||
|
<string name="hint_icon_name">Naam van het pictogram</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -162,8 +162,8 @@
|
|||||||
<string name="kdf_explanation">Aby wygenerować klucz dla algorytmu szyfrowania, klucz główny jest transformowany przy użyciu losowo solonej funkcji wyprowadzania klucza.</string>
|
<string name="kdf_explanation">Aby wygenerować klucz dla algorytmu szyfrowania, klucz główny jest transformowany przy użyciu losowo solonej funkcji wyprowadzania klucza.</string>
|
||||||
<string name="memory_usage">Użycie pamięci</string>
|
<string name="memory_usage">Użycie pamięci</string>
|
||||||
<string name="memory_usage_explanation">Ilość pamięci do użycia przez funkcję wyprowadzania klucza.</string>
|
<string name="memory_usage_explanation">Ilość pamięci do użycia przez funkcję wyprowadzania klucza.</string>
|
||||||
<string name="parallelism">Równoległy</string>
|
<string name="parallelism">Równoległość</string>
|
||||||
<string name="parallelism_explanation">Stopień równoległości (tj. Liczba wątków) wykorzystywany przez funkcję wyprowadzania klucza.</string>
|
<string name="parallelism_explanation">Stopień równoległości (tj. liczba wątków) wykorzystywany przez funkcję wyprowadzania klucza.</string>
|
||||||
<string name="sort_menu">Sortuj</string>
|
<string name="sort_menu">Sortuj</string>
|
||||||
<string name="sort_ascending">Od najniższej ↓</string>
|
<string name="sort_ascending">Od najniższej ↓</string>
|
||||||
<string name="sort_username">Nazwa Użytkownika</string>
|
<string name="sort_username">Nazwa Użytkownika</string>
|
||||||
@@ -602,4 +602,7 @@
|
|||||||
<string name="autofill_manual_selection_summary">Wyświetl opcję pozwalającą użytkownikowi wybrać wpis bazy danych</string>
|
<string name="autofill_manual_selection_summary">Wyświetl opcję pozwalającą użytkownikowi wybrać wpis bazy danych</string>
|
||||||
<string name="autofill_manual_selection_title">Wybór ręczny</string>
|
<string name="autofill_manual_selection_title">Wybór ręczny</string>
|
||||||
<string name="autofill_select_entry">Wybierz wpis…</string>
|
<string name="autofill_select_entry">Wybierz wpis…</string>
|
||||||
|
<string name="hint_icon_name">Nazwa ikony</string>
|
||||||
|
<string name="warning_exact_alarm">Nie zezwolono aplikacji na użycie dokładnego alarmu. W rezultacie funkcje wymagające czasomierza nie będą wykonywane z dokładnym czasem.</string>
|
||||||
|
<string name="permission">Uprawnienie</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -23,9 +23,9 @@
|
|||||||
<string name="accept">Aceitar</string>
|
<string name="accept">Aceitar</string>
|
||||||
<string name="add_entry">Adicionar entrada</string>
|
<string name="add_entry">Adicionar entrada</string>
|
||||||
<string name="add_group">Adicionar grupo</string>
|
<string name="add_group">Adicionar grupo</string>
|
||||||
<string name="encryption_algorithm">Algoritmo de cifragem</string>
|
<string name="encryption_algorithm">Algoritmo de criptografia</string>
|
||||||
<string name="app_timeout">Tempo de espera</string>
|
<string name="app_timeout">Tempo de espera</string>
|
||||||
<string name="app_timeout_summary">Inatividade até que o aplicativo seja bloqueado</string>
|
<string name="app_timeout_summary">Inatividade até que o banco de dados seja bloqueado</string>
|
||||||
<string name="application">Aplicativo</string>
|
<string name="application">Aplicativo</string>
|
||||||
<string name="menu_app_settings">Configurações do aplicativo</string>
|
<string name="menu_app_settings">Configurações do aplicativo</string>
|
||||||
<string name="brackets">Parênteses</string>
|
<string name="brackets">Parênteses</string>
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
<string name="password">Senha</string>
|
<string name="password">Senha</string>
|
||||||
<string name="hint_pass">Senha</string>
|
<string name="hint_pass">Senha</string>
|
||||||
<string name="invalid_credentials">Não foi possível ler credenciais.</string>
|
<string name="invalid_credentials">Não foi possível ler credenciais.</string>
|
||||||
<string name="invalid_db_sig">Não pôde reconhecer formato da base de dados.</string>
|
<string name="invalid_db_sig">Não foi possível reconhecer o formato do banco de dados.</string>
|
||||||
<string name="length">Comprimento</string>
|
<string name="length">Comprimento</string>
|
||||||
<string name="list_size_title">Tamanho da lista de grupos</string>
|
<string name="list_size_title">Tamanho da lista de grupos</string>
|
||||||
<string name="list_size_summary">Tamanho do texto na lista de grupos</string>
|
<string name="list_size_summary">Tamanho do texto na lista de grupos</string>
|
||||||
@@ -86,14 +86,14 @@
|
|||||||
<string name="hide_password_title">Esconder senhas</string>
|
<string name="hide_password_title">Esconder senhas</string>
|
||||||
<string name="hide_password_summary">Mascarar senhas (***) por padrão</string>
|
<string name="hide_password_summary">Mascarar senhas (***) por padrão</string>
|
||||||
<string name="about">Sobre</string>
|
<string name="about">Sobre</string>
|
||||||
<string name="menu_change_key_settings">Modificar a chave mestra</string>
|
<string name="menu_change_key_settings">Modificar a chave-mestra</string>
|
||||||
<string name="settings">Configurações</string>
|
<string name="settings">Configurações</string>
|
||||||
<string name="menu_database_settings">Configurações do banco de dados</string>
|
<string name="menu_database_settings">Configurações do banco de dados</string>
|
||||||
<string name="menu_delete">Deletar</string>
|
<string name="menu_delete">Deletar</string>
|
||||||
<string name="menu_donate">Doar</string>
|
<string name="menu_donate">Doar</string>
|
||||||
<string name="menu_edit">Editar</string>
|
<string name="menu_edit">Editar</string>
|
||||||
<string name="menu_hide_password">Esconder senha</string>
|
<string name="menu_hide_password">Esconder senha</string>
|
||||||
<string name="menu_lock">Bloquear base de dados</string>
|
<string name="menu_lock">Bloquear banco de dados</string>
|
||||||
<string name="menu_open">Abrir</string>
|
<string name="menu_open">Abrir</string>
|
||||||
<string name="menu_search">Buscar</string>
|
<string name="menu_search">Buscar</string>
|
||||||
<string name="menu_showpass">Mostrar senha</string>
|
<string name="menu_showpass">Mostrar senha</string>
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
<string name="content_description_remove_from_list">Remover</string>
|
<string name="content_description_remove_from_list">Remover</string>
|
||||||
<string name="root">Raiz</string>
|
<string name="root">Raiz</string>
|
||||||
<string name="rounds">Rodadas de transformação</string>
|
<string name="rounds">Rodadas de transformação</string>
|
||||||
<string name="rounds_explanation">Rodadas adicionais de cifragem adicionam mais proteção contra ataques de força bruta, mas podem tornar o processo de carregar e salvar mais lentos.</string>
|
<string name="rounds_explanation">Rodadas adicionais de criptografia adicionam mais proteção contra ataques de força bruta, mas podem tornar o processo de carregar e salvar mais lentos.</string>
|
||||||
<string name="saving_database">Salvando banco de dados…</string>
|
<string name="saving_database">Salvando banco de dados…</string>
|
||||||
<string name="space">Espaço</string>
|
<string name="space">Espaço</string>
|
||||||
<string name="search_label">Busca</string>
|
<string name="search_label">Busca</string>
|
||||||
@@ -122,13 +122,13 @@
|
|||||||
<string name="version_label">Versão %1$s</string>
|
<string name="version_label">Versão %1$s</string>
|
||||||
<string name="education_unlock_summary">Entre com a senha e/ou com o caminho para o arquivo-chave do banco de dados.
|
<string name="education_unlock_summary">Entre com a senha e/ou com o caminho para o arquivo-chave do banco de dados.
|
||||||
\n
|
\n
|
||||||
\nGuarde uma cópia do seu arquivo do banco em um lugar mais seguro depois de cada alteração.</string>
|
\nFaça um backup do banco em um lugar mais seguro depois de cada alteração.</string>
|
||||||
<string-array name="list_size_options">
|
<string-array name="list_size_options">
|
||||||
<item>Pequeno</item>
|
<item>Pequeno</item>
|
||||||
<item>Médio</item>
|
<item>Médio</item>
|
||||||
<item>Grande</item>
|
<item>Grande</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string name="encryption">Cifragem</string>
|
<string name="encryption">Criptografia</string>
|
||||||
<string name="key_derivation_function">Função de derivação de chave</string>
|
<string name="key_derivation_function">Função de derivação de chave</string>
|
||||||
<string name="extended_ASCII">ASCII estendido</string>
|
<string name="extended_ASCII">ASCII estendido</string>
|
||||||
<string name="allow">Permitir</string>
|
<string name="allow">Permitir</string>
|
||||||
@@ -137,10 +137,10 @@
|
|||||||
<string name="clipboard_error_clear">Não foi possível limpar a área de transferência</string>
|
<string name="clipboard_error_clear">Não foi possível limpar a área de transferência</string>
|
||||||
<string name="entry_not_found">Não pôde encontrar dados de entrada.</string>
|
<string name="entry_not_found">Não pôde encontrar dados de entrada.</string>
|
||||||
<string name="error_string_key">Um nome do campo é necessário para cada string.</string>
|
<string name="error_string_key">Um nome do campo é necessário para cada string.</string>
|
||||||
<string name="error_autofill_enable_service">Não pôde ser habilitado o serviço de preenchimento automático.</string>
|
<string name="error_autofill_enable_service">Não foi possível habilitar o serviço de preenchimento automático.</string>
|
||||||
<string name="field_name">Nome do campo</string>
|
<string name="field_name">Nome do campo</string>
|
||||||
<string name="field_value">Valor do campo</string>
|
<string name="field_value">Valor do campo</string>
|
||||||
<string name="file_not_found_content">Não pôde encontrar o arquivo. Tente reabri-lo de seu explorador de arquivos.</string>
|
<string name="file_not_found_content">Não foi possível encontrar o arquivo. Tente reabri-lo de seu explorador de arquivos.</string>
|
||||||
<string name="invalid_algorithm">Algoritmo errado.</string>
|
<string name="invalid_algorithm">Algoritmo errado.</string>
|
||||||
<string name="keyfile_is_empty">O arquivo-chave está vazio.</string>
|
<string name="keyfile_is_empty">O arquivo-chave está vazio.</string>
|
||||||
<string name="copy_field">Cópia de %1$s</string>
|
<string name="copy_field">Cópia de %1$s</string>
|
||||||
@@ -156,8 +156,8 @@
|
|||||||
<string name="read_only_warning">KeePassDX precisa de permissões de escrita para poder mudar qualquer coisa no seu banco.</string>
|
<string name="read_only_warning">KeePassDX precisa de permissões de escrita para poder mudar qualquer coisa no seu banco.</string>
|
||||||
<string name="show_recent_files_title">Exibir arquivos recentes</string>
|
<string name="show_recent_files_title">Exibir arquivos recentes</string>
|
||||||
<string name="show_recent_files_summary">Exibir locais de bancos de dados recentes</string>
|
<string name="show_recent_files_summary">Exibir locais de bancos de dados recentes</string>
|
||||||
<string name="encryption_explanation">Algoritmo de cifragem usado para todos os dados.</string>
|
<string name="encryption_explanation">Algoritmo de criptografia usado para todos os dados.</string>
|
||||||
<string name="kdf_explanation">Para gerar uma chave para o algoritmo de cifragem, a chave mestra é transformada usando uma função de derivação de chave salgada aleatoriamente.</string>
|
<string name="kdf_explanation">Para gerar uma chave para o algoritmo de criptografia, a chave-mestra é transformada usando uma função de derivação de chave salgada aleatoriamente.</string>
|
||||||
<string name="memory_usage">Uso de memória</string>
|
<string name="memory_usage">Uso de memória</string>
|
||||||
<string name="memory_usage_explanation">Quantidade de memória a ser usada pela função de derivação de chave.</string>
|
<string name="memory_usage_explanation">Quantidade de memória a ser usada pela função de derivação de chave.</string>
|
||||||
<string name="parallelism">Paralelismo</string>
|
<string name="parallelism">Paralelismo</string>
|
||||||
@@ -174,8 +174,8 @@
|
|||||||
<string name="search_results">Resultados da busca</string>
|
<string name="search_results">Resultados da busca</string>
|
||||||
<string name="warning">Aviso</string>
|
<string name="warning">Aviso</string>
|
||||||
<string name="warning_password_encoding">Evite caracteres fora do formato de codificação do arquivo do banco (todos os caracteres não reconhecidos são convertidos para a mesma letra).</string>
|
<string name="warning_password_encoding">Evite caracteres fora do formato de codificação do arquivo do banco (todos os caracteres não reconhecidos são convertidos para a mesma letra).</string>
|
||||||
<string name="warning_empty_password">Continuar sem proteção de desbloqueio de senha\?</string>
|
<string name="warning_empty_password">Continuar sem proteção de desbloqueio por senha\?</string>
|
||||||
<string name="warning_no_encryption_key">Continuar sem chave de cifragem\?</string>
|
<string name="warning_no_encryption_key">Continuar sem chave de criptografia\?</string>
|
||||||
<string name="encrypted_value_stored">Senha cifrada armazenada</string>
|
<string name="encrypted_value_stored">Senha cifrada armazenada</string>
|
||||||
<string name="no_credentials_stored">Ainda não há nenhuma senha armazenada nesse banco de dados.</string>
|
<string name="no_credentials_stored">Ainda não há nenhuma senha armazenada nesse banco de dados.</string>
|
||||||
<string name="database_history">Histórico</string>
|
<string name="database_history">Histórico</string>
|
||||||
@@ -199,14 +199,14 @@
|
|||||||
<string name="advanced_unlock">Desbloqueio avançado</string>
|
<string name="advanced_unlock">Desbloqueio avançado</string>
|
||||||
<string name="biometric_unlock_enable_title">Desbloqueio biométrico</string>
|
<string name="biometric_unlock_enable_title">Desbloqueio biométrico</string>
|
||||||
<string name="biometric_unlock_enable_summary">Permite que você escaneie sua biometria para a abertura do banco de dados</string>
|
<string name="biometric_unlock_enable_summary">Permite que você escaneie sua biometria para a abertura do banco de dados</string>
|
||||||
<string name="biometric_delete_all_key_title">Apague chaves de cifragem</string>
|
<string name="biometric_delete_all_key_title">Apague chaves de criptografia</string>
|
||||||
<string name="biometric_delete_all_key_summary">Apagar todas as chaves de cifragem relacionadas ao reconhecimento de desbloqueio avançado</string>
|
<string name="biometric_delete_all_key_summary">Apagar todas as chaves de criptografia relacionadas ao reconhecimento de desbloqueio avançado</string>
|
||||||
<string name="unavailable_feature_text">Não foi possível iniciar esse recurso.</string>
|
<string name="unavailable_feature_text">Não foi possível iniciar esse recurso.</string>
|
||||||
<string name="unavailable_feature_version">O dispositivo está utilizando Android %1$s, mas precisa %2$s ou posterior.</string>
|
<string name="unavailable_feature_version">O dispositivo está utilizando Android %1$s, mas precisa %2$s ou posterior.</string>
|
||||||
<string name="unavailable_feature_hardware">Não foi possível encontrar o hardware correspondente.</string>
|
<string name="unavailable_feature_hardware">Não foi possível encontrar o hardware correspondente.</string>
|
||||||
<string name="file_name">Nome do arquivo</string>
|
<string name="file_name">Nome do arquivo</string>
|
||||||
<string name="path">Caminho</string>
|
<string name="path">Caminho</string>
|
||||||
<string name="assign_master_key">Defina uma chave mestra</string>
|
<string name="assign_master_key">Defina uma chave-mestra</string>
|
||||||
<string name="create_keepass_file">Criar novo banco</string>
|
<string name="create_keepass_file">Criar novo banco</string>
|
||||||
<string name="recycle_bin_title">Usar lixeira</string>
|
<string name="recycle_bin_title">Usar lixeira</string>
|
||||||
<string name="recycle_bin_summary">Mover grupos e entradas para o grupo \"Lixeira\" antes de apagar</string>
|
<string name="recycle_bin_summary">Mover grupos e entradas para o grupo \"Lixeira\" antes de apagar</string>
|
||||||
@@ -224,7 +224,7 @@
|
|||||||
<string name="keyboard">Teclado</string>
|
<string name="keyboard">Teclado</string>
|
||||||
<string name="magic_keyboard_title">Magikeyboard</string>
|
<string name="magic_keyboard_title">Magikeyboard</string>
|
||||||
<string name="magic_keyboard_explanation_summary">Ative um teclado customizado, populando suas senhas e todos os campos de identidade</string>
|
<string name="magic_keyboard_explanation_summary">Ative um teclado customizado, populando suas senhas e todos os campos de identidade</string>
|
||||||
<string name="allow_no_password_title">Não permitir chave mestra</string>
|
<string name="allow_no_password_title">Não permitir chave-mestra</string>
|
||||||
<string name="allow_no_password_summary">Permitir tocar no botão \"Abrir\" se nenhuma credencial for selecionada</string>
|
<string name="allow_no_password_summary">Permitir tocar no botão \"Abrir\" se nenhuma credencial for selecionada</string>
|
||||||
<string name="enable_read_only_title">Somente leitura</string>
|
<string name="enable_read_only_title">Somente leitura</string>
|
||||||
<string name="enable_read_only_summary">Por padrão, abrir seu banco de dados no modo somente leitura</string>
|
<string name="enable_read_only_summary">Por padrão, abrir seu banco de dados no modo somente leitura</string>
|
||||||
@@ -238,9 +238,9 @@
|
|||||||
<string name="education_select_database_title">Abra um banco de dados existente</string>
|
<string name="education_select_database_title">Abra um banco de dados existente</string>
|
||||||
<string name="education_select_database_summary">Abra seu arquivo de banco de dados existente a partir do navegador de arquivos.</string>
|
<string name="education_select_database_summary">Abra seu arquivo de banco de dados existente a partir do navegador de arquivos.</string>
|
||||||
<string name="education_new_node_title">Adicione itens ao seu banco</string>
|
<string name="education_new_node_title">Adicione itens ao seu banco</string>
|
||||||
<string name="education_new_node_summary">Entradas ajudam a gerenciar suas identidades digitais.
|
<string name="education_new_node_summary">Entradas ajudam a gerenciar suas identidades digitais.
|
||||||
\n
|
\n
|
||||||
\nGrupos (~pastas) organizam suas entradas e sua base de dados.</string>
|
\nGrupos (~pastas) organizam suas entradas e seu banco de dados.</string>
|
||||||
<string name="education_search_title">Pesquise suas entradas</string>
|
<string name="education_search_title">Pesquise suas entradas</string>
|
||||||
<string name="education_search_summary">Entre com título, nome de usuário ou outros campos para recuperar facilmente suas senhas.</string>
|
<string name="education_search_summary">Entre com título, nome de usuário ou outros campos para recuperar facilmente suas senhas.</string>
|
||||||
<string name="education_entry_edit_title">Modifique a entrada</string>
|
<string name="education_entry_edit_title">Modifique a entrada</string>
|
||||||
@@ -248,7 +248,7 @@
|
|||||||
<string name="education_generate_password_title">Criar uma senha forte</string>
|
<string name="education_generate_password_title">Criar uma senha forte</string>
|
||||||
<string name="education_generate_password_summary">Gere uma senha forte para associar a sua entrada, defina-a facilmente de acordo com os critérios do formulário e lembre-se de torná-la segura.</string>
|
<string name="education_generate_password_summary">Gere uma senha forte para associar a sua entrada, defina-a facilmente de acordo com os critérios do formulário e lembre-se de torná-la segura.</string>
|
||||||
<string name="education_entry_new_field_title">Adicione campos customizados</string>
|
<string name="education_entry_new_field_title">Adicione campos customizados</string>
|
||||||
<string name="education_entry_new_field_summary">Registrar um campo adicional, adicionar um valor e, opcionalmente, protegê-lo.</string>
|
<string name="education_entry_new_field_summary">Registre um campo adicional, adicione um valor e, opcionalmente, proteja-o.</string>
|
||||||
<string name="education_unlock_title">Desbloqueie seu banco de dados</string>
|
<string name="education_unlock_title">Desbloqueie seu banco de dados</string>
|
||||||
<string name="education_read_only_title">Proteja seu banco de modificações</string>
|
<string name="education_read_only_title">Proteja seu banco de modificações</string>
|
||||||
<string name="education_read_only_summary">Altere o modo de abertura para a sessão.
|
<string name="education_read_only_summary">Altere o modo de abertura para a sessão.
|
||||||
@@ -301,7 +301,7 @@
|
|||||||
<string name="keyboard_notification_entry_content_title">%1$s disponível no Magikeyboard</string>
|
<string name="keyboard_notification_entry_content_title">%1$s disponível no Magikeyboard</string>
|
||||||
<string name="keyboard_notification_entry_content_text">%1$s</string>
|
<string name="keyboard_notification_entry_content_text">%1$s</string>
|
||||||
<string name="keyboard_notification_entry_clear_close_title">Limpar ao fechar</string>
|
<string name="keyboard_notification_entry_clear_close_title">Limpar ao fechar</string>
|
||||||
<string name="keyboard_notification_entry_clear_close_summary">Fecha a base de dados ao dispensar a notificação</string>
|
<string name="keyboard_notification_entry_clear_close_summary">Fechar o banco de dados ao dispensar a notificação</string>
|
||||||
<string name="keyboard_appearance_category">Aparência</string>
|
<string name="keyboard_appearance_category">Aparência</string>
|
||||||
<string name="keyboard_theme_title">Tema do teclado</string>
|
<string name="keyboard_theme_title">Tema do teclado</string>
|
||||||
<string name="keyboard_keys_category">Teclas</string>
|
<string name="keyboard_keys_category">Teclas</string>
|
||||||
@@ -347,7 +347,7 @@
|
|||||||
<string name="biometric_auto_open_prompt_summary">Solicitar desbloqueio avançado automaticamente se o banco de dados estiver configurado para usá-lo</string>
|
<string name="biometric_auto_open_prompt_summary">Solicitar desbloqueio avançado automaticamente se o banco de dados estiver configurado para usá-lo</string>
|
||||||
<string name="enable">Habilitado</string>
|
<string name="enable">Habilitado</string>
|
||||||
<string name="disable">Desabilitado</string>
|
<string name="disable">Desabilitado</string>
|
||||||
<string name="master_key">Chave mestra</string>
|
<string name="master_key">Chave-mestra</string>
|
||||||
<string name="security">Segurança</string>
|
<string name="security">Segurança</string>
|
||||||
<string name="entry_history">Histórico</string>
|
<string name="entry_history">Histórico</string>
|
||||||
<string name="entry_setup_otp">Configure uma senha de uso único</string>
|
<string name="entry_setup_otp">Configure uma senha de uso único</string>
|
||||||
@@ -368,7 +368,7 @@
|
|||||||
<string name="invalid_db_same_uuid">%1$s com o mesmo UUID %2$s já existe.</string>
|
<string name="invalid_db_same_uuid">%1$s com o mesmo UUID %2$s já existe.</string>
|
||||||
<string name="creating_database">Criando banco de dados…</string>
|
<string name="creating_database">Criando banco de dados…</string>
|
||||||
<string name="menu_security_settings">Configurações de segurança</string>
|
<string name="menu_security_settings">Configurações de segurança</string>
|
||||||
<string name="menu_master_key_settings">Configurações da chave mestra</string>
|
<string name="menu_master_key_settings">Configurações da chave-mestra</string>
|
||||||
<string name="contains_duplicate_uuid">O banco de dados contém UUIDs duplicados.</string>
|
<string name="contains_duplicate_uuid">O banco de dados contém UUIDs duplicados.</string>
|
||||||
<string name="contains_duplicate_uuid_procedure">Consertar o problema gerando nova UUIDs para duplicatas para continuar\?</string>
|
<string name="contains_duplicate_uuid_procedure">Consertar o problema gerando nova UUIDs para duplicatas para continuar\?</string>
|
||||||
<string name="database_opened">Banco de dados aberto</string>
|
<string name="database_opened">Banco de dados aberto</string>
|
||||||
@@ -381,11 +381,11 @@
|
|||||||
<string name="max_history_size_title">Tamanho máximo</string>
|
<string name="max_history_size_title">Tamanho máximo</string>
|
||||||
<string name="max_history_size_summary">Limitar o tamanho do histórico por entrada</string>
|
<string name="max_history_size_summary">Limitar o tamanho do histórico por entrada</string>
|
||||||
<string name="settings_database_recommend_changing_master_key_title">Renovação recomendada</string>
|
<string name="settings_database_recommend_changing_master_key_title">Renovação recomendada</string>
|
||||||
<string name="settings_database_recommend_changing_master_key_summary">Recomende a mudança da chave mestra (em dias)</string>
|
<string name="settings_database_recommend_changing_master_key_summary">Recomende a mudança da chave-mestra (em dias)</string>
|
||||||
<string name="settings_database_force_changing_master_key_title">Forçar renovação</string>
|
<string name="settings_database_force_changing_master_key_title">Forçar renovação</string>
|
||||||
<string name="settings_database_force_changing_master_key_summary">Forçar mudança da chave mestra (em dias)</string>
|
<string name="settings_database_force_changing_master_key_summary">Forçar mudança da chave-mestra (em dias)</string>
|
||||||
<string name="settings_database_force_changing_master_key_next_time_title">Forçar renovação na próxima vez</string>
|
<string name="settings_database_force_changing_master_key_next_time_title">Forçar renovação na próxima vez</string>
|
||||||
<string name="settings_database_force_changing_master_key_next_time_summary">Forçar mudança da chave mestra na próxima vez (uma vez)</string>
|
<string name="settings_database_force_changing_master_key_next_time_summary">Forçar mudança da chave-mestra na próxima vez (uma vez)</string>
|
||||||
<string name="database_default_username_title">Usuário padrão</string>
|
<string name="database_default_username_title">Usuário padrão</string>
|
||||||
<string name="database_custom_color_title">Cor personalizada do banco de dados</string>
|
<string name="database_custom_color_title">Cor personalizada do banco de dados</string>
|
||||||
<string name="compression">Compressão</string>
|
<string name="compression">Compressão</string>
|
||||||
@@ -399,8 +399,8 @@
|
|||||||
<string name="warning_permanently_delete_nodes">Apagar permanentemente os nós selecionados\?</string>
|
<string name="warning_permanently_delete_nodes">Apagar permanentemente os nós selecionados\?</string>
|
||||||
<string name="keystore_not_accessible">O armazenamento chaves não foi propriamente inicializado.</string>
|
<string name="keystore_not_accessible">O armazenamento chaves não foi propriamente inicializado.</string>
|
||||||
<string name="recycle_bin_group_title">Grupo de lixeira</string>
|
<string name="recycle_bin_group_title">Grupo de lixeira</string>
|
||||||
<string name="enable_auto_save_database_title">Salvar automaticamente a base de dados</string>
|
<string name="enable_auto_save_database_title">Salvar automaticamente o banco de dados</string>
|
||||||
<string name="enable_auto_save_database_summary">Salvar automaticamente a base de dados depois de uma ação importante (somente no modo \"Modificável\")</string>
|
<string name="enable_auto_save_database_summary">Salvar automaticamente o banco de dados depois de uma ação importante (somente no modo \"Modificável\")</string>
|
||||||
<string name="discard">Descartar</string>
|
<string name="discard">Descartar</string>
|
||||||
<string name="contact">Contato</string>
|
<string name="contact">Contato</string>
|
||||||
<string name="auto_focus_search_title">Busca rápida</string>
|
<string name="auto_focus_search_title">Busca rápida</string>
|
||||||
@@ -423,10 +423,10 @@
|
|||||||
<string name="lock_database_show_button_title">Mostrar botão de bloqueio</string>
|
<string name="lock_database_show_button_title">Mostrar botão de bloqueio</string>
|
||||||
<string name="autofill_preference_title">Configurações de preenchimento automático</string>
|
<string name="autofill_preference_title">Configurações de preenchimento automático</string>
|
||||||
<string name="warning_database_link_revoked">Acesso ao arquivo revogado pelo gerenciador de arquivos</string>
|
<string name="warning_database_link_revoked">Acesso ao arquivo revogado pelo gerenciador de arquivos</string>
|
||||||
<string name="warning_database_read_only">Conceder acesso de gravação de arquivo para salvar alterações na base de dados</string>
|
<string name="warning_database_read_only">Conceder acesso de gravação de arquivo para salvar alterações no banco de dados</string>
|
||||||
<string name="hide_broken_locations_summary">Esconder links quebrados na lista de bases de dados recentes</string>
|
<string name="hide_broken_locations_summary">Esconder links quebrados na lista de bancos de dados recentes</string>
|
||||||
<string name="hide_broken_locations_title">Ocultar links quebrados do banco de dados</string>
|
<string name="hide_broken_locations_title">Ocultar links quebrados do banco de dados</string>
|
||||||
<string name="remember_keyfile_locations_summary">Manter o local onde os arquivos-chave são armazenados</string>
|
<string name="remember_keyfile_locations_summary">Lembrar o local onde os arquivos-chave são guardados</string>
|
||||||
<string name="remember_keyfile_locations_title">Lembrar a localização dos arquivos-chave</string>
|
<string name="remember_keyfile_locations_title">Lembrar a localização dos arquivos-chave</string>
|
||||||
<string name="remember_database_locations_summary">Lembra o local onde os bancos de dados são guardados</string>
|
<string name="remember_database_locations_summary">Lembra o local onde os bancos de dados são guardados</string>
|
||||||
<string name="remember_database_locations_title">Lembrar a localização dos bancos de dados</string>
|
<string name="remember_database_locations_title">Lembrar a localização dos bancos de dados</string>
|
||||||
@@ -589,7 +589,7 @@
|
|||||||
<string name="templates_group_uuid_title">Grupo de modelos</string>
|
<string name="templates_group_uuid_title">Grupo de modelos</string>
|
||||||
<string name="templates_group_enable_summary">Use modelos dinâmicos para preencher os campos de uma entrada</string>
|
<string name="templates_group_enable_summary">Use modelos dinâmicos para preencher os campos de uma entrada</string>
|
||||||
<string name="templates_group_enable_title">Uso de modelos</string>
|
<string name="templates_group_enable_title">Uso de modelos</string>
|
||||||
<string name="advanced_unlock_delete_all_key_warning">Apagar todas as chaves de cifragem relacionadas ao reconhecimento de desbloqueio avançado\?</string>
|
<string name="advanced_unlock_delete_all_key_warning">Apagar todas as chaves de criptografia relacionadas ao reconhecimento de desbloqueio avançado\?</string>
|
||||||
<string name="advanced_unlock_timeout">Tempo limite de desbloqueio avançado</string>
|
<string name="advanced_unlock_timeout">Tempo limite de desbloqueio avançado</string>
|
||||||
<string name="temp_advanced_unlock_timeout_summary">Duração do uso de desbloqueio avançado antes de excluir seu conteúdo</string>
|
<string name="temp_advanced_unlock_timeout_summary">Duração do uso de desbloqueio avançado antes de excluir seu conteúdo</string>
|
||||||
<string name="temp_advanced_unlock_timeout_title">Expiração de desbloqueio avançado</string>
|
<string name="temp_advanced_unlock_timeout_title">Expiração de desbloqueio avançado</string>
|
||||||
@@ -600,4 +600,7 @@
|
|||||||
<string name="advanced_unlock_tap_delete">Toque para excluir as chaves de desbloqueio avançado</string>
|
<string name="advanced_unlock_tap_delete">Toque para excluir as chaves de desbloqueio avançado</string>
|
||||||
<string name="content">Conteúdo</string>
|
<string name="content">Conteúdo</string>
|
||||||
<string name="autofill_select_entry">Selecione a entrada…</string>
|
<string name="autofill_select_entry">Selecione a entrada…</string>
|
||||||
|
<string name="hint_icon_name">Nome do ícone</string>
|
||||||
|
<string name="permission">Permissão</string>
|
||||||
|
<string name="warning_exact_alarm">Você não permitiu que o aplicativo usasse um alarme exato. Como resultado, os recursos que requerem um cronômetro não serão executados com um tempo exato.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -578,4 +578,7 @@
|
|||||||
<string name="debit_credit_card">Cartão de crédito / débito</string>
|
<string name="debit_credit_card">Cartão de crédito / débito</string>
|
||||||
<string name="template_group_name">Modelos</string>
|
<string name="template_group_name">Modelos</string>
|
||||||
<string name="content_description_otp_information">Informação de palavra-passe de uso único</string>
|
<string name="content_description_otp_information">Informação de palavra-passe de uso único</string>
|
||||||
|
<string name="hint_icon_name">Nome do ícone</string>
|
||||||
|
<string name="permission">Permissão</string>
|
||||||
|
<string name="warning_exact_alarm">Não permitiu que a app usasse um alarme exato. Como resultado, as funcionalidades que requerem um temporizador não serão feitas com um tempo exato.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -494,7 +494,7 @@
|
|||||||
<string name="save_mode">Режим записи</string>
|
<string name="save_mode">Режим записи</string>
|
||||||
<string name="search_mode">Режим поиска</string>
|
<string name="search_mode">Режим поиска</string>
|
||||||
<string name="error_registration_read_only">Сохранение новых записей невозможно, т.к. база открыта только для чтения</string>
|
<string name="error_registration_read_only">Сохранение новых записей невозможно, т.к. база открыта только для чтения</string>
|
||||||
<string name="error_field_name_already_exists">Поле с таким именем уже существует.</string>
|
<string name="error_field_name_already_exists">Поле с таким названием уже существует.</string>
|
||||||
<string name="device_credential_unlock_enable_summary">Позволяет использовать учётные данные вашего устройства для открытия базы</string>
|
<string name="device_credential_unlock_enable_summary">Позволяет использовать учётные данные вашего устройства для открытия базы</string>
|
||||||
<string name="device_credential_unlock_enable_title">Разблокировка учётными данными устройства</string>
|
<string name="device_credential_unlock_enable_title">Разблокировка учётными данными устройства</string>
|
||||||
<string name="device_credential">Учётные данные устройства</string>
|
<string name="device_credential">Учётные данные устройства</string>
|
||||||
@@ -602,4 +602,7 @@
|
|||||||
<string name="autofill_select_entry">Выбор записи…</string>
|
<string name="autofill_select_entry">Выбор записи…</string>
|
||||||
<string name="autofill_manual_selection_summary">Показывать функцию, позволяющую пользователю вручную выбирать запить из базы</string>
|
<string name="autofill_manual_selection_summary">Показывать функцию, позволяющую пользователю вручную выбирать запить из базы</string>
|
||||||
<string name="autofill_manual_selection_title">Ручной выбор</string>
|
<string name="autofill_manual_selection_title">Ручной выбор</string>
|
||||||
|
<string name="hint_icon_name">Название значка</string>
|
||||||
|
<string name="permission">Разрешение</string>
|
||||||
|
<string name="warning_exact_alarm">Вы не разрешили приложению использовать точный сигнал будильника. В результате требующие этого функции не будут выполняться с точным временем.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -300,7 +300,7 @@
|
|||||||
<string name="icon_pack_choose_title">Simge paketi</string>
|
<string name="icon_pack_choose_title">Simge paketi</string>
|
||||||
<string name="icon_pack_choose_summary">Uygulamada kullanılan simge paketi</string>
|
<string name="icon_pack_choose_summary">Uygulamada kullanılan simge paketi</string>
|
||||||
<string name="selection_mode">Seçim modu</string>
|
<string name="selection_mode">Seçim modu</string>
|
||||||
<string name="do_not_kill_app">Uygulamayı öldürmeyin…</string>
|
<string name="do_not_kill_app">Uygulamayı sonlandırmayın…</string>
|
||||||
<string name="lock_database_back_root_summary">Kullanıcı kök ekranda geri düğmesine tıkladığında veri tabanını kilitle</string>
|
<string name="lock_database_back_root_summary">Kullanıcı kök ekranda geri düğmesine tıkladığında veri tabanını kilitle</string>
|
||||||
<string name="clear_clipboard_notification_title">Kapanışta temizle</string>
|
<string name="clear_clipboard_notification_title">Kapanışta temizle</string>
|
||||||
<string name="clear_clipboard_notification_summary">Panonun süresi dolduğunda veya kullanmaya başladıktan sonra bildirim kapatıldığında veri tabanını kilitle</string>
|
<string name="clear_clipboard_notification_summary">Panonun süresi dolduğunda veya kullanmaya başladıktan sonra bildirim kapatıldığında veri tabanını kilitle</string>
|
||||||
@@ -597,4 +597,7 @@
|
|||||||
<string name="autofill_manual_selection_summary">Kullanıcının veri tabanı girdisini seçmesine izin veren seçeneği göster</string>
|
<string name="autofill_manual_selection_summary">Kullanıcının veri tabanı girdisini seçmesine izin veren seçeneği göster</string>
|
||||||
<string name="autofill_manual_selection_title">Elle seçim</string>
|
<string name="autofill_manual_selection_title">Elle seçim</string>
|
||||||
<string name="autofill_select_entry">Girdi seç…</string>
|
<string name="autofill_select_entry">Girdi seç…</string>
|
||||||
|
<string name="hint_icon_name">Simge adı</string>
|
||||||
|
<string name="warning_exact_alarm">Uygulamanın tam bir alarm kullanmasına izin vermediniz. Sonuç olarak zamanlayıcı gerektiren özellikler kesin bir süre ile yapılmayacaktır.</string>
|
||||||
|
<string name="permission">İzinler</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
<string name="content_description_open_file">Відкрити файл</string>
|
<string name="content_description_open_file">Відкрити файл</string>
|
||||||
<string name="extended_ASCII">Розширений ASCII</string>
|
<string name="extended_ASCII">Розширений ASCII</string>
|
||||||
<string name="edit_entry">Змінити запис</string>
|
<string name="edit_entry">Змінити запис</string>
|
||||||
<string name="clear_clipboard_notification_summary">Блокувати базу даних після закінчення терміну дії буфера обміну, або закрито сповіщення після початку користування ним</string>
|
<string name="clear_clipboard_notification_summary">Блокувати базу даних після закриття сповіщення після використання або якщо вичерпано час очікування</string>
|
||||||
<string name="content_description_add_group">Додати групу</string>
|
<string name="content_description_add_group">Додати групу</string>
|
||||||
<string name="content_description_update_from_list">Оновити</string>
|
<string name="content_description_update_from_list">Оновити</string>
|
||||||
<string name="keyboard">Клавіатура</string>
|
<string name="keyboard">Клавіатура</string>
|
||||||
@@ -159,7 +159,7 @@
|
|||||||
<string name="clipboard_error">Деякі пристрої не дозволяють застосункам користуватися буфером обміну.</string>
|
<string name="clipboard_error">Деякі пристрої не дозволяють застосункам користуватися буфером обміну.</string>
|
||||||
<string name="clipboard_error_title">Помилка буфера обміну</string>
|
<string name="clipboard_error_title">Помилка буфера обміну</string>
|
||||||
<string name="allow">Дозволити</string>
|
<string name="allow">Дозволити</string>
|
||||||
<string name="key_derivation_function">Функція створення ключа</string>
|
<string name="key_derivation_function">Функція генерування ключа</string>
|
||||||
<string name="encryption">Шифрування</string>
|
<string name="encryption">Шифрування</string>
|
||||||
<string name="security">Безпека</string>
|
<string name="security">Безпека</string>
|
||||||
<string name="master_key">Головний ключ</string>
|
<string name="master_key">Головний ключ</string>
|
||||||
@@ -375,7 +375,7 @@
|
|||||||
<string name="file_name">Назва файлу</string>
|
<string name="file_name">Назва файлу</string>
|
||||||
<string name="unavailable_feature_version">Пристрій працює під керуванням Android %1$s, але необхідний %2$s чи пізніші.</string>
|
<string name="unavailable_feature_version">Пристрій працює під керуванням Android %1$s, але необхідний %2$s чи пізніші.</string>
|
||||||
<string name="unavailable_feature_text">Не вдалось запустити цю функцію.</string>
|
<string name="unavailable_feature_text">Не вдалось запустити цю функцію.</string>
|
||||||
<string name="biometric_delete_all_key_summary">Видалити всі ключі шифрування, пов\'язані з розпізнаванням розширеного розблокування</string>
|
<string name="biometric_delete_all_key_summary">Видалити всі ключі шифрування, пов’язані з розпізнаванням розширеного розблокування</string>
|
||||||
<string name="biometric_delete_all_key_title">Видалити ключі шифрування</string>
|
<string name="biometric_delete_all_key_title">Видалити ключі шифрування</string>
|
||||||
<string name="biometric_auto_open_prompt_summary">Автоматично запитувати розширене розблокування, якщо базу даних налаштовано для роботи з ним</string>
|
<string name="biometric_auto_open_prompt_summary">Автоматично запитувати розширене розблокування, якщо базу даних налаштовано для роботи з ним</string>
|
||||||
<string name="biometric_auto_open_prompt_title">Автозапит ключа</string>
|
<string name="biometric_auto_open_prompt_title">Автозапит ключа</string>
|
||||||
@@ -471,8 +471,8 @@
|
|||||||
<string name="warning_empty_keyfile">Не рекомендовано додавати порожній файл ключа.</string>
|
<string name="warning_empty_keyfile">Не рекомендовано додавати порожній файл ключа.</string>
|
||||||
<string name="warning_empty_keyfile_explanation">Вміст файлу ключів ніколи не слід змінювати, а в кращому випадку повинен містити випадково згенеровані дані.</string>
|
<string name="warning_empty_keyfile_explanation">Вміст файлу ключів ніколи не слід змінювати, а в кращому випадку повинен містити випадково згенеровані дані.</string>
|
||||||
<string name="data">Дані</string>
|
<string name="data">Дані</string>
|
||||||
<string name="database_data_remove_unlinked_attachments_title">Вилучити незв\'язані дані</string>
|
<string name="database_data_remove_unlinked_attachments_title">Вилучити непов’язані дані</string>
|
||||||
<string name="database_data_remove_unlinked_attachments_summary">Вилучає вкладення, що містяться в базі даних, але не пов’язані із записом</string>
|
<string name="database_data_remove_unlinked_attachments_summary">Вилучає вкладення, що містяться в базі даних, але не пов’язані з записом</string>
|
||||||
<string name="show_uuid_summary">Показ пов\'язаного з записом чи групою UUID</string>
|
<string name="show_uuid_summary">Показ пов\'язаного з записом чи групою UUID</string>
|
||||||
<string name="show_uuid_title">Показувати UUID</string>
|
<string name="show_uuid_title">Показувати UUID</string>
|
||||||
<string name="autofill_read_only_save">Збереження даних заборонено для бази даних, відкритої лише для читання.</string>
|
<string name="autofill_read_only_save">Збереження даних заборонено для бази даних, відкритої лише для читання.</string>
|
||||||
@@ -495,7 +495,7 @@
|
|||||||
<string name="search_mode">Режим пошуку</string>
|
<string name="search_mode">Режим пошуку</string>
|
||||||
<string name="error_registration_read_only">Збереження нового елемента заборонено в базі даних лише для читання</string>
|
<string name="error_registration_read_only">Збереження нового елемента заборонено в базі даних лише для читання</string>
|
||||||
<string name="error_field_name_already_exists">Назва поля вже існує.</string>
|
<string name="error_field_name_already_exists">Назва поля вже існує.</string>
|
||||||
<string name="advanced_unlock_delete_all_key_warning">Видалити всі ключі шифрування, пов\'язані з розширеним розпізнаванням розблокування\?</string>
|
<string name="advanced_unlock_delete_all_key_warning">Видалити всі ключі шифрування, пов’язані з розширеним розпізнаванням розблокування\?</string>
|
||||||
<string name="device_credential_unlock_enable_summary">Дає змогу використовувати облікові дані пристрою для відкриття бази даних</string>
|
<string name="device_credential_unlock_enable_summary">Дає змогу використовувати облікові дані пристрою для відкриття бази даних</string>
|
||||||
<string name="device_credential_unlock_enable_title">Розблокування облікових даних пристрою</string>
|
<string name="device_credential_unlock_enable_title">Розблокування облікових даних пристрою</string>
|
||||||
<string name="device_credential">Облікові дані пристрою</string>
|
<string name="device_credential">Облікові дані пристрою</string>
|
||||||
@@ -521,7 +521,7 @@
|
|||||||
<string name="education_advanced_unlock_summary">Пов’яжіть свій пароль зі сканованими біометричними даними або даними пристрою, щоб швидко розблокувати базу даних.</string>
|
<string name="education_advanced_unlock_summary">Пов’яжіть свій пароль зі сканованими біометричними даними або даними пристрою, щоб швидко розблокувати базу даних.</string>
|
||||||
<string name="education_advanced_unlock_title">Розширене розблокування бази даних</string>
|
<string name="education_advanced_unlock_title">Розширене розблокування бази даних</string>
|
||||||
<string name="temp_advanced_unlock_timeout_summary">Тривалість використання розширеного розблокування перед видаленням його вмісту</string>
|
<string name="temp_advanced_unlock_timeout_summary">Тривалість використання розширеного розблокування перед видаленням його вмісту</string>
|
||||||
<string name="temp_advanced_unlock_enable_summary">Не зберігайте зашифрований вміст для використання розширеного розблокування</string>
|
<string name="temp_advanced_unlock_enable_summary">Не зберігати зашифрований вміст для використання розширеного розблокування</string>
|
||||||
<string name="temp_advanced_unlock_enable_title">Тимчасове розширене розблокування</string>
|
<string name="temp_advanced_unlock_enable_title">Тимчасове розширене розблокування</string>
|
||||||
<string name="advanced_unlock_tap_delete">Торкнутися, щоб видалити клавіші розширеного розблокування</string>
|
<string name="advanced_unlock_tap_delete">Торкнутися, щоб видалити клавіші розширеного розблокування</string>
|
||||||
<string name="content">Вміст</string>
|
<string name="content">Вміст</string>
|
||||||
@@ -542,7 +542,7 @@
|
|||||||
<string name="download_canceled">Скасовано!</string>
|
<string name="download_canceled">Скасовано!</string>
|
||||||
<string name="icon_section_custom">Власні</string>
|
<string name="icon_section_custom">Власні</string>
|
||||||
<string name="icon_section_standard">Стандартні</string>
|
<string name="icon_section_standard">Стандартні</string>
|
||||||
<string name="style_brightness_summary">Вибрати світлі або темні теми</string>
|
<string name="style_brightness_summary">Вибрати світлу або темну теми</string>
|
||||||
<string name="style_brightness_title">Яскравість теми</string>
|
<string name="style_brightness_title">Яскравість теми</string>
|
||||||
<string name="error_upload_file">Під час передавання даних файлу сталася помилка.</string>
|
<string name="error_upload_file">Під час передавання даних файлу сталася помилка.</string>
|
||||||
<string name="error_file_to_big">Файл, який ви намагаєтеся передати, завеликий.</string>
|
<string name="error_file_to_big">Файл, який ви намагаєтеся передати, завеликий.</string>
|
||||||
@@ -602,4 +602,7 @@
|
|||||||
<string name="autofill_manual_selection_summary">Параметр показу, який дає користувачеві змогу вибрати запис бази даних</string>
|
<string name="autofill_manual_selection_summary">Параметр показу, який дає користувачеві змогу вибрати запис бази даних</string>
|
||||||
<string name="autofill_manual_selection_title">Вибір вручну</string>
|
<string name="autofill_manual_selection_title">Вибір вручну</string>
|
||||||
<string name="autofill_select_entry">Вибрати запис…</string>
|
<string name="autofill_select_entry">Вибрати запис…</string>
|
||||||
|
<string name="hint_icon_name">Назва піктограми</string>
|
||||||
|
<string name="permission">Дозвіл</string>
|
||||||
|
<string name="warning_exact_alarm">Ви не дозволили застосунку використовувати точний час. У результаті функції, для яких потрібен таймер, не виконуватимуться точно за часом.</string>
|
||||||
</resources>
|
</resources>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user