mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'Kunzisoft:develop' into develop
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
KeePassDX(3.0.0)
|
KeePassDX(3.0.0)
|
||||||
* Add / Manage dynamic templates #191
|
* Add / Manage dynamic templates #191
|
||||||
* Manually select RecycleBin group and Templates group #191
|
* Manually select RecycleBin group and Templates group #191
|
||||||
|
* Setting to display OTP Token in list #655
|
||||||
* Fix timeout in dialogs #716
|
* Fix timeout in dialogs #716
|
||||||
* Check URI permissions #626
|
* Check URI permissions #626
|
||||||
* Small changes #1035 #1043 #942
|
* Improvements #1035 #1043 #942 #1021 #1027
|
||||||
|
|
||||||
KeePassDX(2.10.5)
|
KeePassDX(2.10.5)
|
||||||
* Increase the saving speed of database #1028
|
* Increase the saving speed of database #1028
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ package com.igreenwood.loupe
|
|||||||
import android.animation.Animator
|
import android.animation.Animator
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.graphics.Matrix
|
import android.graphics.Matrix
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
@@ -108,6 +109,8 @@ class Loupe(imageView: ImageView, container: ViewGroup) : View.OnTouchListener,
|
|||||||
var viewDragFriction = DEFAULT_VIEW_DRAG_FRICTION
|
var viewDragFriction = DEFAULT_VIEW_DRAG_FRICTION
|
||||||
// drag distance threshold in dp for swipe to dismiss
|
// drag distance threshold in dp for swipe to dismiss
|
||||||
var dragDismissDistanceInDp = DEFAULT_DRAG_DISMISS_DISTANCE_IN_DP
|
var dragDismissDistanceInDp = DEFAULT_DRAG_DISMISS_DISTANCE_IN_DP
|
||||||
|
// on view touched
|
||||||
|
var onViewTouchedListener: View.OnTouchListener? = null
|
||||||
// on view translate listener
|
// on view translate listener
|
||||||
var onViewTranslateListener: OnViewTranslateListener? = null
|
var onViewTranslateListener: OnViewTranslateListener? = null
|
||||||
// on scale changed
|
// on scale changed
|
||||||
@@ -272,7 +275,10 @@ class Loupe(imageView: ImageView, container: ViewGroup) : View.OnTouchListener,
|
|||||||
private var imageViewRef: WeakReference<ImageView> = WeakReference(imageView)
|
private var imageViewRef: WeakReference<ImageView> = WeakReference(imageView)
|
||||||
private var containerRef: WeakReference<ViewGroup> = WeakReference(container)
|
private var containerRef: WeakReference<ViewGroup> = WeakReference(container)
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
|
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
|
||||||
|
onViewTouchedListener?.onTouch(view, event)
|
||||||
|
|
||||||
event ?: return false
|
event ?: return false
|
||||||
val imageView = imageViewRef.get() ?: return false
|
val imageView = imageViewRef.get() ?: return false
|
||||||
val container = containerRef.get() ?: return false
|
val container = containerRef.get() ?: return false
|
||||||
|
|||||||
@@ -157,11 +157,6 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
mEntryViewModel.url.observe(this) { url ->
|
|
||||||
this.mUrl = url
|
|
||||||
invalidateOptionsMenu()
|
|
||||||
}
|
|
||||||
|
|
||||||
mEntryViewModel.entryInfo.observe(this) { entryInfo ->
|
mEntryViewModel.entryInfo.observe(this) { entryInfo ->
|
||||||
// Manage entry copy to start notification if allowed (at the first start)
|
// Manage entry copy to start notification if allowed (at the first start)
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
@@ -184,6 +179,8 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
collapsingToolbarLayout?.title = entryTitle
|
collapsingToolbarLayout?.title = entryTitle
|
||||||
toolbar?.title = entryTitle
|
toolbar?.title = entryTitle
|
||||||
|
|
||||||
|
mUrl = entryInfo.url
|
||||||
|
|
||||||
// Refresh Menu
|
// Refresh Menu
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
|
|
||||||
|
|||||||
@@ -143,13 +143,13 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
|
|
||||||
// Entry is retrieve, it's an entry to update
|
// Entry is retrieve, it's an entry to update
|
||||||
intent.getParcelableExtra<NodeId<UUID>>(KEY_ENTRY)?.let { entryToUpdate ->
|
intent.getParcelableExtra<NodeId<UUID>>(KEY_ENTRY)?.let { entryToUpdate ->
|
||||||
intent.removeExtra(KEY_ENTRY)
|
//intent.removeExtra(KEY_ENTRY)
|
||||||
mEntryId = entryToUpdate
|
mEntryId = entryToUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parent is retrieve, it's a new entry to create
|
// Parent is retrieve, it's a new entry to create
|
||||||
intent.getParcelableExtra<NodeId<*>>(KEY_PARENT)?.let { parent ->
|
intent.getParcelableExtra<NodeId<*>>(KEY_PARENT)?.let { parent ->
|
||||||
intent.removeExtra(KEY_PARENT)
|
//intent.removeExtra(KEY_PARENT)
|
||||||
mParentId = parent
|
mParentId = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,37 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
// Save button
|
// Save button
|
||||||
validateButton?.setOnClickListener { saveEntry() }
|
validateButton?.setOnClickListener { saveEntry() }
|
||||||
|
|
||||||
mEntryEditViewModel.entryInfo.observe(this) {
|
mEntryEditViewModel.templatesEntry.observe(this) { templatesEntry ->
|
||||||
|
// Change template dynamically
|
||||||
|
templatesEntry?.templates?.let { templates ->
|
||||||
|
val defaultTemplate = templatesEntry.defaultTemplate
|
||||||
|
templateSelectorSpinner?.apply {
|
||||||
|
// Build template selector
|
||||||
|
if (templates.isNotEmpty()) {
|
||||||
|
adapter = TemplatesSelectorAdapter(
|
||||||
|
this@EntryEditActivity,
|
||||||
|
mIconDrawableFactory,
|
||||||
|
templates
|
||||||
|
)
|
||||||
|
setSelection(templates.indexOf(defaultTemplate))
|
||||||
|
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(
|
||||||
|
parent: AdapterView<*>?,
|
||||||
|
view: View?,
|
||||||
|
position: Int,
|
||||||
|
id: Long
|
||||||
|
) {
|
||||||
|
mEntryEditViewModel.changeTemplate(templates[position])
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loadingView?.hideByFading()
|
loadingView?.hideByFading()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,27 +268,6 @@ class EntryEditActivity : DatabaseLockActivity(),
|
|||||||
mAttachmentFileBinderManager?.removeBinaryAttachment(it)
|
mAttachmentFileBinderManager?.removeBinaryAttachment(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change template dynamically
|
|
||||||
mEntryEditViewModel.templates.observe(this) { templatesLoaded ->
|
|
||||||
val templates = templatesLoaded.templates
|
|
||||||
val defaultTemplate = templatesLoaded.defaultTemplate
|
|
||||||
templateSelectorSpinner?.apply {
|
|
||||||
// Build template selector
|
|
||||||
if (templates.isNotEmpty()) {
|
|
||||||
adapter = TemplatesSelectorAdapter(this@EntryEditActivity, mIconDrawableFactory, templates)
|
|
||||||
setSelection(templates.indexOf(defaultTemplate))
|
|
||||||
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
|
||||||
mEntryEditViewModel.changeTemplate(templates[position])
|
|
||||||
}
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
visibility = View.GONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build new entry from the entry info retrieved
|
// Build new entry from the entry info retrieved
|
||||||
mEntryEditViewModel.onEntrySaved.observe(this) { entrySave ->
|
mEntryEditViewModel.onEntrySaved.observe(this) { entrySave ->
|
||||||
// Open a progress dialog and save entry
|
// Open a progress dialog and save entry
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.activities
|
package com.kunzisoft.keepass.activities
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -43,6 +44,7 @@ class ImageViewerActivity : DatabaseLockActivity() {
|
|||||||
private lateinit var imageView: ImageView
|
private lateinit var imageView: ImageView
|
||||||
private lateinit var progressView: View
|
private lateinit var progressView: View
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -52,12 +54,21 @@ class ImageViewerActivity : DatabaseLockActivity() {
|
|||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
toolbar.setOnTouchListener { _, _ ->
|
||||||
|
resetAppTimeout()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
imageContainerView = findViewById(R.id.image_viewer_container)
|
imageContainerView = findViewById(R.id.image_viewer_container)
|
||||||
imageView = findViewById(R.id.image_viewer_image)
|
imageView = findViewById(R.id.image_viewer_image)
|
||||||
progressView = findViewById(R.id.image_viewer_progress)
|
progressView = findViewById(R.id.image_viewer_progress)
|
||||||
|
|
||||||
Loupe.create(imageView, imageContainerView!!) {
|
Loupe.create(imageView, imageContainerView!!) {
|
||||||
|
onViewTouchedListener = View.OnTouchListener { _, _ ->
|
||||||
|
// to reset timeout when Loupe image view touched
|
||||||
|
resetAppTimeout()
|
||||||
|
false
|
||||||
|
}
|
||||||
onViewTranslateListener = object : Loupe.OnViewTranslateListener {
|
onViewTranslateListener = object : Loupe.OnViewTranslateListener {
|
||||||
|
|
||||||
override fun onStart(view: ImageView) {
|
override fun onStart(view: ImageView) {
|
||||||
@@ -81,7 +92,8 @@ class ImageViewerActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun viewToInvalidateTimeout(): View? {
|
override fun viewToInvalidateTimeout(): View? {
|
||||||
return imageContainerView
|
// Null to manually manage events
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun finishActivityIfReloadRequested(): Boolean {
|
override fun finishActivityIfReloadRequested(): Boolean {
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import android.os.Bundle
|
|||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
||||||
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewFocusedOrChanged
|
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
||||||
|
|
||||||
abstract class DatabaseDialogFragment : DialogFragment(), DatabaseRetrieval {
|
abstract class DatabaseDialogFragment : DialogFragment(), DatabaseRetrieval {
|
||||||
@@ -19,7 +20,7 @@ abstract class DatabaseDialogFragment : DialogFragment(), DatabaseRetrieval {
|
|||||||
|
|
||||||
mDatabaseViewModel.database.observe(this) { database ->
|
mDatabaseViewModel.database.observe(this) { database ->
|
||||||
this.mDatabase = database
|
this.mDatabase = database
|
||||||
resetAppTimeoutWhenViewFocusedOrChanged()
|
resetAppTimeoutOnTouchOrFocus()
|
||||||
onDatabaseRetrieved(database)
|
onDatabaseRetrieved(database)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ abstract class DatabaseDialogFragment : DialogFragment(), DatabaseRetrieval {
|
|||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
|
||||||
resetAppTimeoutWhenViewFocusedOrChanged()
|
resetAppTimeoutOnTouchOrFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDatabaseRetrieved(database: Database?) {
|
override fun onDatabaseRetrieved(database: Database?) {
|
||||||
@@ -46,9 +47,25 @@ abstract class DatabaseDialogFragment : DialogFragment(), DatabaseRetrieval {
|
|||||||
// Can be overridden by a subclass
|
// Can be overridden by a subclass
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetAppTimeoutWhenViewFocusedOrChanged() {
|
fun resetAppTimeout() {
|
||||||
context?.let {
|
context?.let {
|
||||||
dialog?.window?.decorView?.resetAppTimeoutWhenViewFocusedOrChanged(it, mDatabase?.loaded)
|
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(it,
|
||||||
|
mDatabase?.loaded ?: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun overrideTimeoutTouchAndFocusEvents(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetAppTimeoutOnTouchOrFocus() {
|
||||||
|
if (!overrideTimeoutTouchAndFocusEvents()) {
|
||||||
|
context?.let {
|
||||||
|
dialog?.window?.decorView?.resetAppTimeoutWhenViewTouchedOrFocused(
|
||||||
|
it,
|
||||||
|
mDatabase?.loaded
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,10 @@ import android.app.DatePickerDialog
|
|||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
|
||||||
class DatePickerFragment : DatabaseDialogFragment() {
|
// Not as DatabaseDialogFragment because crash on KitKat
|
||||||
|
class DatePickerFragment : DialogFragment() {
|
||||||
|
|
||||||
private var mDefaultYear: Int = 2000
|
private var mDefaultYear: Int = 2000
|
||||||
private var mDefaultMonth: Int = 1
|
private var mDefaultMonth: Int = 1
|
||||||
|
|||||||
@@ -19,11 +19,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.activities.dialogs
|
package com.kunzisoft.keepass.activities.dialogs
|
||||||
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.model.MainCredential
|
import com.kunzisoft.keepass.model.MainCredential
|
||||||
|
|||||||
@@ -79,11 +79,15 @@ class SetOTPDialogFragment : DatabaseDialogFragment() {
|
|||||||
private var mOnFocusChangeListener = View.OnFocusChangeListener { _, isFocus ->
|
private var mOnFocusChangeListener = View.OnFocusChangeListener { _, isFocus ->
|
||||||
if (!isFocus)
|
if (!isFocus)
|
||||||
mManualEvent = true
|
mManualEvent = true
|
||||||
|
else
|
||||||
|
resetAppTimeout()
|
||||||
}
|
}
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private var mOnTouchListener = View.OnTouchListener { _, event ->
|
private var mOnTouchListener = View.OnTouchListener { _, event ->
|
||||||
when (event.action) {
|
when (event.action) {
|
||||||
MotionEvent.ACTION_DOWN -> {
|
MotionEvent.ACTION_DOWN -> {
|
||||||
mManualEvent = true
|
mManualEvent = true
|
||||||
|
resetAppTimeout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@@ -94,6 +98,10 @@ class SetOTPDialogFragment : DatabaseDialogFragment() {
|
|||||||
private var mPeriodWellFormed = false
|
private var mPeriodWellFormed = false
|
||||||
private var mDigitsWellFormed = false
|
private var mDigitsWellFormed = false
|
||||||
|
|
||||||
|
override fun overrideTimeoutTouchAndFocusEvents(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
super.onAttach(context)
|
super.onAttach(context)
|
||||||
// Verify that the host activity implements the callback interface
|
// Verify that the host activity implements the callback interface
|
||||||
@@ -224,8 +232,11 @@ class SetOTPDialogFragment : DatabaseDialogFragment() {
|
|||||||
val builder = AlertDialog.Builder(activity)
|
val builder = AlertDialog.Builder(activity)
|
||||||
builder.apply {
|
builder.apply {
|
||||||
setView(root)
|
setView(root)
|
||||||
.setPositiveButton(android.R.string.ok) {_, _ -> }
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
resetAppTimeout()
|
||||||
|
}
|
||||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
|
resetAppTimeout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import android.app.TimePickerDialog
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
|
||||||
class TimePickerFragment : DatabaseDialogFragment() {
|
// Not as DatabaseDialogFragment because crash on KitKat
|
||||||
|
class TimePickerFragment : DialogFragment() {
|
||||||
|
|
||||||
private var defaultHour: Int = 0
|
private var defaultHour: Int = 0
|
||||||
private var defaultMinute: Int = 0
|
private var defaultMinute: Int = 0
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import android.os.Bundle
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
|
||||||
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewFocusedOrChanged
|
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
|
||||||
import com.kunzisoft.keepass.activities.stylish.StylishFragment
|
import com.kunzisoft.keepass.activities.stylish.StylishFragment
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
||||||
@@ -33,7 +33,7 @@ abstract class DatabaseFragment : StylishFragment(), DatabaseRetrieval {
|
|||||||
|
|
||||||
protected fun resetAppTimeoutWhenViewFocusedOrChanged(view: View?) {
|
protected fun resetAppTimeoutWhenViewFocusedOrChanged(view: View?) {
|
||||||
context?.let {
|
context?.let {
|
||||||
view?.resetAppTimeoutWhenViewFocusedOrChanged(it, mDatabase?.loaded)
|
view?.resetAppTimeoutWhenViewTouchedOrFocused(it, mDatabase?.loaded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,10 +118,11 @@ class EntryEditFragment: DatabaseFragment() {
|
|||||||
templateView.setTemplate(template)
|
templateView.setTemplate(template)
|
||||||
}
|
}
|
||||||
|
|
||||||
mEntryEditViewModel.entryInfo.observe(viewLifecycleOwner) { entryInfo ->
|
mEntryEditViewModel.templatesEntry.observe(viewLifecycleOwner) { templateEntry ->
|
||||||
|
templateView.setTemplate(templateEntry.defaultTemplate)
|
||||||
// Load entry info only the first time to keep change locally
|
// Load entry info only the first time to keep change locally
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
assignEntryInfo(entryInfo)
|
assignEntryInfo(templateEntry.entryInfo)
|
||||||
}
|
}
|
||||||
// To prevent flickering
|
// To prevent flickering
|
||||||
rootView.showByFading()
|
rootView.showByFading()
|
||||||
|
|||||||
@@ -219,6 +219,8 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
activity?.intent?.let {
|
activity?.intent?.let {
|
||||||
specialMode = EntrySelectionHelper.retrieveSpecialModeFromIntent(it)
|
specialMode = EntrySelectionHelper.retrieveSpecialModeFromIntent(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rebuildList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
|
|||||||
// Focus view to reinitialize timeout,
|
// Focus view to reinitialize timeout,
|
||||||
// view is not necessary loaded so retry later in resume
|
// view is not necessary loaded so retry later in resume
|
||||||
viewToInvalidateTimeout()
|
viewToInvalidateTimeout()
|
||||||
?.resetAppTimeoutWhenViewFocusedOrChanged(this, database?.loaded)
|
?.resetAppTimeoutWhenViewTouchedOrFocused(this, database?.loaded)
|
||||||
|
|
||||||
database?.let {
|
database?.let {
|
||||||
// check timeout
|
// check timeout
|
||||||
@@ -383,7 +383,7 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
|
|||||||
// Invalidate timeout by touch
|
// Invalidate timeout by touch
|
||||||
mDatabase?.let { database ->
|
mDatabase?.let { database ->
|
||||||
viewToInvalidateTimeout()
|
viewToInvalidateTimeout()
|
||||||
?.resetAppTimeoutWhenViewFocusedOrChanged(this, database.loaded)
|
?.resetAppTimeoutWhenViewTouchedOrFocused(this, database.loaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
@@ -422,6 +422,11 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
|
|||||||
sendBroadcast(Intent(LOCK_ACTION))
|
sendBroadcast(Intent(LOCK_ACTION))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resetAppTimeout() {
|
||||||
|
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this,
|
||||||
|
mDatabase?.loaded ?: false)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if (mTimeoutEnable) {
|
if (mTimeoutEnable) {
|
||||||
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this,
|
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this,
|
||||||
@@ -451,12 +456,12 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
|
|||||||
* To reset the app timeout when a view is focused or changed
|
* To reset the app timeout when a view is focused or changed
|
||||||
*/
|
*/
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
fun View.resetAppTimeoutWhenViewFocusedOrChanged(context: Context, databaseLoaded: Boolean?) {
|
fun View.resetAppTimeoutWhenViewTouchedOrFocused(context: Context, databaseLoaded: Boolean?) {
|
||||||
// Log.d(LockingActivity.TAG, "View prepared to reset app timeout")
|
// Log.d(DatabaseLockActivity.TAG, "View prepared to reset app timeout")
|
||||||
setOnTouchListener { _, event ->
|
setOnTouchListener { _, event ->
|
||||||
when (event.action) {
|
when (event.action) {
|
||||||
MotionEvent.ACTION_DOWN -> {
|
MotionEvent.ACTION_DOWN -> {
|
||||||
// Log.d(LockingActivity.TAG, "View touched, try to reset app timeout")
|
// Log.d(DatabaseLockActivity.TAG, "View touched, try to reset app timeout")
|
||||||
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(context,
|
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(context,
|
||||||
databaseLoaded ?: false)
|
databaseLoaded ?: false)
|
||||||
}
|
}
|
||||||
@@ -464,13 +469,13 @@ fun View.resetAppTimeoutWhenViewFocusedOrChanged(context: Context, databaseLoade
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
setOnFocusChangeListener { _, _ ->
|
setOnFocusChangeListener { _, _ ->
|
||||||
// Log.d(LockingActivity.TAG, "View focused, try to reset app timeout")
|
// Log.d(DatabaseLockActivity.TAG, "View focused, try to reset app timeout")
|
||||||
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(context,
|
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(context,
|
||||||
databaseLoaded ?: false)
|
databaseLoaded ?: false)
|
||||||
}
|
}
|
||||||
if (this is ViewGroup) {
|
if (this is ViewGroup) {
|
||||||
for (i in 0..childCount) {
|
for (i in 0..childCount) {
|
||||||
getChildAt(i)?.resetAppTimeoutWhenViewFocusedOrChanged(context, databaseLoaded)
|
getChildAt(i)?.resetAppTimeoutWhenViewTouchedOrFocused(context, databaseLoaded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class EntryHistoryAdapter(val context: Context) : RecyclerView.Adapter<EntryHist
|
|||||||
holder.lastModifiedView.text = entryHistory.lastModificationTime.getDateTimeString(context.resources)
|
holder.lastModifiedView.text = entryHistory.lastModificationTime.getDateTimeString(context.resources)
|
||||||
holder.titleView.text = entryHistory.title
|
holder.titleView.text = entryHistory.title
|
||||||
holder.usernameView.text = entryHistory.username
|
holder.usernameView.text = entryHistory.username
|
||||||
holder.urlView.text = entryHistory.url
|
|
||||||
|
|
||||||
holder.itemView.setOnClickListener {
|
holder.itemView.setOnClickListener {
|
||||||
onItemClickListener?.invoke(entryHistory, position)
|
onItemClickListener?.invoke(entryHistory, position)
|
||||||
@@ -64,6 +63,5 @@ class EntryHistoryAdapter(val context: Context) : RecyclerView.Adapter<EntryHist
|
|||||||
var lastModifiedView: TextView = itemView.findViewById(R.id.entry_history_last_modified)
|
var lastModifiedView: TextView = itemView.findViewById(R.id.entry_history_last_modified)
|
||||||
var titleView: TextView = itemView.findViewById(R.id.entry_history_title)
|
var titleView: TextView = itemView.findViewById(R.id.entry_history_title)
|
||||||
var usernameView: TextView = itemView.findViewById(R.id.entry_history_username)
|
var usernameView: TextView = itemView.findViewById(R.id.entry_history_username)
|
||||||
var urlView: TextView = itemView.findViewById(R.id.entry_history_url)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,7 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
@@ -40,6 +41,8 @@ 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.NodeVersionedInterface
|
import com.kunzisoft.keepass.database.element.node.NodeVersionedInterface
|
||||||
import com.kunzisoft.keepass.database.element.node.Type
|
import com.kunzisoft.keepass.database.element.node.Type
|
||||||
|
import com.kunzisoft.keepass.otp.OtpElement
|
||||||
|
import com.kunzisoft.keepass.otp.OtpType
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.view.setTextSize
|
import com.kunzisoft.keepass.view.setTextSize
|
||||||
import com.kunzisoft.keepass.view.strikeOut
|
import com.kunzisoft.keepass.view.strikeOut
|
||||||
@@ -68,6 +71,7 @@ class NodeAdapter (private val context: Context,
|
|||||||
|
|
||||||
private var mShowUserNames: Boolean = true
|
private var mShowUserNames: Boolean = true
|
||||||
private var mShowNumberEntries: Boolean = true
|
private var mShowNumberEntries: Boolean = true
|
||||||
|
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>()
|
||||||
|
|
||||||
@@ -122,6 +126,7 @@ class NodeAdapter (private val context: Context,
|
|||||||
|
|
||||||
this.mShowUserNames = PreferencesUtil.showUsernamesListEntries(context)
|
this.mShowUserNames = PreferencesUtil.showUsernamesListEntries(context)
|
||||||
this.mShowNumberEntries = PreferencesUtil.showNumberEntries(context)
|
this.mShowNumberEntries = PreferencesUtil.showNumberEntries(context)
|
||||||
|
this.mShowOTP = PreferencesUtil.showOTPToken(context)
|
||||||
this.mShowUUID = PreferencesUtil.showUUID(context)
|
this.mShowUUID = PreferencesUtil.showUUID(context)
|
||||||
|
|
||||||
this.mEntryFilters = Group.ChildFilter.getDefaults(context)
|
this.mEntryFilters = Group.ChildFilter.getDefaults(context)
|
||||||
@@ -148,6 +153,7 @@ class NodeAdapter (private val context: Context,
|
|||||||
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.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
|
||||||
@@ -357,6 +363,25 @@ class NodeAdapter (private val context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val otpElement = entry.getOtpElement()
|
||||||
|
holder.otpContainer?.removeCallbacks(holder.otpRunnable)
|
||||||
|
if (otpElement != null
|
||||||
|
&& mShowOTP
|
||||||
|
&& otpElement.token.isNotEmpty()) {
|
||||||
|
|
||||||
|
// Execute runnable to show progress
|
||||||
|
holder.otpRunnable.action = {
|
||||||
|
populateOtpView(holder, otpElement)
|
||||||
|
}
|
||||||
|
if (otpElement.type == OtpType.TOTP) {
|
||||||
|
holder.otpRunnable.postDelayed()
|
||||||
|
}
|
||||||
|
populateOtpView(holder, otpElement)
|
||||||
|
|
||||||
|
holder.otpContainer?.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
holder.otpContainer?.visibility = View.GONE
|
||||||
|
}
|
||||||
holder.attachmentIcon?.visibility =
|
holder.attachmentIcon?.visibility =
|
||||||
if (entry.containsAttachment()) View.VISIBLE else View.GONE
|
if (entry.containsAttachment()) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
@@ -386,7 +411,41 @@ class NodeAdapter (private val context: Context,
|
|||||||
mNodeClickCallback?.onNodeLongClick(database, subNode) ?: false
|
mNodeClickCallback?.onNodeLongClick(database, subNode) ?: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun populateOtpView(holder: NodeViewHolder?, otpElement: OtpElement?) {
|
||||||
|
when (otpElement?.type) {
|
||||||
|
OtpType.HOTP -> {
|
||||||
|
holder?.otpCounter?.text = otpElement.counter.toString()
|
||||||
|
holder?.otpProgress?.apply {
|
||||||
|
max = 100
|
||||||
|
progress = 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OtpType.TOTP -> {
|
||||||
|
holder?.otpCounter?.text = otpElement.secondsRemaining.toString()
|
||||||
|
holder?.otpProgress?.apply {
|
||||||
|
max = otpElement.period
|
||||||
|
progress = otpElement.secondsRemaining
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
holder?.otpToken?.text = otpElement?.token
|
||||||
|
}
|
||||||
|
|
||||||
|
class OtpRunnable(val view: View?): Runnable {
|
||||||
|
|
||||||
|
var action: (() -> Unit)? = null
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
action?.invoke()
|
||||||
|
postDelayed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun postDelayed() {
|
||||||
|
view?.postDelayed(this, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return mNodeSortedList.size()
|
return mNodeSortedList.size()
|
||||||
}
|
}
|
||||||
@@ -413,6 +472,11 @@ 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 otpContainer: ViewGroup? = itemView.findViewById(R.id.node_otp_container)
|
||||||
|
var otpProgress: ProgressBar? = itemView.findViewById(R.id.node_otp_progress)
|
||||||
|
var otpCounter: TextView? = itemView.findViewById(R.id.node_otp_counter)
|
||||||
|
var otpToken: TextView? = itemView.findViewById(R.id.node_otp_token)
|
||||||
|
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)
|
||||||
var attachmentIcon: ImageView? = itemView.findViewById(R.id.node_attachment_icon)
|
var attachmentIcon: ImageView? = itemView.findViewById(R.id.node_attachment_icon)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class OtpModel() : Parcelable {
|
|||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
other as OtpElement
|
other as OtpModel
|
||||||
|
|
||||||
if (type != other.type) return false
|
if (type != other.type) return false
|
||||||
// Token type is important only if it's a TOTP
|
// Token type is important only if it's a TOTP
|
||||||
|
|||||||
@@ -185,6 +185,19 @@ data class OtpElement(var otpModel: OtpModel = OtpModel()) {
|
|||||||
return secondsRemaining == otpModel.period
|
return secondsRemaining == otpModel.period
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is OtpElement) return false
|
||||||
|
|
||||||
|
if (otpModel != other.otpModel) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return otpModel.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MIN_HOTP_COUNTER = 0
|
const val MIN_HOTP_COUNTER = 0
|
||||||
const val MAX_HOTP_COUNTER = Long.MAX_VALUE
|
const val MAX_HOTP_COUNTER = Long.MAX_VALUE
|
||||||
|
|||||||
@@ -454,7 +454,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||||
// To reload group when appearence settings are modified
|
// To reload group when appearance settings are modified
|
||||||
when (preference?.key) {
|
when (preference?.key) {
|
||||||
getString(R.string.setting_style_key),
|
getString(R.string.setting_style_key),
|
||||||
getString(R.string.setting_style_brightness_key),
|
getString(R.string.setting_style_brightness_key),
|
||||||
@@ -464,6 +464,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
getString(R.string.list_size_key),
|
getString(R.string.list_size_key),
|
||||||
getString(R.string.monospace_font_fields_enable_key),
|
getString(R.string.monospace_font_fields_enable_key),
|
||||||
getString(R.string.hide_expired_entries_key),
|
getString(R.string.hide_expired_entries_key),
|
||||||
|
getString(R.string.show_otp_token_key),
|
||||||
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) -> {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ 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.legacy.DatabaseRetrieval
|
||||||
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewFocusedOrChanged
|
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
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
@@ -74,7 +74,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
|
|||||||
|
|
||||||
mDatabaseViewModel.database.observe(viewLifecycleOwner) { database ->
|
mDatabaseViewModel.database.observe(viewLifecycleOwner) { database ->
|
||||||
mDatabase = database
|
mDatabase = database
|
||||||
view.resetAppTimeoutWhenViewFocusedOrChanged(requireContext(), database?.loaded)
|
view.resetAppTimeoutWhenViewTouchedOrFocused(requireContext(), database?.loaded)
|
||||||
onDatabaseRetrieved(database)
|
onDatabaseRetrieved(database)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,12 @@ object PreferencesUtil {
|
|||||||
context.resources.getBoolean(R.bool.hide_expired_entries_default))
|
context.resources.getBoolean(R.bool.hide_expired_entries_default))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showOTPToken(context: Context): Boolean {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
return prefs.getBoolean(context.getString(R.string.show_otp_token_key),
|
||||||
|
context.resources.getBoolean(R.bool.show_otp_token_default))
|
||||||
|
}
|
||||||
|
|
||||||
fun showUUID(context: Context): Boolean {
|
fun showUUID(context: Context): Boolean {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
return prefs.getBoolean(context.getString(R.string.show_uuid_key),
|
return prefs.getBoolean(context.getString(R.string.show_uuid_key),
|
||||||
@@ -644,6 +650,7 @@ object PreferencesUtil {
|
|||||||
context.getString(R.string.list_size_key) -> editor.putString(name, value)
|
context.getString(R.string.list_size_key) -> editor.putString(name, value)
|
||||||
context.getString(R.string.monospace_font_fields_enable_key) -> editor.putBoolean(name, value.toBoolean())
|
context.getString(R.string.monospace_font_fields_enable_key) -> editor.putBoolean(name, value.toBoolean())
|
||||||
context.getString(R.string.hide_expired_entries_key) -> editor.putBoolean(name, value.toBoolean())
|
context.getString(R.string.hide_expired_entries_key) -> editor.putBoolean(name, value.toBoolean())
|
||||||
|
context.getString(R.string.show_otp_token_key) -> editor.putBoolean(name, value.toBoolean())
|
||||||
context.getString(R.string.show_uuid_key) -> editor.putBoolean(name, value.toBoolean())
|
context.getString(R.string.show_uuid_key) -> editor.putBoolean(name, value.toBoolean())
|
||||||
context.getString(R.string.enable_education_screens_key) -> editor.putBoolean(name, value.toBoolean())
|
context.getString(R.string.enable_education_screens_key) -> editor.putBoolean(name, value.toBoolean())
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package com.kunzisoft.keepass.timeout
|
package com.kunzisoft.keepass.timeout
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -30,6 +29,7 @@ import android.text.method.LinkMovementMethod
|
|||||||
import android.text.util.Linkify
|
import android.text.util.Linkify
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.exception.ClipboardException
|
import com.kunzisoft.keepass.database.exception.ClipboardException
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
|
|||||||
@@ -239,8 +239,23 @@ object UriUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openExternalApp(context: Context, packageName: String) {
|
fun openExternalApp(context: Context, packageName: String) {
|
||||||
|
var launchIntent: Intent? = null
|
||||||
try {
|
try {
|
||||||
context.startActivity(context.applicationContext.packageManager.getLaunchIntentForPackage(packageName))
|
launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)?.apply {
|
||||||
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
}
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (launchIntent == null) {
|
||||||
|
context.startActivity(
|
||||||
|
Intent(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
.setData(Uri.parse("https://play.google.com/store/apps/details?id=$packageName"))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
context.startActivity(launchIntent)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "App cannot be open", e)
|
Log.e(TAG, "App cannot be open", e)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ abstract class TemplateAbstractView<
|
|||||||
private var mTemplate: Template? = null
|
private var mTemplate: Template? = null
|
||||||
protected var mEntryInfo: EntryInfo? = null
|
protected var mEntryInfo: EntryInfo? = null
|
||||||
|
|
||||||
protected var mCustomFieldIds = mutableListOf<FieldId>()
|
private var mViewFields = mutableListOf<ViewField>()
|
||||||
|
|
||||||
protected var mFontInVisibility: Boolean = PreferencesUtil.fieldFontIsInVisibility(context)
|
protected var mFontInVisibility: Boolean = PreferencesUtil.fieldFontIsInVisibility(context)
|
||||||
protected var mHideProtectedValue: Boolean = PreferencesUtil.hideProtectedValue(context)
|
protected var mHideProtectedValue: Boolean = PreferencesUtil.hideProtectedValue(context)
|
||||||
@@ -48,7 +48,7 @@ abstract class TemplateAbstractView<
|
|||||||
protected var entryIconView: ImageView
|
protected var entryIconView: ImageView
|
||||||
private var titleContainerView: ViewGroup
|
private var titleContainerView: ViewGroup
|
||||||
protected var templateContainerView: ViewGroup
|
protected var templateContainerView: ViewGroup
|
||||||
protected var customFieldsContainerView: SectionView
|
private var customFieldsContainerView: SectionView
|
||||||
private var notReferencedFieldsContainerView: SectionView
|
private var notReferencedFieldsContainerView: SectionView
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -95,7 +95,7 @@ abstract class TemplateAbstractView<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildTemplate() {
|
private fun buildTemplate() {
|
||||||
// Retrieve preferences
|
// Retrieve preferences
|
||||||
mHideProtectedValue = PreferencesUtil.hideProtectedValue(context)
|
mHideProtectedValue = PreferencesUtil.hideProtectedValue(context)
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ abstract class TemplateAbstractView<
|
|||||||
templateContainerView.removeAllViews()
|
templateContainerView.removeAllViews()
|
||||||
customFieldsContainerView.removeAllViews()
|
customFieldsContainerView.removeAllViews()
|
||||||
notReferencedFieldsContainerView.removeAllViews()
|
notReferencedFieldsContainerView.removeAllViews()
|
||||||
mCustomFieldIds.clear()
|
mViewFields.clear()
|
||||||
|
|
||||||
mTemplate?.let { template ->
|
mTemplate?.let { template ->
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ abstract class TemplateAbstractView<
|
|||||||
TemplateAttributeType.TEXT,
|
TemplateAttributeType.TEXT,
|
||||||
field.protectedValue.isProtected,
|
field.protectedValue.isProtected,
|
||||||
TemplateAttributeOption().apply {
|
TemplateAttributeOption().apply {
|
||||||
setNumberLines(3)
|
setNumberLines(20)
|
||||||
},
|
},
|
||||||
TemplateAttributeAction.CUSTOM_EDITION
|
TemplateAttributeAction.CUSTOM_EDITION
|
||||||
).apply {
|
).apply {
|
||||||
@@ -231,13 +231,13 @@ abstract class TemplateAbstractView<
|
|||||||
|
|
||||||
// Add new custom view id to the custom field list
|
// Add new custom view id to the custom field list
|
||||||
if (fieldTag == FIELD_CUSTOM_TAG) {
|
if (fieldTag == FIELD_CUSTOM_TAG) {
|
||||||
val indexOldItem = indexCustomFieldIdByName(field.name)
|
val indexOldItem = getIndexViewFieldByName(field.name)
|
||||||
if (indexOldItem >= 0)
|
if (indexOldItem >= 0)
|
||||||
mCustomFieldIds.removeAt(indexOldItem)
|
mViewFields.removeAt(indexOldItem)
|
||||||
if (itemView?.id != null) {
|
if (itemView?.id != null) {
|
||||||
mCustomFieldIds.add(
|
mViewFields.add(
|
||||||
FieldId(
|
ViewField(
|
||||||
itemView.id,
|
itemView,
|
||||||
field
|
field
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -320,7 +320,7 @@ abstract class TemplateAbstractView<
|
|||||||
/**
|
/**
|
||||||
* Return empty custom fields
|
* Return empty custom fields
|
||||||
*/
|
*/
|
||||||
protected open fun populateViewsWithEntryInfo(showEmptyFields: Boolean): List<FieldId> {
|
protected open fun populateViewsWithEntryInfo(showEmptyFields: Boolean): List<ViewField> {
|
||||||
mEntryInfo?.let { entryInfo ->
|
mEntryInfo?.let { entryInfo ->
|
||||||
|
|
||||||
populateEntryFieldView(FIELD_TITLE_TAG,
|
populateEntryFieldView(FIELD_TITLE_TAG,
|
||||||
@@ -350,15 +350,14 @@ abstract class TemplateAbstractView<
|
|||||||
showEmptyFields)
|
showEmptyFields)
|
||||||
|
|
||||||
customFieldsContainerView.removeAllViews()
|
customFieldsContainerView.removeAllViews()
|
||||||
val emptyCustomFields = mutableListOf<FieldId>().also { it.addAll(mCustomFieldIds) }
|
val emptyCustomFields = mutableListOf<ViewField>().also { it.addAll(mViewFields) }
|
||||||
entryInfo.customFields.forEach { customField ->
|
entryInfo.customFields.forEach { customField ->
|
||||||
val indexFieldViewId = indexCustomFieldIdByName(customField.name)
|
val indexFieldViewId = getIndexViewFieldByName(customField.name)
|
||||||
if (indexFieldViewId >= 0) {
|
if (indexFieldViewId >= 0) {
|
||||||
// Template contains the custom view
|
// Template contains the custom view
|
||||||
val customFieldId = mCustomFieldIds[indexFieldViewId]
|
val viewField = mViewFields[indexFieldViewId]
|
||||||
emptyCustomFields.remove(customFieldId)
|
emptyCustomFields.remove(viewField)
|
||||||
templateContainerView.findViewById<View>(customFieldId.viewId)
|
viewField.view.let { customView ->
|
||||||
?.let { customView ->
|
|
||||||
if (customView is GenericTextFieldView) {
|
if (customView is GenericTextFieldView) {
|
||||||
customView.value = customField.protectedValue.stringValue
|
customView.value = customField.protectedValue.stringValue
|
||||||
customView.applyFontVisibility(mFontInVisibility)
|
customView.applyFontVisibility(mFontInVisibility)
|
||||||
@@ -459,22 +458,22 @@ abstract class TemplateAbstractView<
|
|||||||
* -------------
|
* -------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected data class FieldId(var viewId: Int, var field: Field)
|
protected data class ViewField(var view: View, var field: Field)
|
||||||
|
|
||||||
private fun isStandardFieldName(name: String): Boolean {
|
private fun isStandardFieldName(name: String): Boolean {
|
||||||
return TemplateField.isStandardFieldName(name)
|
return TemplateField.isStandardFieldName(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun customFieldIdByName(name: String): FieldId? {
|
protected fun getViewFieldByName(name: String): ViewField? {
|
||||||
return mCustomFieldIds.find { it.field.name.equals(name, true) }
|
return mViewFields.find { it.field.name.equals(name, true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun indexCustomFieldIdByName(name: String): Int {
|
private fun getIndexViewFieldByName(name: String): Int {
|
||||||
return mCustomFieldIds.indexOfFirst { it.field.name.equals(name, true) }
|
return mViewFields.indexOfFirst { it.field.name.equals(name, true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun retrieveCustomFieldsFromView(templateFieldNotEmpty: Boolean = false) {
|
private fun retrieveCustomFieldsFromView(templateFieldNotEmpty: Boolean = false) {
|
||||||
mEntryInfo?.customFields = mCustomFieldIds.mapNotNull {
|
mEntryInfo?.customFields = mViewFields.mapNotNull {
|
||||||
getCustomField(it.field.name, templateFieldNotEmpty)
|
getCustomField(it.field.name, templateFieldNotEmpty)
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
}
|
}
|
||||||
@@ -484,10 +483,9 @@ abstract class TemplateAbstractView<
|
|||||||
?: Field(fieldName, ProtectedString(false))
|
?: Field(fieldName, ProtectedString(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun getCustomField(fieldName: String, templateFieldNotEmpty: Boolean): Field? {
|
private fun getCustomField(fieldName: String, templateFieldNotEmpty: Boolean): Field? {
|
||||||
customFieldIdByName(fieldName)?.let { fieldId ->
|
getViewFieldByName(fieldName)?.let { fieldId ->
|
||||||
val editView: View? = templateContainerView.findViewById(fieldId.viewId)
|
val editView: View? = fieldId.view
|
||||||
?: customFieldsContainerView.findViewById(fieldId.viewId)
|
|
||||||
if (editView is GenericFieldView) {
|
if (editView is GenericFieldView) {
|
||||||
// Do not return field with a default value
|
// Do not return field with a default value
|
||||||
val defaultViewValue = if (editView.value == editView.default) "" else editView.value
|
val defaultViewValue = if (editView.value == editView.default) "" else editView.value
|
||||||
@@ -514,20 +512,22 @@ abstract class TemplateAbstractView<
|
|||||||
|
|
||||||
return if (!isStandardFieldName(customField.name)) {
|
return if (!isStandardFieldName(customField.name)) {
|
||||||
customFieldsContainerView.visibility = View.VISIBLE
|
customFieldsContainerView.visibility = View.VISIBLE
|
||||||
if (indexCustomFieldIdByName(customField.name) >= 0) {
|
if (getIndexViewFieldByName(customField.name) >= 0) {
|
||||||
replaceCustomField(customField, customField, focus)
|
// Update a custom field with a new value,
|
||||||
|
// new field name must be the same as old field name
|
||||||
|
replaceCustomField(customField, customField, false, focus)
|
||||||
} else {
|
} else {
|
||||||
val newCustomView = buildViewForCustomField(customField)
|
val newCustomView = buildViewForCustomField(customField)
|
||||||
newCustomView?.let {
|
newCustomView?.let {
|
||||||
customFieldsContainerView.addView(newCustomView)
|
customFieldsContainerView.addView(newCustomView)
|
||||||
val fieldId = FieldId(
|
val fieldId = ViewField(
|
||||||
newCustomView.id,
|
newCustomView,
|
||||||
customField
|
customField
|
||||||
)
|
)
|
||||||
val indexOldItem = indexCustomFieldIdByName(fieldId.field.name)
|
val indexOldItem = getIndexViewFieldByName(fieldId.field.name)
|
||||||
if (indexOldItem >= 0)
|
if (indexOldItem >= 0)
|
||||||
mCustomFieldIds.removeAt(indexOldItem)
|
mViewFields.removeAt(indexOldItem)
|
||||||
mCustomFieldIds.add(indexOldItem, fieldId)
|
mViewFields.add(indexOldItem, fieldId)
|
||||||
if (focus)
|
if (focus)
|
||||||
newCustomView.requestFocus()
|
newCustomView.requestFocus()
|
||||||
}
|
}
|
||||||
@@ -544,58 +544,63 @@ abstract class TemplateAbstractView<
|
|||||||
return put
|
return put
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun replaceCustomField(oldField: Field,
|
||||||
* Update a custom field and keep the old value
|
newField: Field,
|
||||||
*/
|
keepOldValue: Boolean,
|
||||||
private fun replaceCustomField(oldField: Field, newField: Field, focus: Boolean): Boolean {
|
focus: Boolean): Boolean {
|
||||||
if (!isStandardFieldName(newField.name)) {
|
if (!isStandardFieldName(newField.name)) {
|
||||||
customFieldIdByName(oldField.name)?.viewId?.let { viewId ->
|
getViewFieldByName(oldField.name)?.view?.let { viewToReplace ->
|
||||||
customFieldsContainerView.findViewById<View>(viewId)?.let { viewToReplace ->
|
val oldValue = getCustomField(oldField.name).protectedValue.toString()
|
||||||
val oldValue = getCustomField(oldField.name).protectedValue.toString()
|
|
||||||
|
|
||||||
val parentGroup = viewToReplace.parent as ViewGroup
|
val parentGroup = viewToReplace.parent as ViewGroup
|
||||||
val indexInParent = parentGroup.indexOfChild(viewToReplace)
|
val indexInParent = parentGroup.indexOfChild(viewToReplace)
|
||||||
parentGroup.removeView(viewToReplace)
|
parentGroup.removeView(viewToReplace)
|
||||||
|
|
||||||
val newCustomFieldWithValue = Field(newField.name,
|
val newCustomFieldWithValue = if (keepOldValue)
|
||||||
ProtectedString(newField.protectedValue.isProtected, oldValue))
|
Field(newField.name,
|
||||||
val oldPosition = indexCustomFieldIdByName(oldField.name)
|
ProtectedString(newField.protectedValue.isProtected, oldValue)
|
||||||
if (oldPosition >= 0)
|
)
|
||||||
mCustomFieldIds.removeAt(oldPosition)
|
else
|
||||||
|
newField
|
||||||
|
val oldPosition = getIndexViewFieldByName(oldField.name)
|
||||||
|
if (oldPosition >= 0)
|
||||||
|
mViewFields.removeAt(oldPosition)
|
||||||
|
|
||||||
val newCustomView = buildViewForCustomField(newCustomFieldWithValue)
|
val newCustomView = buildViewForCustomField(newCustomFieldWithValue)
|
||||||
newCustomView?.let {
|
newCustomView?.let {
|
||||||
parentGroup.addView(newCustomView, indexInParent)
|
parentGroup.addView(newCustomView, indexInParent)
|
||||||
mCustomFieldIds.add(
|
mViewFields.add(
|
||||||
oldPosition,
|
oldPosition,
|
||||||
FieldId(
|
ViewField(
|
||||||
newCustomView.id,
|
newCustomView,
|
||||||
newCustomFieldWithValue
|
newCustomFieldWithValue
|
||||||
)
|
|
||||||
)
|
)
|
||||||
if (focus)
|
)
|
||||||
newCustomView.requestFocus()
|
if (focus)
|
||||||
}
|
newCustomView.requestFocus()
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a custom field and keep the old value
|
||||||
|
*/
|
||||||
fun replaceCustomField(oldField: Field, newField: Field): Boolean {
|
fun replaceCustomField(oldField: Field, newField: Field): Boolean {
|
||||||
val replace = replaceCustomField(oldField, newField, true)
|
val replace = replaceCustomField(oldField, newField, keepOldValue = true, focus = true)
|
||||||
retrieveCustomFieldsFromView()
|
retrieveCustomFieldsFromView()
|
||||||
return replace
|
return replace
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeCustomField(oldCustomField: Field) {
|
fun removeCustomField(oldCustomField: Field) {
|
||||||
val indexOldField = indexCustomFieldIdByName(oldCustomField.name)
|
val indexOldField = getIndexViewFieldByName(oldCustomField.name)
|
||||||
if (indexOldField >= 0) {
|
if (indexOldField >= 0) {
|
||||||
mCustomFieldIds[indexOldField].viewId.let { viewId ->
|
mViewFields[indexOldField].let { fieldView ->
|
||||||
customFieldsContainerView.removeViewById(viewId)
|
customFieldsContainerView.removeViewById(fieldView.view.id)
|
||||||
}
|
}
|
||||||
mCustomFieldIds.removeAt(indexOldField)
|
mViewFields.removeAt(indexOldField)
|
||||||
}
|
}
|
||||||
retrieveCustomFieldsFromView()
|
retrieveCustomFieldsFromView()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ class TemplateEditView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun populateViewsWithEntryInfo(showEmptyFields: Boolean): List<FieldId> {
|
override fun populateViewsWithEntryInfo(showEmptyFields: Boolean): List<ViewField> {
|
||||||
refreshIcon()
|
refreshIcon()
|
||||||
return super.populateViewsWithEntryInfo(showEmptyFields)
|
return super.populateViewsWithEntryInfo(showEmptyFields)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,18 +56,20 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
setMaxLines(templateAttribute.options.getNumberLines())
|
setMaxLines(templateAttribute.options.getNumberLines())
|
||||||
// TODO Linkify
|
// TODO Linkify
|
||||||
value = field.protectedValue.stringValue
|
value = field.protectedValue.stringValue
|
||||||
|
// Here the value is often empty
|
||||||
|
|
||||||
if (field.protectedValue.isProtected) {
|
if (field.protectedValue.isProtected) {
|
||||||
if (mFirstTimeAskAllowCopyProtectedFields) {
|
if (mFirstTimeAskAllowCopyProtectedFields) {
|
||||||
setCopyButtonState(TextFieldView.ButtonState.DEACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.DEACTIVATE)
|
||||||
setCopyButtonClickListener {
|
setCopyButtonClickListener { _, _ ->
|
||||||
mOnAskCopySafeClickListener?.invoke()
|
mOnAskCopySafeClickListener?.invoke()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (mAllowCopyProtectedFields) {
|
if (mAllowCopyProtectedFields) {
|
||||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||||
setCopyButtonClickListener {
|
setCopyButtonClickListener { label, value ->
|
||||||
mOnCopyActionClickListener?.invoke(field)
|
mOnCopyActionClickListener
|
||||||
|
?.invoke(Field(label, ProtectedString(false, value)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setCopyButtonState(TextFieldView.ButtonState.GONE)
|
setCopyButtonState(TextFieldView.ButtonState.GONE)
|
||||||
@@ -76,8 +78,9 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||||
setCopyButtonClickListener {
|
setCopyButtonClickListener { label, value ->
|
||||||
mOnCopyActionClickListener?.invoke(field)
|
mOnCopyActionClickListener
|
||||||
|
?.invoke(Field(label, ProtectedString(false, value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,22 +117,22 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
return findViewWithTag<TextFieldView?>(FIELD_PASSWORD_TAG)?.getCopyButtonView()
|
return findViewWithTag<TextFieldView?>(FIELD_PASSWORD_TAG)?.getCopyButtonView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun populateViewsWithEntryInfo(showEmptyFields: Boolean): List<FieldId> {
|
override fun populateViewsWithEntryInfo(showEmptyFields: Boolean): List<ViewField> {
|
||||||
val emptyCustomFields = super.populateViewsWithEntryInfo(false)
|
val emptyCustomFields = super.populateViewsWithEntryInfo(false)
|
||||||
|
|
||||||
// Hide empty custom fields
|
// Hide empty custom fields
|
||||||
emptyCustomFields.forEach { customFieldId ->
|
emptyCustomFields.forEach { customFieldId ->
|
||||||
templateContainerView.findViewById<View?>(customFieldId.viewId)
|
customFieldId.view.isVisible = false
|
||||||
?.isVisible = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeOtpRunnable()
|
||||||
mEntryInfo?.let { entryInfo ->
|
mEntryInfo?.let { entryInfo ->
|
||||||
// Assign specific OTP dynamic view
|
// Assign specific OTP dynamic view
|
||||||
removeOtpRunnable()
|
|
||||||
entryInfo.otpModel?.let {
|
entryInfo.otpModel?.let {
|
||||||
assignOtp(it)
|
assignOtp(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return emptyCustomFields
|
return emptyCustomFields
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,11 +149,10 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
private var mOnOtpElementUpdated: ((OtpElement?) -> Unit)? = null
|
private var mOnOtpElementUpdated: ((OtpElement?) -> Unit)? = null
|
||||||
|
|
||||||
private fun getOtpTokenView(): TextFieldView? {
|
private fun getOtpTokenView(): TextFieldView? {
|
||||||
val indexFieldViewId = indexCustomFieldIdByName(OTP_TOKEN_FIELD)
|
getViewFieldByName(OTP_TOKEN_FIELD)?.let { viewField ->
|
||||||
if (indexFieldViewId >= 0) {
|
val view = viewField.view
|
||||||
// Template contains the custom view
|
if (view is TextFieldView)
|
||||||
val customFieldId = mCustomFieldIds[indexFieldViewId]
|
return view
|
||||||
return findViewById(customFieldId.viewId)
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -166,7 +168,7 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
label = otpElement.type.name
|
label = otpElement.type.name
|
||||||
value = otpElement.token
|
value = otpElement.token
|
||||||
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
|
||||||
setCopyButtonClickListener {
|
setCopyButtonClickListener { _, _ ->
|
||||||
mOnCopyActionClickListener?.invoke(Field(
|
mOnCopyActionClickListener?.invoke(Field(
|
||||||
otpElement.type.name,
|
otpElement.type.name,
|
||||||
ProtectedString(false, otpElement.token)))
|
ProtectedString(false, otpElement.token)))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.kunzisoft.keepass.view
|
package com.kunzisoft.keepass.view
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
import android.text.InputFilter
|
import android.text.InputFilter
|
||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@@ -41,11 +42,11 @@ class TextEditFieldView @JvmOverloads constructor(context: Context,
|
|||||||
LayoutParams.MATCH_PARENT,
|
LayoutParams.MATCH_PARENT,
|
||||||
LayoutParams.WRAP_CONTENT)
|
LayoutParams.WRAP_CONTENT)
|
||||||
inputType = EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
inputType = EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
imeOptions = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
|
imeOptions = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
|
||||||
importantForAutofill = IMPORTANT_FOR_AUTOFILL_NO
|
importantForAutofill = IMPORTANT_FOR_AUTOFILL_NO
|
||||||
}
|
}
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO
|
importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||||
}
|
}
|
||||||
maxLines = 1
|
maxLines = 1
|
||||||
@@ -61,7 +62,7 @@ class TextEditFieldView @JvmOverloads constructor(context: Context,
|
|||||||
resources.displayMetrics
|
resources.displayMetrics
|
||||||
).toInt()
|
).toInt()
|
||||||
it.addRule(ALIGN_PARENT_RIGHT)
|
it.addRule(ALIGN_PARENT_RIGHT)
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
it.addRule(ALIGN_PARENT_END)
|
it.addRule(ALIGN_PARENT_END)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +83,7 @@ class TextEditFieldView @JvmOverloads constructor(context: Context,
|
|||||||
id = labelViewId
|
id = labelViewId
|
||||||
layoutParams = (layoutParams as LayoutParams?).also {
|
layoutParams = (layoutParams as LayoutParams?).also {
|
||||||
it?.addRule(LEFT_OF, actionImageButtonId)
|
it?.addRule(LEFT_OF, actionImageButtonId)
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
it?.addRule(START_OF, actionImageButtonId)
|
it?.addRule(START_OF, actionImageButtonId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
* Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||||
*
|
*
|
||||||
* This file is part of KeePassDX.
|
* This file is part of KeePassDX.
|
||||||
*
|
*
|
||||||
@@ -20,16 +20,20 @@
|
|||||||
package com.kunzisoft.keepass.view
|
package com.kunzisoft.keepass.view
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
import android.text.InputFilter
|
import android.text.InputFilter
|
||||||
import android.text.util.Linkify
|
import android.text.util.Linkify
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.util.TypedValue
|
||||||
|
import android.view.ContextThemeWrapper
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.RelativeLayout
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.text.util.LinkifyCompat
|
import androidx.core.text.util.LinkifyCompat
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.model.EntryInfo.Companion.APPLICATION_ID_FIELD_NAME
|
import com.kunzisoft.keepass.model.EntryInfo.Companion.APPLICATION_ID_FIELD_NAME
|
||||||
@@ -39,22 +43,130 @@ import com.kunzisoft.keepass.utils.UriUtil
|
|||||||
class TextFieldView @JvmOverloads constructor(context: Context,
|
class TextFieldView @JvmOverloads constructor(context: Context,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyle: Int = 0)
|
defStyle: Int = 0)
|
||||||
: LinearLayout(context, attrs, defStyle), GenericTextFieldView {
|
: RelativeLayout(context, attrs, defStyle), GenericTextFieldView {
|
||||||
|
|
||||||
private val labelView: TextView
|
private var labelViewId = ViewCompat.generateViewId()
|
||||||
private val valueView: TextView
|
private var valueViewId = ViewCompat.generateViewId()
|
||||||
private val showButtonView: ImageView
|
private var showButtonId = ViewCompat.generateViewId()
|
||||||
private val copyButtonView: ImageView
|
private var copyButtonId = ViewCompat.generateViewId()
|
||||||
|
|
||||||
|
private val labelView = AppCompatTextView(context).apply {
|
||||||
|
setTextAppearance(context,
|
||||||
|
R.style.KeepassDXStyle_TextAppearance_LabelTextStyle)
|
||||||
|
layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.WRAP_CONTENT
|
||||||
|
).also {
|
||||||
|
it.leftMargin = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
4f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
it.marginStart = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
4f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private val valueView = AppCompatTextView(context).apply {
|
||||||
|
setTextAppearance(context,
|
||||||
|
R.style.KeepassDXStyle_TextAppearance_TextEntryItem)
|
||||||
|
layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.WRAP_CONTENT).also {
|
||||||
|
it.topMargin = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
4f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
it.leftMargin = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
8f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
it.marginStart = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
8f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTextIsSelectable(true)
|
||||||
|
}
|
||||||
|
private var showButton = AppCompatImageButton(
|
||||||
|
ContextThemeWrapper(context, R.style.KeepassDXStyle_ImageButton_Simple), null, 0).apply {
|
||||||
|
layoutParams = LayoutParams(
|
||||||
|
LayoutParams.WRAP_CONTENT,
|
||||||
|
LayoutParams.WRAP_CONTENT)
|
||||||
|
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_visibility_state))
|
||||||
|
contentDescription = context.getString(R.string.menu_showpass)
|
||||||
|
}
|
||||||
|
private var copyButton = AppCompatImageButton(
|
||||||
|
ContextThemeWrapper(context, R.style.KeepassDXStyle_ImageButton_Simple), null, 0).apply {
|
||||||
|
layoutParams = LayoutParams(
|
||||||
|
LayoutParams.WRAP_CONTENT,
|
||||||
|
LayoutParams.WRAP_CONTENT)
|
||||||
|
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_content_copy_white_24dp))
|
||||||
|
contentDescription = context.getString(R.string.menu_copy)
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
|
buildViews()
|
||||||
inflater?.inflate(R.layout.view_entry_field, this)
|
addView(copyButton)
|
||||||
|
addView(showButton)
|
||||||
|
addView(labelView)
|
||||||
|
addView(valueView)
|
||||||
|
}
|
||||||
|
|
||||||
labelView = findViewById(R.id.entry_field_label)
|
private fun buildViews() {
|
||||||
valueView = findViewById(R.id.entry_field_value)
|
copyButton.apply {
|
||||||
showButtonView = findViewById(R.id.entry_field_show)
|
id = copyButtonId
|
||||||
copyButtonView = findViewById(R.id.entry_field_copy)
|
layoutParams = (layoutParams as LayoutParams?).also {
|
||||||
copyButtonView.visibility = View.GONE
|
it?.addRule(ALIGN_PARENT_RIGHT)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
it?.addRule(ALIGN_PARENT_END)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showButton.apply {
|
||||||
|
id = showButtonId
|
||||||
|
layoutParams = (layoutParams as LayoutParams?).also {
|
||||||
|
if (copyButton.isVisible) {
|
||||||
|
it?.addRule(LEFT_OF, copyButtonId)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
it?.addRule(START_OF, copyButtonId)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
it?.addRule(ALIGN_PARENT_RIGHT)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
it?.addRule(ALIGN_PARENT_END)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
labelView.apply {
|
||||||
|
id = labelViewId
|
||||||
|
layoutParams = (layoutParams as LayoutParams?).also {
|
||||||
|
it?.addRule(LEFT_OF, showButtonId)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
it?.addRule(START_OF, showButtonId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
valueView.apply {
|
||||||
|
id = valueViewId
|
||||||
|
layoutParams = (layoutParams as LayoutParams?).also {
|
||||||
|
it?.addRule(LEFT_OF, showButtonId)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
it?.addRule(START_OF, showButtonId)
|
||||||
|
}
|
||||||
|
it?.addRule(BELOW, labelViewId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyFontVisibility(fontInVisibility: Boolean) {
|
override fun applyFontVisibility(fontInVisibility: Boolean) {
|
||||||
@@ -115,19 +227,20 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setProtection(protection: Boolean, hiddenProtectedValue: Boolean = false) {
|
fun setProtection(protection: Boolean, hiddenProtectedValue: Boolean = false) {
|
||||||
showButtonView.isVisible = protection
|
showButton.isVisible = protection
|
||||||
showButtonView.isSelected = hiddenProtectedValue
|
showButton.isSelected = hiddenProtectedValue
|
||||||
showButtonView.setOnClickListener {
|
showButton.setOnClickListener {
|
||||||
showButtonView.isSelected = !showButtonView.isSelected
|
showButton.isSelected = !showButton.isSelected
|
||||||
changeProtectedValueParameters()
|
changeProtectedValueParameters()
|
||||||
}
|
}
|
||||||
changeProtectedValueParameters()
|
changeProtectedValueParameters()
|
||||||
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeProtectedValueParameters() {
|
private fun changeProtectedValueParameters() {
|
||||||
valueView.apply {
|
valueView.apply {
|
||||||
if (showButtonView.isVisible) {
|
if (showButton.isVisible) {
|
||||||
applyHiddenStyle(showButtonView.isSelected)
|
applyHiddenStyle(showButton.isSelected)
|
||||||
} else {
|
} else {
|
||||||
linkify()
|
linkify()
|
||||||
}
|
}
|
||||||
@@ -138,11 +251,11 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
when {
|
when {
|
||||||
labelView.text.contains(APPLICATION_ID_FIELD_NAME) -> {
|
labelView.text.contains(APPLICATION_ID_FIELD_NAME) -> {
|
||||||
val packageName = valueView.text.toString()
|
val packageName = valueView.text.toString()
|
||||||
if (UriUtil.isExternalAppInstalled(context, packageName)) {
|
// TODO #996 if (UriUtil.isExternalAppInstalled(context, packageName)) {
|
||||||
valueView.customLink {
|
valueView.customLink {
|
||||||
UriUtil.openExternalApp(context, packageName)
|
UriUtil.openExternalApp(context, packageName)
|
||||||
}
|
}
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
LinkifyCompat.addLinks(valueView, Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES)
|
LinkifyCompat.addLinks(valueView, Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES)
|
||||||
@@ -151,8 +264,8 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getCopyButtonView(): View? {
|
fun getCopyButtonView(): View? {
|
||||||
if (copyButtonView.isVisible) {
|
if (copyButton.isVisible) {
|
||||||
return copyButtonView
|
return copyButton
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -160,7 +273,7 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
fun setCopyButtonState(buttonState: ButtonState) {
|
fun setCopyButtonState(buttonState: ButtonState) {
|
||||||
when (buttonState) {
|
when (buttonState) {
|
||||||
ButtonState.ACTIVATE -> {
|
ButtonState.ACTIVATE -> {
|
||||||
copyButtonView.apply {
|
copyButton.apply {
|
||||||
visibility = VISIBLE
|
visibility = VISIBLE
|
||||||
isActivated = false
|
isActivated = false
|
||||||
}
|
}
|
||||||
@@ -170,7 +283,7 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ButtonState.DEACTIVATE -> {
|
ButtonState.DEACTIVATE -> {
|
||||||
copyButtonView.apply {
|
copyButton.apply {
|
||||||
visibility = VISIBLE
|
visibility = VISIBLE
|
||||||
// Reverse because isActivated show custom color and allow click
|
// Reverse because isActivated show custom color and allow click
|
||||||
isActivated = true
|
isActivated = true
|
||||||
@@ -181,7 +294,7 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ButtonState.GONE -> {
|
ButtonState.GONE -> {
|
||||||
copyButtonView.apply {
|
copyButton.apply {
|
||||||
visibility = GONE
|
visibility = GONE
|
||||||
setOnClickListener(null)
|
setOnClickListener(null)
|
||||||
}
|
}
|
||||||
@@ -191,18 +304,24 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCopyButtonClickListener(onActionClickListener: OnClickListener?) {
|
fun setCopyButtonClickListener(onActionClickListener: ((label: String, value: String) -> Unit)?) {
|
||||||
setOnActionClickListener(onActionClickListener, null)
|
val clickListener = if (onActionClickListener != null)
|
||||||
|
OnClickListener { onActionClickListener.invoke(label, value) }
|
||||||
|
else
|
||||||
|
null
|
||||||
|
setOnActionClickListener(clickListener, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setOnActionClickListener(
|
override fun setOnActionClickListener(
|
||||||
onActionClickListener: OnClickListener?,
|
onActionClickListener: OnClickListener?,
|
||||||
actionImageId: Int?
|
actionImageId: Int?
|
||||||
) {
|
) {
|
||||||
copyButtonView.setOnClickListener(onActionClickListener)
|
copyButton.setOnClickListener(onActionClickListener)
|
||||||
copyButtonView.isVisible = onActionClickListener != null
|
copyButton.isVisible = onActionClickListener != null
|
||||||
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
override var isFieldVisible: Boolean
|
override var isFieldVisible: Boolean
|
||||||
@@ -213,6 +332,11 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
isVisible = value
|
isVisible = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
super.invalidate()
|
||||||
|
buildViews()
|
||||||
|
}
|
||||||
|
|
||||||
enum class ButtonState {
|
enum class ButtonState {
|
||||||
ACTIVATE, DEACTIVATE, GONE
|
ACTIVATE, DEACTIVATE, GONE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,27 @@ package com.kunzisoft.keepass.view
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.text.InputType
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.ContextThemeWrapper
|
import android.view.ContextThemeWrapper
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.appcompat.widget.AppCompatImageButton
|
import androidx.appcompat.widget.AppCompatImageButton
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||||
|
import androidx.core.graphics.BlendModeCompat
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|
||||||
|
|
||||||
class TextSelectFieldView @JvmOverloads constructor(context: Context,
|
class TextSelectFieldView @JvmOverloads constructor(context: Context,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyle: Int = 0)
|
defStyle: Int = 0)
|
||||||
@@ -28,30 +34,54 @@ class TextSelectFieldView @JvmOverloads constructor(context: Context,
|
|||||||
private var actionImageButtonId = ViewCompat.generateViewId()
|
private var actionImageButtonId = ViewCompat.generateViewId()
|
||||||
private var mDefaultPosition = 0
|
private var mDefaultPosition = 0
|
||||||
|
|
||||||
private val labelView = AppCompatTextView(context).apply {
|
private val labelView = TextInputLayout(context).apply {
|
||||||
setTextAppearance(context, R.style.KeepassDXStyle_TextAppearance_LabelTextStyle)
|
|
||||||
layoutParams = LayoutParams(
|
layoutParams = LayoutParams(
|
||||||
LayoutParams.MATCH_PARENT,
|
LayoutParams.MATCH_PARENT,
|
||||||
LayoutParams.WRAP_CONTENT).also {
|
LayoutParams.WRAP_CONTENT)
|
||||||
val leftMargin = 4f
|
}
|
||||||
it.leftMargin = TypedValue.applyDimension(
|
private val valueView = TextInputEditText(
|
||||||
TypedValue.COMPLEX_UNIT_DIP,
|
ContextThemeWrapper(context,
|
||||||
leftMargin,
|
R.style.KeepassDXStyle_TextInputLayout)
|
||||||
resources.displayMetrics
|
).apply {
|
||||||
).toInt()
|
layoutParams = LinearLayout.LayoutParams(
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
LayoutParams.MATCH_PARENT,
|
||||||
it.marginStart = TypedValue.applyDimension(
|
LayoutParams.WRAP_CONTENT)
|
||||||
TypedValue.COMPLEX_UNIT_DIP,
|
inputType = EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
||||||
leftMargin,
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
resources.displayMetrics
|
imeOptions = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
|
||||||
).toInt()
|
importantForAutofill = IMPORTANT_FOR_AUTOFILL_NO
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
|
importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||||
|
}
|
||||||
|
val drawable = ContextCompat.getDrawable(context, R.drawable.ic_arrow_down_white_24dp)
|
||||||
|
?.apply {
|
||||||
|
mutate().colorFilter = BlendModeColorFilterCompat
|
||||||
|
.createBlendModeColorFilterCompat(currentTextColor, BlendModeCompat.SRC_IN)
|
||||||
|
}
|
||||||
|
setCompoundDrawablesWithIntrinsicBounds(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
drawable,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
drawable,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
isFocusable = false
|
||||||
|
inputType = InputType.TYPE_NULL
|
||||||
|
maxLines = 1
|
||||||
}
|
}
|
||||||
private val valueSpinnerView = Spinner(context).apply {
|
private val valueSpinnerView = Spinner(context).apply {
|
||||||
layoutParams = LayoutParams(
|
layoutParams = LayoutParams(
|
||||||
LayoutParams.MATCH_PARENT,
|
0,
|
||||||
LayoutParams.WRAP_CONTENT)
|
0
|
||||||
|
)
|
||||||
}
|
}
|
||||||
private var actionImageButton = AppCompatImageButton(
|
private var actionImageButton = AppCompatImageButton(
|
||||||
ContextThemeWrapper(context, R.style.KeepassDXStyle_ImageButton_Simple), null, 0).apply {
|
ContextThemeWrapper(context, R.style.KeepassDXStyle_ImageButton_Simple), null, 0).apply {
|
||||||
@@ -64,7 +94,7 @@ class TextSelectFieldView @JvmOverloads constructor(context: Context,
|
|||||||
resources.displayMetrics
|
resources.displayMetrics
|
||||||
).toInt()
|
).toInt()
|
||||||
it.addRule(ALIGN_PARENT_RIGHT)
|
it.addRule(ALIGN_PARENT_RIGHT)
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
it.addRule(ALIGN_PARENT_END)
|
it.addRule(ALIGN_PARENT_END)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,15 +105,22 @@ class TextSelectFieldView @JvmOverloads constructor(context: Context,
|
|||||||
init {
|
init {
|
||||||
// Manually write view to avoid view id bugs
|
// Manually write view to avoid view id bugs
|
||||||
buildViews()
|
buildViews()
|
||||||
|
labelView.addView(valueView)
|
||||||
addView(labelView)
|
addView(labelView)
|
||||||
addView(valueSpinnerView)
|
addView(valueSpinnerView)
|
||||||
addView(actionImageButton)
|
addView(actionImageButton)
|
||||||
|
|
||||||
|
valueView.apply {
|
||||||
|
setOnClickListener {
|
||||||
|
valueSpinnerView.performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
valueSpinnerView.apply {
|
valueSpinnerView.apply {
|
||||||
adapter = valueSpinnerAdapter
|
adapter = valueSpinnerAdapter
|
||||||
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
valueSpinnerAdapter.getItem(position)
|
val stringValue = valueSpinnerAdapter.getItem(position)
|
||||||
|
valueView.setText(stringValue)
|
||||||
}
|
}
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||||
}
|
}
|
||||||
@@ -95,7 +132,7 @@ class TextSelectFieldView @JvmOverloads constructor(context: Context,
|
|||||||
id = labelViewId
|
id = labelViewId
|
||||||
layoutParams = (layoutParams as LayoutParams?).also {
|
layoutParams = (layoutParams as LayoutParams?).also {
|
||||||
it?.addRule(LEFT_OF, actionImageButtonId)
|
it?.addRule(LEFT_OF, actionImageButtonId)
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
it?.addRule(START_OF, actionImageButtonId)
|
it?.addRule(START_OF, actionImageButtonId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +141,7 @@ class TextSelectFieldView @JvmOverloads constructor(context: Context,
|
|||||||
id = valueViewId
|
id = valueViewId
|
||||||
layoutParams = (layoutParams as LayoutParams?).also {
|
layoutParams = (layoutParams as LayoutParams?).also {
|
||||||
it?.addRule(LEFT_OF, actionImageButtonId)
|
it?.addRule(LEFT_OF, actionImageButtonId)
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
it?.addRule(START_OF, actionImageButtonId)
|
it?.addRule(START_OF, actionImageButtonId)
|
||||||
}
|
}
|
||||||
it?.addRule(BELOW, labelViewId)
|
it?.addRule(BELOW, labelViewId)
|
||||||
|
|||||||
@@ -5,29 +5,23 @@ import androidx.lifecycle.LiveData
|
|||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.kunzisoft.keepass.app.database.IOActionTask
|
import com.kunzisoft.keepass.app.database.IOActionTask
|
||||||
import com.kunzisoft.keepass.database.element.*
|
import com.kunzisoft.keepass.database.element.*
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
|
||||||
import com.kunzisoft.keepass.database.element.template.Template
|
import com.kunzisoft.keepass.database.element.template.Template
|
||||||
import com.kunzisoft.keepass.model.*
|
import com.kunzisoft.keepass.model.*
|
||||||
import com.kunzisoft.keepass.otp.OtpElement
|
import com.kunzisoft.keepass.otp.OtpElement
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
class EntryEditViewModel: NodeEditViewModel() {
|
class EntryEditViewModel: NodeEditViewModel() {
|
||||||
|
|
||||||
private val mTempAttachments = mutableListOf<EntryAttachmentState>()
|
private val mTempAttachments = mutableListOf<EntryAttachmentState>()
|
||||||
|
|
||||||
val entryInfo : LiveData<EntryInfo> get() = _entryInfo
|
val templatesEntry : LiveData<TemplatesEntry> get() = _templatesEntry
|
||||||
private val _entryInfo = MutableLiveData<EntryInfo>()
|
private val _templatesEntry = MutableLiveData<TemplatesEntry>()
|
||||||
|
|
||||||
val requestEntryInfoUpdate : LiveData<EntryUpdate> get() = _requestEntryInfoUpdate
|
val requestEntryInfoUpdate : LiveData<EntryUpdate> get() = _requestEntryInfoUpdate
|
||||||
private val _requestEntryInfoUpdate = SingleLiveEvent<EntryUpdate>()
|
private val _requestEntryInfoUpdate = SingleLiveEvent<EntryUpdate>()
|
||||||
val onEntrySaved : LiveData<EntrySave> get() = _onEntrySaved
|
val onEntrySaved : LiveData<EntrySave> get() = _onEntrySaved
|
||||||
private val _onEntrySaved = SingleLiveEvent<EntrySave>()
|
private val _onEntrySaved = SingleLiveEvent<EntrySave>()
|
||||||
|
|
||||||
val templates : LiveData<TemplatesLoad> get() = _templates
|
|
||||||
private val _templates = MutableLiveData<TemplatesLoad>()
|
|
||||||
val onTemplateChanged : LiveData<Template> get() = _onTemplateChanged
|
val onTemplateChanged : LiveData<Template> get() = _onTemplateChanged
|
||||||
private val _onTemplateChanged = SingleLiveEvent<Template>()
|
private val _onTemplateChanged = SingleLiveEvent<Template>()
|
||||||
|
|
||||||
@@ -69,38 +63,27 @@ class EntryEditViewModel: NodeEditViewModel() {
|
|||||||
{
|
{
|
||||||
val templates = database.getTemplates(isTemplate)
|
val templates = database.getTemplates(isTemplate)
|
||||||
val entryTemplate = entry?.let { database.getTemplate(it) } ?: Template.STANDARD
|
val entryTemplate = entry?.let { database.getTemplate(it) } ?: Template.STANDARD
|
||||||
TemplatesLoad(templates, entryTemplate)
|
var entryInfo: EntryInfo? = null
|
||||||
},
|
// Decode the entry / load entry info
|
||||||
{ templatesLoad ->
|
entry?.let {
|
||||||
// Call templates init before populate entry info
|
database.decodeEntryWithTemplateConfiguration(it).let { entry ->
|
||||||
_templates.value = templatesLoad
|
// Load entry info
|
||||||
changeTemplate(templatesLoad!!.defaultTemplate)
|
entry.getEntryInfo(database, true).let { tempEntryInfo ->
|
||||||
|
// Retrieve data from registration
|
||||||
IOActionTask(
|
(registerInfo?.searchInfo ?: searchInfo)?.let { tempSearchInfo ->
|
||||||
{
|
tempEntryInfo.saveSearchInfo(database, tempSearchInfo)
|
||||||
var entryInfo: EntryInfo? = null
|
|
||||||
// Decode the entry / load entry info
|
|
||||||
entry?.let {
|
|
||||||
database.decodeEntryWithTemplateConfiguration(it).let { entry ->
|
|
||||||
// Load entry info
|
|
||||||
entry.getEntryInfo(database, true).let { tempEntryInfo ->
|
|
||||||
// Retrieve data from registration
|
|
||||||
(registerInfo?.searchInfo ?: searchInfo)?.let { tempSearchInfo ->
|
|
||||||
tempEntryInfo.saveSearchInfo(database, tempSearchInfo)
|
|
||||||
}
|
|
||||||
registerInfo?.let { regInfo ->
|
|
||||||
tempEntryInfo.saveRegisterInfo(database, regInfo)
|
|
||||||
}
|
|
||||||
entryInfo = tempEntryInfo
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
registerInfo?.let { regInfo ->
|
||||||
|
tempEntryInfo.saveRegisterInfo(database, regInfo)
|
||||||
|
}
|
||||||
|
entryInfo = tempEntryInfo
|
||||||
}
|
}
|
||||||
entryInfo
|
|
||||||
},
|
|
||||||
{ entryInfo ->
|
|
||||||
_entryInfo.value = entryInfo
|
|
||||||
}
|
}
|
||||||
).execute()
|
}
|
||||||
|
TemplatesEntry(templates, entryTemplate, entryInfo)
|
||||||
|
},
|
||||||
|
{ templatesEntry ->
|
||||||
|
_templatesEntry.value = templatesEntry
|
||||||
}
|
}
|
||||||
).execute()
|
).execute()
|
||||||
}
|
}
|
||||||
@@ -239,7 +222,7 @@ class EntryEditViewModel: NodeEditViewModel() {
|
|||||||
_onBinaryPreviewLoaded.value = AttachmentPosition(entryAttachmentState, viewPosition)
|
_onBinaryPreviewLoaded.value = AttachmentPosition(entryAttachmentState, viewPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class TemplatesLoad(val templates: List<Template>, val defaultTemplate: Template)
|
data class TemplatesEntry(val templates: List<Template>, val defaultTemplate: Template, val entryInfo: EntryInfo?)
|
||||||
data class EntryUpdate(val database: Database?, val entry: Entry?, val parent: Group?)
|
data class EntryUpdate(val database: Database?, val entry: Entry?, val parent: Group?)
|
||||||
data class EntrySave(val oldEntry: Entry, val newEntry: Entry, val parent: Group?)
|
data class EntrySave(val oldEntry: Entry, val newEntry: Entry, val parent: Group?)
|
||||||
data class FieldEdition(val oldField: Field?, val newField: Field?)
|
data class FieldEdition(val oldField: Field?, val newField: Field?)
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ class EntryViewModel: ViewModel() {
|
|||||||
val historyPosition : LiveData<Int> get() = _historyPosition
|
val historyPosition : LiveData<Int> get() = _historyPosition
|
||||||
private val _historyPosition = MutableLiveData<Int>()
|
private val _historyPosition = MutableLiveData<Int>()
|
||||||
|
|
||||||
val url : LiveData<String?> get() = _url
|
|
||||||
private val _url = MutableLiveData<String?>()
|
|
||||||
|
|
||||||
val entryInfo : LiveData<EntryInfo> get() = _entryInfo
|
val entryInfo : LiveData<EntryInfo> get() = _entryInfo
|
||||||
private val _entryInfo = MutableLiveData<EntryInfo>()
|
private val _entryInfo = MutableLiveData<EntryInfo>()
|
||||||
|
|
||||||
@@ -66,58 +63,49 @@ class EntryViewModel: ViewModel() {
|
|||||||
private val _historySelected = SingleLiveEvent<EntryHistory>()
|
private val _historySelected = SingleLiveEvent<EntryHistory>()
|
||||||
|
|
||||||
fun loadEntry(database: Database?, entryId: NodeId<UUID>?, historyPosition: Int) {
|
fun loadEntry(database: Database?, entryId: NodeId<UUID>?, historyPosition: Int) {
|
||||||
if (entryId != null) {
|
if (database != null && entryId != null) {
|
||||||
IOActionTask(
|
IOActionTask(
|
||||||
{
|
{
|
||||||
database?.getEntryById(entryId)
|
val mainEntry = database.getEntryById(entryId)
|
||||||
},
|
|
||||||
{ mainEntry ->
|
|
||||||
// Manage current version and history
|
|
||||||
_mainEntryId.value = mainEntry?.nodeId
|
|
||||||
_historyPosition.value = historyPosition
|
|
||||||
|
|
||||||
val currentEntry = if (historyPosition > -1) {
|
val currentEntry = if (historyPosition > -1) {
|
||||||
mainEntry?.getHistory()?.get(historyPosition)
|
mainEntry?.getHistory()?.get(historyPosition)
|
||||||
} else {
|
} else {
|
||||||
mainEntry
|
mainEntry
|
||||||
}
|
}
|
||||||
_url.value = currentEntry?.url
|
|
||||||
|
|
||||||
IOActionTask(
|
val entryTemplate = currentEntry?.let {
|
||||||
{
|
database.getTemplate(it)
|
||||||
val entryTemplate = currentEntry?.let {
|
} ?: Template.STANDARD
|
||||||
database?.getTemplate(it)
|
|
||||||
} ?: Template.STANDARD
|
|
||||||
|
|
||||||
// To simplify template field visibility
|
// To simplify template field visibility
|
||||||
currentEntry?.let { entry ->
|
currentEntry?.let { entry ->
|
||||||
// Add mainEntry to check the parent and define the template state
|
// Add mainEntry to check the parent and define the template state
|
||||||
database?.decodeEntryWithTemplateConfiguration(entry, mainEntry)
|
database.decodeEntryWithTemplateConfiguration(entry, mainEntry).let {
|
||||||
?.let {
|
// To update current modification time
|
||||||
// To update current modification time
|
it.touch(modified = false, touchParents = false)
|
||||||
it.touch(modified = false, touchParents = false)
|
|
||||||
|
|
||||||
// Build history info
|
// Build history info
|
||||||
val entryInfoHistory = it.getHistory().map { entryHistory ->
|
val entryInfoHistory = it.getHistory().map { entryHistory ->
|
||||||
entryHistory.getEntryInfo(database)
|
entryHistory.getEntryInfo(database)
|
||||||
}
|
|
||||||
|
|
||||||
EntryInfoHistory(
|
|
||||||
entryTemplate,
|
|
||||||
it.getEntryInfo(database),
|
|
||||||
entryInfoHistory
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ entryInfoHistory ->
|
|
||||||
if (entryInfoHistory != null) {
|
|
||||||
_template.value = entryInfoHistory.template
|
|
||||||
_entryInfo.value = entryInfoHistory.entryInfo
|
|
||||||
_entryHistory.value = entryInfoHistory.entryHistory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EntryInfoHistory(
|
||||||
|
mainEntry!!.nodeId,
|
||||||
|
entryTemplate,
|
||||||
|
it.getEntryInfo(database),
|
||||||
|
entryInfoHistory
|
||||||
|
)
|
||||||
}
|
}
|
||||||
).execute()
|
}
|
||||||
|
},
|
||||||
|
{ entryInfoHistory ->
|
||||||
|
if (entryInfoHistory != null) {
|
||||||
|
_mainEntryId.value = entryInfoHistory.mainEntryId
|
||||||
|
_historyPosition.value = historyPosition
|
||||||
|
_template.value = entryInfoHistory.template
|
||||||
|
_entryInfo.value = entryInfoHistory.entryInfo
|
||||||
|
_entryHistory.value = entryInfoHistory.entryHistory
|
||||||
|
}
|
||||||
}
|
}
|
||||||
).execute()
|
).execute()
|
||||||
}
|
}
|
||||||
@@ -143,7 +131,8 @@ class EntryViewModel: ViewModel() {
|
|||||||
_historySelected.value = EntryHistory(NodeIdUUID(item.id), null, item, position)
|
_historySelected.value = EntryHistory(NodeIdUUID(item.id), null, item, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class EntryInfoHistory(val template: Template,
|
data class EntryInfoHistory(var mainEntryId: NodeId<UUID>,
|
||||||
|
val template: Template,
|
||||||
val entryInfo: EntryInfo,
|
val entryInfo: EntryInfo,
|
||||||
val entryHistory: List<EntryInfo>)
|
val entryHistory: List<EntryInfo>)
|
||||||
// Custom data class to manage entry to retrieve and define is it's an history item (!= -1)
|
// Custom data class to manage entry to retrieve and define is it's an history item (!= -1)
|
||||||
|
|||||||
4
app/src/main/res/color-v21/progress_color.xml
Normal file
4
app/src/main/res/color-v21/progress_color.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?attr/colorAccent" />
|
||||||
|
</selector>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
<item android:state_selected="true" android:color="@color/white"/>
|
<item android:state_selected="true" android:color="@color/white"/>
|
||||||
<item android:color="?android:attr/textColorSecondary"/>
|
<item android:color="@color/grey"/>
|
||||||
</selector>
|
</selector>
|
||||||
4
app/src/main/res/color/progress_color.xml
Normal file
4
app/src/main/res/color/progress_color.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="@color/white_dark" />
|
||||||
|
</selector>
|
||||||
@@ -7,6 +7,6 @@
|
|||||||
android:innerRadiusRatio="2.5"
|
android:innerRadiusRatio="2.5"
|
||||||
android:thickness="2dp"
|
android:thickness="2dp"
|
||||||
android:useLevel="true">
|
android:useLevel="true">
|
||||||
<solid android:color="@color/orange" />
|
<solid android:color="@color/progress_color" />
|
||||||
</shape>
|
</shape>
|
||||||
</rotate>
|
</rotate>
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:fromDegrees="270"
|
android:fromDegrees="270"
|
||||||
android:toDegrees="270">
|
android:toDegrees="270">
|
||||||
@@ -8,6 +7,6 @@
|
|||||||
android:innerRadiusRatio="2.5"
|
android:innerRadiusRatio="2.5"
|
||||||
android:thickness="2dp"
|
android:thickness="2dp"
|
||||||
android:useLevel="true">
|
android:useLevel="true">
|
||||||
<solid android:color="?attr/colorAccent" />
|
<solid android:color="@color/list_secondary_color" />
|
||||||
</shape>
|
</shape>
|
||||||
</rotate>
|
</rotate>
|
||||||
10
app/src/main/res/drawable/ic_downloading_white_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_downloading_white_24dp.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?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">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M 11 2.0566406 C 6.762335 2.4220229 3.0067094 5.7987155 2.203125 9.9785156 C 1.3601754 13.960549 3.1781148 18.394742 6.7089844 20.480469 C 9.6237318 22.368157 13.514425 22.492178 16.582031 20.892578 C 17.959775 20.180473 19.316015 19.099467 20.087891 17.808594 L 18.402344 16.791016 C 16.277892 19.724364 12.039121 20.844607 8.7519531 19.306641 C 5.4810648 17.911181 3.4461927 14.150571 4.109375 10.648438 C 4.6649663 7.2806973 7.5784749 4.4226117 11 4.0839844 L 11 2.0566406 z M 13 2.0644531 L 13 4.09375 C 16.367309 4.4801388 19.308004 7.2166098 19.861328 10.574219 C 20.123351 12.069186 19.935392 13.632673 19.367188 15.037109 C 19.94644 15.387646 20.527063 15.73602 21.105469 16.087891 C 22.671735 12.714066 22.12099 8.4920873 19.708984 5.6542969 C 18.063396 3.6246553 15.604973 2.2995703 13 2.0644531 z M 11 7.0644531 L 11 12.064453 L 7 12.064453 L 12 17.064453 L 17 12.064453 L 13 12.064453 L 13 7.0644531 L 11 7.0644531 z" />
|
||||||
|
</vector>
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:height="24dp"
|
|
||||||
android:width="24dp"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24" >
|
|
||||||
<path android:fillColor="#FFFFFF" android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/>
|
|
||||||
</vector>
|
|
||||||
10
app/src/main/res/drawable/ic_saving_white_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_saving_white_24dp.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?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">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M 11 2.0566406 C 6.762335 2.4220229 3.0067094 5.7987155 2.203125 9.9785156 C 1.3601753 13.960549 3.1781148 18.394742 6.7089844 20.480469 C 9.6237318 22.368157 13.514425 22.492178 16.582031 20.892578 C 17.959775 20.180473 19.316015 19.099467 20.087891 17.808594 L 18.402344 16.791016 C 16.277892 19.724364 12.039121 20.844605 8.7519531 19.306641 C 5.481064 17.911182 3.4461934 14.150571 4.109375 10.648438 C 4.6649664 7.2806974 7.5784749 4.4226117 11 4.0839844 L 11 2.0566406 z M 13 2.0644531 L 13 4.09375 C 16.367309 4.4801387 19.308002 7.2166101 19.861328 10.574219 C 20.123352 12.069186 19.935393 13.632674 19.367188 15.037109 C 19.94644 15.387646 20.527063 15.73602 21.105469 16.087891 C 22.671737 12.714066 22.120988 8.4920871 19.708984 5.6542969 C 18.063396 3.6246553 15.604973 2.2995704 13 2.0644531 z M 8.1113281 7 C 7.4946611 7 7 7.5002173 7 8.1113281 L 7 15.888672 C 7 16.499784 7.4946611 17 8.1113281 17 L 15.888672 17 C 16.499783 17 17 16.499784 17 15.888672 L 17 9.2226562 L 14.777344 7 L 8.1113281 7 z M 8.1113281 8.1113281 L 13.666016 8.1113281 L 13.666016 10.333984 L 8.1113281 10.333984 L 8.1113281 8.1113281 z M 12 12.554688 C 12.922223 12.554688 13.666016 13.300434 13.666016 14.222656 C 13.666016 15.144878 12.922223 15.888672 12 15.888672 C 11.077779 15.888672 10.333984 15.144878 10.333984 14.222656 C 10.333984 13.300434 11.077779 12.554687 12 12.554688 z" />
|
||||||
|
</vector>
|
||||||
@@ -70,16 +70,16 @@
|
|||||||
tools:targetApi="lollipop">
|
tools:targetApi="lollipop">
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
<ProgressBar
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
android:id="@+id/entry_progress"
|
android:id="@+id/entry_progress"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
|
||||||
android:indeterminate="false"
|
android:indeterminate="false"
|
||||||
|
app:indicatorColor="?attr/colorAccent"
|
||||||
android:progress="10"
|
android:progress="10"
|
||||||
android:max="30"
|
android:max="30"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="4dp" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|||||||
@@ -17,7 +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
|
<RelativeLayout
|
||||||
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"
|
||||||
@@ -29,15 +29,14 @@
|
|||||||
android:id="@+id/special_mode_view"
|
android:id="@+id/special_mode_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:theme="?attr/toolbarSpecialAppearance"
|
android:theme="?attr/toolbarSpecialAppearance" />
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<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="0dp"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/special_mode_view"
|
android:layout_below="@+id/special_mode_view"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/toolbar_action">
|
android:layout_above="@+id/toolbar_action">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar"
|
android:id="@+id/app_bar"
|
||||||
@@ -47,99 +46,86 @@
|
|||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
android:fitsSystemWindows="true">
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar_layout"
|
android:id="@+id/toolbar"
|
||||||
|
android:title="@string/app_name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:titleEnabled="false"
|
android:background="?attr/colorPrimary"
|
||||||
app:contentScrim="?attr/colorPrimary"
|
android:theme="?attr/toolbarAppearance"
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
android:elevation="4dp"
|
||||||
|
tools:targetApi="lollipop">
|
||||||
<androidx.appcompat.widget.Toolbar
|
<LinearLayout
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/group_header"
|
||||||
android:title="@string/app_name"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_alignParentLeft="true"
|
||||||
android:background="?attr/colorPrimary"
|
android:layout_alignParentStart="true"
|
||||||
android:theme="?attr/toolbarAppearance"
|
android:layout_below="@+id/toolbar"
|
||||||
android:elevation="4dp"
|
android:orientation="vertical">
|
||||||
tools:targetApi="lollipop">
|
<TextView android:id="@+id/search_title"
|
||||||
<LinearLayout
|
android:layout_width="match_parent"
|
||||||
android:id="@+id/group_header"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:text="@string/search_results"
|
||||||
android:layout_alignParentStart="true"
|
android:visibility="gone"
|
||||||
android:layout_below="@+id/toolbar"
|
style="@style/KeepassDXStyle.TextAppearance.Default.TextOnPrimary" />
|
||||||
android:orientation="vertical">
|
<LinearLayout
|
||||||
<TextView android:id="@+id/search_title"
|
android:layout_width="match_parent"
|
||||||
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: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:gravity="center_vertical">
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<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_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical">
|
tools:text="3"
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
style="@style/KeepassDXStyle.TextAppearance.Info" />
|
||||||
android:id="@+id/group_icon"
|
</RelativeLayout>
|
||||||
android:layout_width="32dp"
|
<LinearLayout
|
||||||
android:layout_height="32dp"
|
android:layout_width="0dp"
|
||||||
android:layout_gravity="end|center_vertical"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="6dp"
|
android:orientation="vertical"
|
||||||
android:layout_marginEnd="6dp"
|
android:layout_gravity="start|center_vertical"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:layout_marginLeft="14dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_marginStart="14dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:layout_weight="1">
|
||||||
android:scaleType="fitXY" />
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
android:id="@+id/group_name"
|
||||||
android:id="@+id/group_numbers"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:text="@string/root"
|
||||||
tools:text="3"
|
android:maxLines="2"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Info"
|
android:ellipsize="end"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
style="@style/KeepassDXStyle.TextAppearance.Title.TextOnPrimary" />
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
android:id="@+id/group_meta"
|
||||||
<LinearLayout
|
android:layout_height="match_parent"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C"
|
||||||
android:orientation="vertical"
|
android:lines="1"
|
||||||
android:layout_gravity="start|center_vertical"
|
android:singleLine="true"
|
||||||
android:layout_marginLeft="@dimen/image_list_margin_vertical"
|
style="@style/KeepassDXStyle.TextAppearance.Meta.TextOnPrimary" />
|
||||||
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
|
||||||
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>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</LinearLayout>
|
||||||
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<RelativeLayout
|
||||||
android:id="@+id/node_list_container"
|
android:id="@+id/node_list_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@@ -151,7 +137,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?android:attr/windowBackground" />
|
android:background="?android:attr/windowBackground" />
|
||||||
</LinearLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<com.kunzisoft.keepass.view.AddNodeButtonView
|
<com.kunzisoft.keepass.view.AddNodeButtonView
|
||||||
android:id="@+id/add_node_button"
|
android:id="@+id/add_node_button"
|
||||||
@@ -167,8 +153,7 @@
|
|||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:theme="?attr/toolbarActionAppearance"
|
android:theme="?attr/toolbarActionAppearance"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:layout_alignParentBottom="true" />
|
||||||
app:layout_constraintBottom_toBottomOf="parent" />
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -185,7 +170,6 @@
|
|||||||
layout="@layout/view_button_lock"
|
layout="@layout/view_button_lock"
|
||||||
android:layout_width="@dimen/lock_button_size"
|
android:layout_width="@dimen/lock_button_size"
|
||||||
android:layout_height="@dimen/lock_button_size"
|
android:layout_height="@dimen/lock_button_size"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:layout_alignParentBottom="true"/>
|
||||||
app:layout_constraintBottom_toBottomOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</RelativeLayout>
|
||||||
@@ -17,7 +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
|
<RelativeLayout
|
||||||
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"
|
||||||
@@ -29,17 +29,16 @@
|
|||||||
android:id="@+id/special_mode_view"
|
android:id="@+id/special_mode_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:theme="?attr/toolbarSpecialAppearance"
|
android:theme="?attr/toolbarSpecialAppearance" />
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/activity_password_coordinator_layout"
|
android:id="@+id/activity_password_coordinator_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/background_repeat"
|
android:background="@drawable/background_repeat"
|
||||||
android:backgroundTint="?android:attr/textColor"
|
android:backgroundTint="?android:attr/textColor"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/special_mode_view"
|
android:layout_below="@+id/special_mode_view"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/activity_password_footer">
|
android:layout_above="@+id/activity_password_footer">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar"
|
android:id="@+id/app_bar"
|
||||||
@@ -111,12 +110,12 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
android:paddingTop="@dimen/default_margin"
|
android:paddingTop="6dp"
|
||||||
android:paddingLeft="@dimen/default_margin"
|
android:paddingLeft="12dp"
|
||||||
android:paddingStart="@dimen/default_margin"
|
android:paddingStart="12dp"
|
||||||
android:paddingRight="@dimen/default_margin"
|
android:paddingRight="12dp"
|
||||||
android:paddingEnd="@dimen/default_margin"
|
android:paddingEnd="12dp"
|
||||||
android:paddingBottom="@dimen/default_margin"
|
android:paddingBottom="12dp"
|
||||||
android:background="?android:attr/windowBackground"
|
android:background="?android:attr/windowBackground"
|
||||||
app:layout_constraintWidth_percent="@dimen/content_percent"
|
app:layout_constraintWidth_percent="@dimen/content_percent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
@@ -214,7 +213,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:layout_constraintBottom_toBottomOf="parent">
|
android:layout_alignParentBottom="true">
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/activity_password_info_container"
|
android:id="@+id/activity_password_info_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -250,4 +249,4 @@
|
|||||||
android:text="@string/menu_open" />
|
android:text="@string/menu_open" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</RelativeLayout>
|
||||||
@@ -17,28 +17,24 @@
|
|||||||
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
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
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"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.kunzisoft.keepass.view.TemplateEditView
|
<com.kunzisoft.keepass.view.TemplateEditView
|
||||||
android:id="@+id/template_view"
|
android:id="@+id/template_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"/>
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/entry_attachments_container"/>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/entry_attachments_container"
|
android:id="@+id/entry_attachments_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">
|
||||||
app:layout_constraintTop_toBottomOf="@+id/template_view"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent">
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -62,4 +58,4 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -38,10 +38,11 @@
|
|||||||
android:text="@string/entry_history"
|
android:text="@string/entry_history"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:weightSum="3"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:layout_marginBottom="4dp">
|
android:layout_marginBottom="4dp">
|
||||||
|
|
||||||
@@ -49,51 +50,30 @@
|
|||||||
android:id="@+id/entry_history_last_modified"
|
android:id="@+id/entry_history_last_modified"
|
||||||
android:text="@string/entry_modified"
|
android:text="@string/entry_modified"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_title"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/entry_history_title"
|
android:id="@+id/entry_history_title"
|
||||||
android:text="@string/entry_title"
|
android:text="@string/entry_title"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
||||||
app:layout_constraintStart_toEndOf="@+id/entry_history_last_modified"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_username"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/entry_history_username"
|
android:id="@+id/entry_history_username"
|
||||||
android:text="@string/entry_user_name"
|
android:text="@string/entry_user_name"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
||||||
app:layout_constraintStart_toEndOf="@+id/entry_history_title"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_url"
|
|
||||||
android:gravity="center"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/entry_history_url"
|
|
||||||
android:text="@string/entry_url"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTableTextStyle"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/entry_history_username"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"/>
|
android:layout_weight="1" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/entry_history_list"
|
android:id="@+id/entry_history_list"
|
||||||
|
|||||||
@@ -18,7 +18,9 @@
|
|||||||
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="vertical" android:layout_width="match_parent"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -55,9 +57,9 @@
|
|||||||
android:layout_marginEnd="20dp"
|
android:layout_marginEnd="20dp"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.WarningTextStyle"/>
|
style="@style/KeepassDXStyle.TextAppearance.WarningTextStyle"/>
|
||||||
|
|
||||||
<ProgressBar
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
android:id="@+id/progress_dialog_bar"
|
android:id="@+id/progress_dialog_bar"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
app:indicatorColor="?attr/colorAccent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
|
|||||||
@@ -38,43 +38,40 @@
|
|||||||
android:background="?android:attr/windowBackground"
|
android:background="?android:attr/windowBackground"
|
||||||
android:scaleType="fitStart" />
|
android:scaleType="fitStart" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<RelativeLayout
|
||||||
android:id="@+id/item_attachment_info"
|
android:id="@+id/item_attachment_info"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="48dp"
|
||||||
android:layout_alignBottom="@+id/item_attachment_thumbnail"
|
android:layout_alignBottom="@+id/item_attachment_thumbnail"
|
||||||
android:background="?attr/cardBackgroundTransparentColor">
|
android:background="?attr/cardBackgroundTransparentColor">
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/item_attachment_broken"
|
android:id="@+id/item_attachment_broken"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:contentDescription="@string/entry_attachments"
|
android:contentDescription="@string/entry_attachments"
|
||||||
android:src="@drawable/ic_attach_file_broken_white_24dp"
|
android:src="@drawable/ic_attach_file_broken_white_24dp" />
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<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.TextEntryItem"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_toStartOf="@+id/item_attachment_size_container"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/item_attachment_size_container"
|
android:layout_toLeftOf="@+id/item_attachment_size_container"
|
||||||
app:layout_constraintStart_toEndOf="@+id/item_attachment_broken"
|
android:layout_toEndOf="@+id/item_attachment_broken"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:layout_toRightOf="@+id/item_attachment_broken"
|
||||||
|
android:gravity="center_vertical"
|
||||||
tools:text="BinaryFile.attach" />
|
tools:text="BinaryFile.attach" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/item_attachment_size_container"
|
android:id="@+id/item_attachment_size_container"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:gravity="end"
|
android:gravity="center_vertical|end"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_toLeftOf="@+id/item_attachment_action_container"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/item_attachment_action_container"
|
android:layout_toStartOf="@+id/item_attachment_action_container">
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/item_attachment_size"
|
android:id="@+id/item_attachment_size"
|
||||||
@@ -101,10 +98,9 @@
|
|||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/item_attachment_action_container"
|
android:id="@+id/item_attachment_action_container"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_alignParentEnd="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_alignParentRight="true">
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/item_attachment_delete_button"
|
android:id="@+id/item_attachment_delete_button"
|
||||||
@@ -150,5 +146,5 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</RelativeLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
@@ -17,65 +17,43 @@
|
|||||||
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
|
<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"
|
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:weightSum="3"
|
||||||
android:background="?android:attr/selectableItemBackground">
|
android:background="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
<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"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_title"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
|
||||||
android:gravity="center"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/entry_history_title"
|
|
||||||
tools:text = "Title"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/entry_history_last_modified"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_username"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
|
||||||
android:maxLines="6"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="center"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/entry_history_username"
|
|
||||||
tools:text = "Username"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/entry_history_title"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/entry_history_url"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
|
||||||
android:maxLines="6"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="center"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/entry_history_url"
|
|
||||||
tools:text = "URL"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/entry_history_username"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"/>
|
android:layout_weight="1" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/entry_history_title"
|
||||||
|
tools:text = "Title"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
||||||
|
android:maxLines="6"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/entry_history_username"
|
||||||
|
tools:text = "Username"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
||||||
|
android:maxLines="6"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
</LinearLayout>
|
||||||
@@ -25,15 +25,20 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/KeepassDXStyle.Selectable.Item">
|
style="@style/KeepassDXStyle.Selectable.Item">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
|
android:minHeight="56dp"
|
||||||
|
android:maxHeight="72dp"
|
||||||
app:layout_constraintWidth_percent="@dimen/content_percent"
|
app:layout_constraintWidth_percent="@dimen/content_percent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:background="?android:attr/selectableItemBackground" >
|
android:background="?android:attr/selectableItemBackground" >
|
||||||
|
|
||||||
<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"
|
||||||
@@ -44,67 +49,129 @@
|
|||||||
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
||||||
android:layout_marginRight="@dimen/image_list_margin_vertical"
|
android:layout_marginRight="@dimen/image_list_margin_vertical"
|
||||||
android:layout_marginEnd="@dimen/image_list_margin_vertical"
|
android:layout_marginEnd="@dimen/image_list_margin_vertical"
|
||||||
|
android:layout_gravity="center"
|
||||||
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_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent" />
|
app:layout_constraintLeft_toLeftOf="parent" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
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:orientation="vertical"
|
android:paddingTop="4dp"
|
||||||
android:paddingTop="2dp"
|
|
||||||
android:paddingBottom="4dp"
|
android:paddingBottom="4dp"
|
||||||
android:layout_marginLeft="@dimen/image_list_margin_vertical"
|
|
||||||
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
||||||
android:layout_marginRight="@dimen/image_list_margin_vertical"
|
android:layout_marginLeft="@dimen/image_list_margin_vertical"
|
||||||
android:layout_marginEnd="@dimen/image_list_margin_vertical"
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_marginRight="12dp"
|
||||||
|
android:orientation="vertical"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/node_icon"
|
app:layout_constraintStart_toEndOf="@+id/node_icon"
|
||||||
app:layout_constraintLeft_toRightOf="@+id/node_icon"
|
app:layout_constraintLeft_toRightOf="@+id/node_icon"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/node_attachment_icon"
|
app:layout_constraintEnd_toStartOf="@+id/node_options"
|
||||||
app:layout_constraintRight_toLeftOf="@+id/node_attachment_icon">
|
app:layout_constraintRight_toLeftOf="@+id/node_options">
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/node_text"
|
android:id="@+id/node_text"
|
||||||
android:layout_height="wrap_content"
|
style="@style/KeepassDXStyle.TextAppearance.Entry.Title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
tools:text="Node Title"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="2"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Entry.Title" />
|
android:maxLines="2"
|
||||||
|
tools:text="Node Title" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/node_subtext"
|
android:id="@+id/node_subtext"
|
||||||
android:layout_height="wrap_content"
|
style="@style/KeepassDXStyle.TextAppearance.Entry.SubTitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="-4dp"
|
android:layout_marginTop="-4dp"
|
||||||
tools:text="Node SubTitle"
|
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Entry.SubTitle" />
|
tools:text="Node SubTitle" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/node_meta"
|
android:id="@+id/node_meta"
|
||||||
android:layout_height="wrap_content"
|
style="@style/KeepassDXStyle.TextAppearance.Entry.Meta"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C"
|
android:layout_height="wrap_content"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Entry.Meta" />
|
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/node_attachment_icon"
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/node_options"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:layout_marginLeft="@dimen/image_list_margin_vertical"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginStart="@dimen/image_list_margin_vertical"
|
android:layout_marginLeft="12dp"
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:layout_marginRight="12dp"
|
|
||||||
android:src="@drawable/ic_attach_file_white_24dp"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Entry.Icon"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
app:layout_constraintRight_toRightOf="parent" />
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/node_otp_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/node_attachment_icon"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/node_otp_token"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Entry.Info"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
tools:text="5136" />
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginLeft="4dp">
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/node_otp_progress"
|
||||||
|
style="@style/KeepassDXStyle.ProgressBar.Circle.Secondary"
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="60" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/node_otp_counter"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Entry.Info"
|
||||||
|
android:textSize="11sp"
|
||||||
|
tools:text="70" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/node_attachment_icon"
|
||||||
|
style="@style/KeepassDXStyle.TextAppearance.Entry.Icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_marginRight="12dp"
|
||||||
|
android:src="@drawable/ic_attach_file_white_24dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/node_otp_container" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -25,12 +25,16 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/KeepassDXStyle.Selectable.Item">
|
style="@style/KeepassDXStyle.Selectable.Item">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
|
android:minHeight="56dp"
|
||||||
|
android:maxHeight="72dp"
|
||||||
app:layout_constraintWidth_percent="@dimen/content_percent"
|
app:layout_constraintWidth_percent="@dimen/content_percent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:background="?android:attr/selectableItemBackground" >
|
android:background="?android:attr/selectableItemBackground" >
|
||||||
@@ -66,14 +70,14 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_toEndOf="@+id/node_icon"
|
|
||||||
android:layout_toRightOf="@+id/node_icon"
|
|
||||||
android:layout_toStartOf="@+id/node_image_identifier"
|
android:layout_toStartOf="@+id/node_image_identifier"
|
||||||
android:layout_toLeftOf="@+id/node_image_identifier"
|
android:layout_toLeftOf="@+id/node_image_identifier"
|
||||||
android:orientation="vertical"
|
android:layout_toEndOf="@+id/node_icon"
|
||||||
android:paddingTop="2dp"
|
android:layout_toRightOf="@+id/node_icon"
|
||||||
android:paddingBottom="4dp">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/node_text"
|
android:id="@+id/node_text"
|
||||||
@@ -84,25 +88,27 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
tools:text="Node Title" />
|
tools:text="Node Title" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/node_subtext"
|
android:id="@+id/node_subtext"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Group.SubTitle"
|
style="@style/KeepassDXStyle.TextAppearance.Group.SubTitle"
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="-4dp"
|
android:layout_marginTop="-4dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
|
android:visibility="gone"
|
||||||
tools:text="Node SubTitle" />
|
tools:text="Node SubTitle" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/node_meta"
|
android:id="@+id/node_meta"
|
||||||
android:layout_height="wrap_content"
|
style="@style/KeepassDXStyle.TextAppearance.Group.Meta"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C"
|
android:layout_height="wrap_content"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Group.Meta" />
|
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
|||||||
@@ -27,10 +27,10 @@
|
|||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:paddingStart="6dp"
|
android:paddingStart="@dimen/default_margin"
|
||||||
android:paddingLeft="6dp"
|
android:paddingLeft="@dimen/default_margin"
|
||||||
android:paddingEnd="6dp"
|
android:paddingEnd="@dimen/default_margin"
|
||||||
android:paddingRight="6dp"
|
android:paddingRight="@dimen/default_margin"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Large"
|
style="@style/KeepassDXStyle.TextAppearance.Large"
|
||||||
tools:text="Test" />
|
tools:text="Test" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingTop="2dp"
|
|
||||||
android:paddingBottom="2dp"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/entry_field_label"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/entry_field_show"
|
|
||||||
tools:text="title"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/entry_field_value"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/entry_field_label"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/entry_field_show"
|
|
||||||
tools:text="value"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem" />
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
|
||||||
android:id="@+id/entry_field_show"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/entry_field_copy"
|
|
||||||
android:src="@drawable/ic_visibility_state"
|
|
||||||
android:contentDescription="@string/menu_showpass"
|
|
||||||
style="@style/KeepassDXStyle.ImageButton.Simple"/>
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
|
||||||
android:id="@+id/entry_field_copy"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
android:src="@drawable/ic_content_copy_white_24dp"
|
|
||||||
android:contentDescription="@string/menu_copy"
|
|
||||||
style="@style/KeepassDXStyle.ImageButton.Simple"/>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -17,22 +17,20 @@
|
|||||||
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
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
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"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
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"
|
android:visibility="gone">
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/template_fields_container">
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -61,8 +59,6 @@
|
|||||||
android:id="@+id/template_fields_container"
|
android:id="@+id/template_fields_container"
|
||||||
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/template_header_container"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/not_referenced_fields_container"
|
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<com.kunzisoft.keepass.view.SectionView
|
<com.kunzisoft.keepass.view.SectionView
|
||||||
@@ -70,16 +66,12 @@
|
|||||||
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:layout_marginBottom="@dimen/card_view_margin_vertical"
|
android:layout_marginBottom="@dimen/card_view_margin_vertical"/>
|
||||||
app:layout_constraintTop_toBottomOf="@+id/template_fields_container"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/custom_fields_container"/>
|
|
||||||
|
|
||||||
<com.kunzisoft.keepass.view.SectionView
|
<com.kunzisoft.keepass.view.SectionView
|
||||||
android:id="@+id/custom_fields_container"
|
android:id="@+id/custom_fields_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"/>
|
||||||
app:layout_constraintTop_toBottomOf="@+id/not_referenced_fields_container"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -20,13 +20,13 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item android:id="@+id/menu_save_database"
|
<item android:id="@+id/menu_save_database"
|
||||||
android:icon="@drawable/ic_save_white_24dp"
|
android:icon="@drawable/ic_saving_white_24dp"
|
||||||
android:title="@string/menu_save_database"
|
android:title="@string/menu_save_database"
|
||||||
android:orderInCategory="95"
|
android:orderInCategory="95"
|
||||||
app:iconTint="?attr/colorControlNormal"
|
app:iconTint="?attr/colorControlNormal"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
<item android:id="@+id/menu_reload_database"
|
<item android:id="@+id/menu_reload_database"
|
||||||
android:icon="@drawable/ic_reload_white_24dp"
|
android:icon="@drawable/ic_downloading_white_24dp"
|
||||||
android:title="@string/menu_reload_database"
|
android:title="@string/menu_reload_database"
|
||||||
android:orderInCategory="96"
|
android:orderInCategory="96"
|
||||||
app:iconTint="?attr/colorControlNormal"
|
app:iconTint="?attr/colorControlNormal"
|
||||||
|
|||||||
@@ -33,5 +33,5 @@
|
|||||||
<dimen name="hidden_lock_button_size">0dp</dimen>
|
<dimen name="hidden_lock_button_size">0dp</dimen>
|
||||||
<dimen name="content_percent">1</dimen>
|
<dimen name="content_percent">1</dimen>
|
||||||
<dimen name="toolbar_parallax_height">160dp</dimen>
|
<dimen name="toolbar_parallax_height">160dp</dimen>
|
||||||
<integer name="animation_duration">320</integer>
|
<integer name="animation_duration">260</integer>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -190,6 +190,8 @@
|
|||||||
<bool name="monospace_font_fields_enable_default" translatable="false">true</bool>
|
<bool name="monospace_font_fields_enable_default" translatable="false">true</bool>
|
||||||
<string name="hide_expired_entries_key" translatable="false">hide_expired_entries_key</string>
|
<string name="hide_expired_entries_key" translatable="false">hide_expired_entries_key</string>
|
||||||
<bool name="hide_expired_entries_default" translatable="false">false</bool>
|
<bool name="hide_expired_entries_default" translatable="false">false</bool>
|
||||||
|
<string name="show_otp_token_key" translatable="false">show_otp_token_key</string>
|
||||||
|
<bool name="show_otp_token_default" translatable="false">true</bool>
|
||||||
<string name="show_uuid_key" translatable="false">show_uuid_key</string>
|
<string name="show_uuid_key" translatable="false">show_uuid_key</string>
|
||||||
<bool name="show_uuid_default" translatable="false">false</bool>
|
<bool name="show_uuid_default" translatable="false">false</bool>
|
||||||
<string name="enable_education_screens_key" translatable="false">enable_education_screens_key</string>
|
<string name="enable_education_screens_key" translatable="false">enable_education_screens_key</string>
|
||||||
|
|||||||
@@ -628,5 +628,7 @@
|
|||||||
<string name="hide_expired_entries_title">Hide expired entries</string>
|
<string name="hide_expired_entries_title">Hide expired entries</string>
|
||||||
<string name="hide_expired_entries_summary">Expired entries are not shown</string>
|
<string name="hide_expired_entries_summary">Expired entries are not shown</string>
|
||||||
<string name="show_uuid_title">Show UUID</string>
|
<string name="show_uuid_title">Show UUID</string>
|
||||||
<string name="show_uuid_summary">Displays the UUID linked to an entry</string>
|
<string name="show_uuid_summary">Displays the UUID linked to an entry or a group</string>
|
||||||
|
<string name="show_otp_token_title">Show OTP Token</string>
|
||||||
|
<string name="show_otp_token_summary">Displays OTP tokens in the list of entries</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -245,8 +245,8 @@
|
|||||||
<item name="colorControlActivated">?attr/colorAccent</item>
|
<item name="colorControlActivated">?attr/colorAccent</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeepassDXStyle.TextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
<style name="KeepassDXStyle.TextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||||
<item name="android:paddingTop">1dp</item>
|
<item name="android:paddingTop">2dp</item>
|
||||||
<item name="android:paddingBottom">1dp</item>
|
<item name="android:paddingBottom">2dp</item>
|
||||||
<item name="boxStrokeColor">@color/edit_text_stroke_color</item>
|
<item name="boxStrokeColor">@color/edit_text_stroke_color</item>
|
||||||
<item name="hintTextColor">?attr/colorAccent</item>
|
<item name="hintTextColor">?attr/colorAccent</item>
|
||||||
<item name="materialThemeOverlay">@style/ThemeOverlay.KeepassDXTheme.TextInputEditText</item>
|
<item name="materialThemeOverlay">@style/ThemeOverlay.KeepassDXTheme.TextInputEditText</item>
|
||||||
@@ -352,35 +352,41 @@
|
|||||||
|
|
||||||
<!-- Nodes Text Style -->
|
<!-- Nodes Text Style -->
|
||||||
<style name="KeepassDXStyle.TextAppearance.Entry.Title" parent="KeepassDXStyle.TextAppearance">
|
<style name="KeepassDXStyle.TextAppearance.Entry.Title" parent="KeepassDXStyle.TextAppearance">
|
||||||
<item name="android:textColor">@color/entry_title_color</item>
|
<item name="android:textColor">@color/list_color</item>
|
||||||
<item name="android:tint">@color/entry_title_color</item>
|
<item name="android:tint">@color/list_color</item>
|
||||||
|
<item name="android:textStyle">bold</item>
|
||||||
<item name="android:textSize">16sp</item>
|
<item name="android:textSize">16sp</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeepassDXStyle.TextAppearance.Entry.SubTitle" parent="KeepassDXStyle.TextAppearance.Small">
|
<style name="KeepassDXStyle.TextAppearance.Entry.SubTitle" parent="KeepassDXStyle.TextAppearance.Small">
|
||||||
<item name="android:textColor">@color/entry_subtitle_color</item>
|
<item name="android:textColor">@color/list_secondary_color</item>
|
||||||
<item name="android:tint">@color/entry_subtitle_color</item>
|
<item name="android:tint">@color/list_secondary_color</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeepassDXStyle.TextAppearance.Entry.Meta" parent="KeepassDXStyle.TextAppearance.Tiny">
|
<style name="KeepassDXStyle.TextAppearance.Entry.Meta" parent="KeepassDXStyle.TextAppearance.Tiny">
|
||||||
<item name="android:textColor">@color/entry_title_color</item>
|
<item name="android:textColor">@color/list_color</item>
|
||||||
<item name="android:tint">@color/entry_title_color</item>
|
<item name="android:tint">@color/list_color</item>
|
||||||
<item name="android:textSize">11sp</item>
|
<item name="android:textSize">11sp</item>
|
||||||
</style>
|
</style>
|
||||||
|
<style name="KeepassDXStyle.TextAppearance.Entry.Info" parent="KeepassDXStyle.TextAppearance.Small">
|
||||||
|
<item name="android:textColor">@color/list_secondary_color</item>
|
||||||
|
<item name="android:tint">@color/list_secondary_color</item>
|
||||||
|
<item name="android:textSize">16sp</item>
|
||||||
|
</style>
|
||||||
<style name="KeepassDXStyle.TextAppearance.Entry.Icon" parent="KeepassDXStyle.TextAppearance.Small">
|
<style name="KeepassDXStyle.TextAppearance.Entry.Icon" parent="KeepassDXStyle.TextAppearance.Small">
|
||||||
<item name="tint">@color/entry_subtitle_color</item>
|
<item name="tint">@color/list_secondary_color</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeepassDXStyle.TextAppearance.Group.Title" parent="KeepassDXStyle.TextAppearance">
|
<style name="KeepassDXStyle.TextAppearance.Group.Title" parent="KeepassDXStyle.TextAppearance">
|
||||||
<item name="android:textColor">@color/group_title_color</item>
|
<item name="android:textColor">@color/list_primary_color</item>
|
||||||
<item name="android:tint">@color/group_title_color</item>
|
<item name="android:tint">@color/list_primary_color</item>
|
||||||
<item name="android:textStyle">bold</item>
|
<item name="android:textStyle">bold</item>
|
||||||
<item name="android:textSize">16sp</item>
|
<item name="android:textSize">16sp</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeepassDXStyle.TextAppearance.Group.SubTitle" parent="KeepassDXStyle.TextAppearance.Small">
|
<style name="KeepassDXStyle.TextAppearance.Group.SubTitle" parent="KeepassDXStyle.TextAppearance.Small">
|
||||||
<item name="android:textColor">@color/group_subtitle_color</item>
|
<item name="android:textColor">@color/list_secondary_color</item>
|
||||||
<item name="android:tint">@color/group_subtitle_color</item>
|
<item name="android:tint">@color/list_secondary_color</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeepassDXStyle.TextAppearance.Group.Meta" parent="KeepassDXStyle.TextAppearance.Tiny">
|
<style name="KeepassDXStyle.TextAppearance.Group.Meta" parent="KeepassDXStyle.TextAppearance.Tiny">
|
||||||
<item name="android:textColor">@color/group_title_color</item>
|
<item name="android:textColor">@color/list_primary_color</item>
|
||||||
<item name="android:tint">@color/group_title_color</item>
|
<item name="android:tint">@color/list_primary_color</item>
|
||||||
<item name="android:textSize">11sp</item>
|
<item name="android:textSize">11sp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -463,18 +469,29 @@
|
|||||||
<item name="android:textColor">?attr/colorAccent</item>
|
<item name="android:textColor">?attr/colorAccent</item>
|
||||||
<item name="android:textSize">12sp</item>
|
<item name="android:textSize">12sp</item>
|
||||||
<item name="android:paddingLeft">4dp</item>
|
<item name="android:paddingLeft">4dp</item>
|
||||||
|
<item name="android:paddingStart">4dp</item>
|
||||||
<item name="android:paddingRight">4dp</item>
|
<item name="android:paddingRight">4dp</item>
|
||||||
|
<item name="android:paddingEnd">4dp</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeepassDXStyle.TextAppearance.LabelTableTextStyle" parent="KeepassDXStyle.TextAppearance">
|
<style name="KeepassDXStyle.TextAppearance.LabelTableTextStyle" parent="KeepassDXStyle.TextAppearance">
|
||||||
<item name="android:textSize">12sp</item>
|
<item name="android:textSize">12sp</item>
|
||||||
<item name="android:textStyle">bold</item>
|
<item name="android:textStyle">bold</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeepassDXStyle.TextAppearance.TextEntryItem" parent="KeepassDXStyle.TextAppearance">
|
<style name="KeepassDXStyle.TextAppearance.TextEntryItem" parent="KeepassDXStyle.TextAppearance">
|
||||||
<item name="android:padding">5sp</item>
|
<item name="android:layout_marginTop">4dp</item>
|
||||||
|
<item name="android:layout_marginBottom">4dp</item>
|
||||||
|
<item name="android:paddingLeft">8dp</item>
|
||||||
|
<item name="android:paddingStart">8dp</item>
|
||||||
|
<item name="android:paddingRight">8dp</item>
|
||||||
|
<item name="android:paddingEnd">8dp</item>
|
||||||
<item name="android:textSize">16sp</item>
|
<item name="android:textSize">16sp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Progress bar -->
|
<!-- Progress bar -->
|
||||||
|
<style name="KeepassDXStyle.ProgressBar.Circle.Secondary" parent="Widget.AppCompat.ProgressBar.Horizontal">
|
||||||
|
<item name="android:progressDrawable">@drawable/foreground_progress_circle_secondary</item>
|
||||||
|
<item name="android:background">@null</item>
|
||||||
|
</style>
|
||||||
<style name="KeepassDXStyle.ProgressBar.Circle" parent="Widget.AppCompat.ProgressBar.Horizontal">
|
<style name="KeepassDXStyle.ProgressBar.Circle" parent="Widget.AppCompat.ProgressBar.Horizontal">
|
||||||
<item name="android:progressDrawable">@drawable/foreground_progress_circle</item>
|
<item name="android:progressDrawable">@drawable/foreground_progress_circle</item>
|
||||||
<item name="android:background">@drawable/background_progress_circle</item>
|
<item name="android:background">@drawable/background_progress_circle</item>
|
||||||
|
|||||||
@@ -80,6 +80,11 @@
|
|||||||
android:title="@string/hide_expired_entries_title"
|
android:title="@string/hide_expired_entries_title"
|
||||||
android:summary="@string/hide_expired_entries_summary"
|
android:summary="@string/hide_expired_entries_summary"
|
||||||
android:defaultValue="@bool/hide_expired_entries_default"/>
|
android:defaultValue="@bool/hide_expired_entries_default"/>
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="@string/show_otp_token_key"
|
||||||
|
android:title="@string/show_otp_token_title"
|
||||||
|
android:summary="@string/show_otp_token_summary"
|
||||||
|
android:defaultValue="@bool/show_otp_token_default"/>
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:key="@string/show_uuid_key"
|
android:key="@string/show_uuid_key"
|
||||||
android:title="@string/show_uuid_title"
|
android:title="@string/show_uuid_title"
|
||||||
|
|||||||
59
art/reload_database.svg
Normal file
59
art/reload_database.svg
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="Save.svg"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||||
|
<metadata
|
||||||
|
id="metadata10">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs8" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#c8c8c8"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0.28235294"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1016"
|
||||||
|
id="namedview6"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="11.313709"
|
||||||
|
inkscape:cx="10.124302"
|
||||||
|
inkscape:cy="14.525681"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg4">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid818" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff"
|
||||||
|
d="M 11 2.0566406 C 6.762335 2.4220229 3.0067094 5.7987155 2.203125 9.9785156 C 1.3601754 13.960549 3.1781148 18.394742 6.7089844 20.480469 C 9.6237318 22.368157 13.514425 22.492178 16.582031 20.892578 C 17.959775 20.180473 19.316015 19.099467 20.087891 17.808594 L 18.402344 16.791016 C 16.277892 19.724364 12.039121 20.844607 8.7519531 19.306641 C 5.4810648 17.911181 3.4461927 14.150571 4.109375 10.648438 C 4.6649663 7.2806973 7.5784749 4.4226117 11 4.0839844 L 11 2.0566406 z M 13 2.0644531 L 13 4.09375 C 16.367309 4.4801388 19.308004 7.2166098 19.861328 10.574219 C 20.123351 12.069186 19.935392 13.632673 19.367188 15.037109 C 19.94644 15.387646 20.527063 15.73602 21.105469 16.087891 C 22.671735 12.714066 22.12099 8.4920873 19.708984 5.6542969 C 18.063396 3.6246553 15.604973 2.2995703 13 2.0644531 z M 11 7.0644531 L 11 12.064453 L 7 12.064453 L 12 17.064453 L 17 12.064453 L 13 12.064453 L 13 7.0644531 L 11 7.0644531 z "
|
||||||
|
id="path868" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
59
art/save_database.svg
Normal file
59
art/save_database.svg
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="Save2.svg"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||||
|
<metadata
|
||||||
|
id="metadata10">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs8" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#c8c8c8"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0.28235294"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1016"
|
||||||
|
id="namedview6"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="11.313709"
|
||||||
|
inkscape:cx="-2.2942603"
|
||||||
|
inkscape:cy="14.525681"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg4">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid818" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff"
|
||||||
|
d="M 11 2.0566406 C 6.762335 2.4220229 3.0067094 5.7987155 2.203125 9.9785156 C 1.3601753 13.960549 3.1781148 18.394742 6.7089844 20.480469 C 9.6237318 22.368157 13.514425 22.492178 16.582031 20.892578 C 17.959775 20.180473 19.316015 19.099467 20.087891 17.808594 L 18.402344 16.791016 C 16.277892 19.724364 12.039121 20.844605 8.7519531 19.306641 C 5.481064 17.911182 3.4461934 14.150571 4.109375 10.648438 C 4.6649664 7.2806974 7.5784749 4.4226117 11 4.0839844 L 11 2.0566406 z M 13 2.0644531 L 13 4.09375 C 16.367309 4.4801387 19.308002 7.2166101 19.861328 10.574219 C 20.123352 12.069186 19.935393 13.632674 19.367188 15.037109 C 19.94644 15.387646 20.527063 15.73602 21.105469 16.087891 C 22.671737 12.714066 22.120988 8.4920871 19.708984 5.6542969 C 18.063396 3.6246553 15.604973 2.2995704 13 2.0644531 z M 8.1113281 7 C 7.4946611 7 7 7.5002173 7 8.1113281 L 7 15.888672 C 7 16.499784 7.4946611 17 8.1113281 17 L 15.888672 17 C 16.499783 17 17 16.499784 17 15.888672 L 17 9.2226562 L 14.777344 7 L 8.1113281 7 z M 8.1113281 8.1113281 L 13.666016 8.1113281 L 13.666016 10.333984 L 8.1113281 10.333984 L 8.1113281 8.1113281 z M 12 12.554688 C 12.922223 12.554688 13.666016 13.300434 13.666016 14.222656 C 13.666016 15.144878 12.922223 15.888672 12 15.888672 C 11.077779 15.888672 10.333984 15.144878 10.333984 14.222656 C 10.333984 13.300434 11.077779 12.554687 12 12.554688 z "
|
||||||
|
id="path868" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -1,5 +1,6 @@
|
|||||||
* Add / Manage dynamic templates #191
|
* Add / Manage dynamic templates #191
|
||||||
* Allow to manually select RecycleBin group and Templates group #191
|
* Allow to manually select RecycleBin group and Templates group #191
|
||||||
|
* Setting to display OTP Token in list #655
|
||||||
* Fix timeout in dialogs #716
|
* Fix timeout in dialogs #716
|
||||||
* Check URI permissions #626
|
* Check URI permissions #626
|
||||||
* Small changes #1035 #1043 #942
|
* Improvements #1035 #1043 #942 #1021 #1027
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
* Ajout / Gestion dynamique des templates #191
|
* Ajout / Gestion dynamique des templates #191
|
||||||
* Sélection manuelle des groupes de la corbeille et des templates #191
|
* Sélection manuelle des groupes de la corbeille et des templates #191
|
||||||
|
* Paramètres pour afficher les jetons OTP dans la liste #655
|
||||||
* Correction du délai d'expiration dans les dialogues #716
|
* Correction du délai d'expiration dans les dialogues #716
|
||||||
* Vérification des permissions URI #626
|
* Vérification des permissions URI #626
|
||||||
* Petits changements #1035 #1043 #942
|
* Améliorations #1035 #1043 #942 #1021 #1027
|
||||||
Reference in New Issue
Block a user