Add group expiration and fix bugs in group info

This commit is contained in:
J-Jamet
2021-02-11 19:13:30 +01:00
parent c56b4964fe
commit 1eea5412a5
14 changed files with 338 additions and 160 deletions

View File

@@ -350,7 +350,7 @@ class EntryActivity : LockingActivity() {
// Assign dates
entryContentsView?.assignCreationDate(entryInfo.creationTime)
entryContentsView?.assignModificationDate(entryInfo.modificationTime)
entryContentsView?.assignModificationDate(entryInfo.lastModificationTime)
entryContentsView?.setExpires(entryInfo.expires, entryInfo.expiryTime)
// Manage history

View File

@@ -205,7 +205,7 @@ class EntryEditActivity : LockingActivity(),
.commit()
entryEditFragment?.apply {
drawFactory = mDatabase?.drawFactory
setOnDateClickListener = View.OnClickListener {
setOnDateClickListener = {
expiryTime.date.let { expiresDate ->
val dateTime = DateTime(expiresDate)
val defaultYear = dateTime.year

View File

@@ -26,10 +26,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
@@ -50,6 +48,7 @@ import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.model.*
import com.kunzisoft.keepass.otp.OtpEntryFields
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.view.ExpirationView
import com.kunzisoft.keepass.view.applyFontVisibility
import com.kunzisoft.keepass.view.collapse
import com.kunzisoft.keepass.view.expand
@@ -64,8 +63,7 @@ class EntryEditFragment: StylishFragment() {
private lateinit var entryPasswordLayoutView: TextInputLayout
private lateinit var entryPasswordView: EditText
private lateinit var entryPasswordGeneratorView: View
private lateinit var entryExpiresCheckBox: CompoundButton
private lateinit var entryExpiresTextView: TextView
private lateinit var entryExpirationView: ExpirationView
private lateinit var entryNotesView: EditText
private lateinit var extraFieldsContainerView: View
private lateinit var extraFieldsListView: ViewGroup
@@ -76,10 +74,9 @@ class EntryEditFragment: StylishFragment() {
private var fontInVisibility: Boolean = false
private var iconColor: Int = 0
private var expiresInstant: DateInstant = DateInstant.IN_ONE_MONTH
var drawFactory: IconDrawableFactory? = null
var setOnDateClickListener: View.OnClickListener? = null
var setOnDateClickListener: (() -> Unit)? = null
var setOnPasswordGeneratorClickListener: View.OnClickListener? = null
var setOnIconViewClickListener: View.OnClickListener? = null
var setOnEditCustomField: ((Field) -> Unit)? = null
@@ -114,12 +111,8 @@ class EntryEditFragment: StylishFragment() {
entryPasswordGeneratorView.setOnClickListener {
setOnPasswordGeneratorClickListener?.onClick(it)
}
entryExpiresCheckBox = rootView.findViewById(R.id.entry_edit_expires_checkbox)
entryExpiresTextView = rootView.findViewById(R.id.entry_edit_expires_text)
entryExpiresTextView.setOnClickListener {
if (entryExpiresCheckBox.isChecked)
setOnDateClickListener?.onClick(it)
}
entryExpirationView = rootView.findViewById(R.id.entry_edit_expiration)
entryExpirationView.setOnDateClickListener = setOnDateClickListener
entryNotesView = rootView.findViewById(R.id.entry_edit_notes)
@@ -143,10 +136,6 @@ class EntryEditFragment: StylishFragment() {
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
}
entryExpiresCheckBox.setOnCheckedChangeListener { _, _ ->
assignExpiresDateText()
}
// Retrieve the textColor to tint the icon
val taIconColor = contextThemed?.theme?.obtainStyledAttributes(intArrayOf(android.R.attr.textColor))
iconColor = taIconColor?.getColor(0, Color.WHITE) ?: Color.WHITE
@@ -181,7 +170,7 @@ class EntryEditFragment: StylishFragment() {
setOnEditCustomField = null
}
fun getEntryInfo(): EntryInfo? {
fun getEntryInfo(): EntryInfo {
populateEntryWithViews()
return mEntryInfo
}
@@ -286,41 +275,20 @@ class EntryEditFragment: StylishFragment() {
}
}
private fun assignExpiresDateText() {
entryExpiresTextView.text = if (entryExpiresCheckBox.isChecked) {
entryExpiresTextView.setOnClickListener(setOnDateClickListener)
expiresInstant.getDateTimeString(resources)
} else {
entryExpiresTextView.setOnClickListener(null)
resources.getString(R.string.never)
}
if (fontInVisibility)
entryExpiresTextView.applyFontVisibility()
}
var expires: Boolean
get() {
return entryExpiresCheckBox.isChecked
return entryExpirationView.expires
}
set(value) {
if (!value) {
expiresInstant = DateInstant.IN_ONE_MONTH
}
entryExpiresCheckBox.isChecked = value
assignExpiresDateText()
entryExpirationView.expires = value
}
var expiryTime: DateInstant
get() {
return if (expires)
expiresInstant
else
DateInstant.NEVER_EXPIRE
return entryExpirationView.expiryTime
}
set(value) {
if (expires)
expiresInstant = value
assignExpiresDateText()
entryExpirationView.expiryTime = value
}
var notes: String

View File

@@ -19,7 +19,9 @@
package com.kunzisoft.keepass.activities
import android.app.Activity
import android.app.DatePickerDialog
import android.app.SearchManager
import android.app.TimePickerDialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@@ -33,9 +35,7 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import android.widget.*
import androidx.annotation.RequiresApi
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView
@@ -53,10 +53,7 @@ import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrCha
import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter
import com.kunzisoft.keepass.autofill.AutofillComponent
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.SortNodeEnum
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.Type
@@ -80,10 +77,13 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.view.*
import org.joda.time.DateTime
class GroupActivity : LockingActivity(),
GroupEditDialogFragment.EditGroupListener,
IconPickerDialogFragment.IconPickerListener,
DatePickerDialog.OnDateSetListener,
TimePickerDialog.OnTimeSetListener,
ListNodesFragment.NodeClickListener,
ListNodesFragment.NodesActionMenuListener,
DeleteNodesDialogFragment.DeleteNodeListener,
@@ -705,6 +705,39 @@ class GroupActivity : LockingActivity(),
)
}
override fun onDateSet(datePicker: DatePicker?, year: Int, month: Int, day: Int) {
// To fix android 4.4 issue
// https://stackoverflow.com/questions/12436073/datepicker-ondatechangedlistener-called-twice
if (datePicker?.isShown == true) {
val groupEditFragment = supportFragmentManager.findFragmentByTag(GroupEditDialogFragment.TAG_CREATE_GROUP) as? GroupEditDialogFragment
groupEditFragment?.getExpiryTime()?.date?.let { expiresDate ->
groupEditFragment.setExpiryTime(DateInstant(DateTime(expiresDate)
.withYear(year)
.withMonthOfYear(month + 1)
.withDayOfMonth(day)
.toDate()))
// Launch the time picker
val dateTime = DateTime(expiresDate)
val defaultHour = dateTime.hourOfDay
val defaultMinute = dateTime.minuteOfHour
TimePickerFragment.getInstance(defaultHour, defaultMinute)
.show(supportFragmentManager, "TimePickerFragment")
}
}
}
override fun onTimeSet(view: TimePicker?, hours: Int, minutes: Int) {
val groupEditFragment = supportFragmentManager.findFragmentByTag(GroupEditDialogFragment.TAG_CREATE_GROUP) as? GroupEditDialogFragment
groupEditFragment?.getExpiryTime()?.date?.let { expiresDate ->
// Save the date
groupEditFragment.setExpiryTime(
DateInstant(DateTime(expiresDate)
.withHourOfDay(hours)
.withMinuteOfHour(minutes)
.toDate()))
}
}
private fun finishNodeAction() {
actionNodeMode?.finish()
}
@@ -1039,7 +1072,7 @@ class GroupActivity : LockingActivity(),
override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction,
groupInfo: GroupInfo) {
if (groupInfo.name.isNotEmpty()) {
if (groupInfo.title.isNotEmpty()) {
when (action) {
GroupEditDialogFragment.EditGroupDialogAction.CREATION -> {
// If group creation

View File

@@ -34,8 +34,11 @@ import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION
import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.UPDATE
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.model.GroupInfo
import com.kunzisoft.keepass.view.ExpirationView
import org.joda.time.DateTime
class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconPickerListener {
@@ -46,12 +49,13 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
private var mEditGroupDialogAction = EditGroupDialogAction.NONE
private var mGroupInfo = GroupInfo()
private var iconButtonView: ImageView? = null
private lateinit var iconButtonView: ImageView
private var iconColor: Int = 0
private var nameTextLayoutView: TextInputLayout? = null
private var nameTextView: TextView? = null
private var notesTextLayoutView: TextInputLayout? = null
private var notesTextView: TextView? = null
private lateinit var nameTextLayoutView: TextInputLayout
private lateinit var nameTextView: TextView
private lateinit var notesTextLayoutView: TextInputLayout
private lateinit var notesTextView: TextView
private lateinit var expirationView: ExpirationView
enum class EditGroupDialogAction {
CREATION, UPDATE, NONE;
@@ -84,11 +88,12 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
val root = activity.layoutInflater.inflate(R.layout.fragment_group_edit, null)
iconButtonView = root?.findViewById(R.id.group_edit_icon_button)
nameTextLayoutView = root?.findViewById(R.id.group_edit_name_container)
nameTextView = root?.findViewById(R.id.group_edit_name)
notesTextLayoutView = root?.findViewById(R.id.group_edit_note_container)
notesTextView = root?.findViewById(R.id.group_edit_note)
iconButtonView = root.findViewById(R.id.group_edit_icon_button)
nameTextLayoutView = root.findViewById(R.id.group_edit_name_container)
nameTextView = root.findViewById(R.id.group_edit_name)
notesTextLayoutView = root.findViewById(R.id.group_edit_note_container)
notesTextView = root.findViewById(R.id.group_edit_note)
expirationView = root.findViewById(R.id.group_edit_expiration)
// Retrieve the textColor to tint the icon
val ta = activity.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor))
@@ -115,27 +120,30 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
}
}
// populate the icon
assignIconView()
// populate the name
nameTextView?.text = mGroupInfo.name
// populate the note
notesTextLayoutView?.visibility = if (mGroupInfo.notes == null) View.GONE else View.VISIBLE
mGroupInfo.notes?.let {
notesTextView?.text = it
// populate info in views
populateInfoToViews()
expirationView.setOnDateClickListener = {
expirationView.expiryTime.date.let { expiresDate ->
val dateTime = DateTime(expiresDate)
val defaultYear = dateTime.year
val defaultMonth = dateTime.monthOfYear-1
val defaultDay = dateTime.dayOfMonth
DatePickerFragment.getInstance(defaultYear, defaultMonth, defaultDay)
.show(parentFragmentManager, "DatePickerFragment")
}
}
val builder = AlertDialog.Builder(activity)
builder.setView(root)
.setPositiveButton(android.R.string.ok, null)
.setNegativeButton(android.R.string.cancel) { _, _ ->
retrieveGroupInfo()
retrieveGroupInfoFromViews()
mEditGroupListener?.cancelEditGroup(
mEditGroupDialogAction,
mGroupInfo)
}
iconButtonView?.setOnClickListener { _ ->
iconButtonView.setOnClickListener { _ ->
IconPickerDialogFragment().show(parentFragmentManager, "IconPickerDialogFragment")
}
@@ -152,7 +160,7 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
if (d != null) {
val positiveButton = d.getButton(Dialog.BUTTON_POSITIVE) as Button
positiveButton.setOnClickListener {
retrieveGroupInfo()
retrieveGroupInfoFromViews()
if (isValid()) {
mEditGroupListener?.approveEditGroup(
mEditGroupDialogAction,
@@ -163,18 +171,41 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
}
}
private fun retrieveGroupInfo() {
mGroupInfo.name = nameTextView?.text?.toString() ?: ""
fun getExpiryTime(): DateInstant {
retrieveGroupInfoFromViews()
return mGroupInfo.expiryTime
}
fun setExpiryTime(expiryTime: DateInstant) {
mGroupInfo.expiryTime = expiryTime
populateInfoToViews()
}
private fun populateInfoToViews() {
assignIconView()
nameTextView.text = mGroupInfo.title
notesTextLayoutView.visibility = if (mGroupInfo.notes == null) View.GONE else View.VISIBLE
mGroupInfo.notes?.let {
notesTextView.text = it
}
expirationView.expires = mGroupInfo.expires
expirationView.expiryTime = mGroupInfo.expiryTime
}
private fun retrieveGroupInfoFromViews() {
mGroupInfo.title = nameTextView.text.toString()
// Only if there
val newNotes = notesTextView?.text?.toString()
if (newNotes != null && newNotes.isNotEmpty()) {
val newNotes = notesTextView.text.toString()
if (newNotes.isNotEmpty()) {
mGroupInfo.notes = newNotes
}
mGroupInfo.expires = expirationView.expires
mGroupInfo.expiryTime = expirationView.expiryTime
}
private fun assignIconView() {
if (mDatabase?.drawFactory != null) {
iconButtonView?.assignDatabaseIcon(mDatabase?.drawFactory!!, mGroupInfo.icon, iconColor)
iconButtonView.assignDatabaseIcon(mDatabase?.drawFactory!!, mGroupInfo.icon, iconColor)
}
}
@@ -184,14 +215,15 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
}
override fun onSaveInstanceState(outState: Bundle) {
retrieveGroupInfoFromViews()
outState.putInt(KEY_ACTION_ID, mEditGroupDialogAction.ordinal)
outState.putParcelable(KEY_GROUP_INFO, mGroupInfo)
super.onSaveInstanceState(outState)
}
private fun isValid(): Boolean {
if (nameTextView?.text?.toString()?.isNotEmpty() != true) {
nameTextLayoutView?.error = getString(R.string.error_no_name)
if (nameTextView.text.toString().isEmpty()) {
nameTextLayoutView.error = getString(R.string.error_no_name)
return false
}
return true

View File

@@ -421,7 +421,7 @@ class Entry : Node, EntryVersionedInterface<Group> {
entryInfo.username = username
entryInfo.password = password
entryInfo.creationTime = creationTime
entryInfo.modificationTime = lastModificationTime
entryInfo.lastModificationTime = lastModificationTime
entryInfo.expires = expires
entryInfo.expiryTime = expiryTime
entryInfo.url = url

View File

@@ -408,15 +408,24 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
fun getGroupInfo(): GroupInfo {
val groupInfo = GroupInfo()
groupInfo.name = title
groupInfo.title = title
groupInfo.icon = icon
groupInfo.creationTime = creationTime
groupInfo.lastModificationTime = lastModificationTime
groupInfo.expires = expires
groupInfo.expiryTime = expiryTime
groupInfo.notes = notes
return groupInfo
}
fun setGroupInfo(groupInfo: GroupInfo) {
title = groupInfo.name
title = groupInfo.title
icon = groupInfo.icon
// Update date time, creation time stay as is
lastModificationTime = DateInstant()
lastAccessTime = DateInstant()
expires = groupInfo.expires
expiryTime = groupInfo.expiryTime
notes = groupInfo.notes
}

View File

@@ -23,44 +23,28 @@ import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.database.element.Attachment
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.otp.OtpElement
import com.kunzisoft.keepass.otp.OtpEntryFields
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD
import kotlin.collections.ArrayList
class EntryInfo : Parcelable {
class EntryInfo : NodeInfo {
var id: String = ""
var title: String = ""
var icon: IconImage = IconImageStandard()
var username: String = ""
var password: String = ""
var creationTime: DateInstant = DateInstant()
var modificationTime: DateInstant = DateInstant()
var expires: Boolean = false
var expiryTime: DateInstant = DateInstant.IN_ONE_MONTH
var url: String = ""
var notes: String = ""
var customFields: List<Field> = ArrayList()
var attachments: List<Attachment> = ArrayList()
var otpModel: OtpModel? = null
constructor()
constructor(): super()
private constructor(parcel: Parcel) {
constructor(parcel: Parcel): super(parcel) {
id = parcel.readString() ?: id
title = parcel.readString() ?: title
icon = parcel.readParcelable(IconImage::class.java.classLoader) ?: icon
username = parcel.readString() ?: username
password = parcel.readString() ?: password
creationTime = parcel.readParcelable(DateInstant::class.java.classLoader) ?: creationTime
modificationTime = parcel.readParcelable(DateInstant::class.java.classLoader) ?: modificationTime
expires = parcel.readInt() != 0
expiryTime = parcel.readParcelable(DateInstant::class.java.classLoader) ?: expiryTime
url = parcel.readString() ?: url
notes = parcel.readString() ?: notes
parcel.readList(customFields, Field::class.java.classLoader)
@@ -73,15 +57,10 @@ class EntryInfo : Parcelable {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
super.writeToParcel(parcel, flags)
parcel.writeString(id)
parcel.writeString(title)
parcel.writeParcelable(icon, flags)
parcel.writeString(username)
parcel.writeString(password)
parcel.writeParcelable(creationTime, flags)
parcel.writeParcelable(modificationTime, flags)
parcel.writeInt(if (expires) 1 else 0)
parcel.writeParcelable(expiryTime, flags)
parcel.writeString(url)
parcel.writeString(notes)
parcel.writeArray(customFields.toTypedArray())

View File

@@ -2,31 +2,28 @@ package com.kunzisoft.keepass.model
import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.FOLDER
class GroupInfo() : Parcelable {
class GroupInfo : NodeInfo {
var name: String = ""
var icon: IconImage = IconImageStandard()
var notes: String? = null
constructor(parcel: Parcel) : this() {
name = parcel.readString() ?: name
icon = parcel.readParcelable(IconImage::class.java.classLoader) ?: icon
init {
icon = IconImageStandard(FOLDER)
}
constructor(): super()
constructor(parcel: Parcel): super(parcel) {
notes = parcel.readString()
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
parcel.writeParcelable(icon, flags)
super.writeToParcel(parcel, flags)
parcel.writeString(notes)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<GroupInfo> {
override fun createFromParcel(parcel: Parcel): GroupInfo {
return GroupInfo(parcel)

View File

@@ -0,0 +1,49 @@
package com.kunzisoft.keepass.model
import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
open class NodeInfo() : Parcelable {
var title: String = ""
var icon: IconImage = IconImageStandard()
var creationTime: DateInstant = DateInstant()
var lastModificationTime: DateInstant = DateInstant()
var expires: Boolean = false
var expiryTime: DateInstant = DateInstant.IN_ONE_MONTH
constructor(parcel: Parcel) : this() {
title = parcel.readString() ?: title
icon = parcel.readParcelable(IconImage::class.java.classLoader) ?: icon
creationTime = parcel.readParcelable(DateInstant::class.java.classLoader) ?: creationTime
lastModificationTime = parcel.readParcelable(DateInstant::class.java.classLoader) ?: lastModificationTime
expires = parcel.readInt() != 0
expiryTime = parcel.readParcelable(DateInstant::class.java.classLoader) ?: expiryTime
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(title)
parcel.writeParcelable(icon, flags)
parcel.writeParcelable(creationTime, flags)
parcel.writeParcelable(lastModificationTime, flags)
parcel.writeInt(if (expires) 1 else 0)
parcel.writeParcelable(expiryTime, flags)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<NodeInfo> {
override fun createFromParcel(parcel: Parcel): NodeInfo {
return NodeInfo(parcel)
}
override fun newArray(size: Int): Array<NodeInfo?> {
return arrayOfNulls(size)
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.view
import android.content.Context
import android.text.util.Linkify
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.CompoundButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.text.util.LinkifyCompat
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.model.EntryInfo.Companion.APPLICATION_ID_FIELD_NAME
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.UriUtil
class ExpirationView @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0)
: ConstraintLayout(context, attrs, defStyle) {
private var entryExpiresTextView: TextView
private var entryExpiresCheckBox: CompoundButton
private var expiresInstant: DateInstant = DateInstant.IN_ONE_MONTH
private var fontInVisibility: Boolean = false
var setOnDateClickListener: (() -> Unit)? = null
init {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
inflater?.inflate(R.layout.view_expiration, this)
entryExpiresTextView = findViewById(R.id.expiration_text)
entryExpiresCheckBox = findViewById(R.id.expiration_checkbox)
entryExpiresTextView.setOnClickListener {
if (entryExpiresCheckBox.isChecked)
setOnDateClickListener?.invoke()
}
entryExpiresCheckBox.setOnCheckedChangeListener { _, _ ->
assignExpiresDateText()
}
fontInVisibility = PreferencesUtil.fieldFontIsInVisibility(context)
}
private fun assignExpiresDateText() {
entryExpiresTextView.text = if (entryExpiresCheckBox.isChecked) {
expiresInstant.getDateTimeString(resources)
} else {
resources.getString(R.string.never)
}
if (fontInVisibility)
entryExpiresTextView.applyFontVisibility()
}
var expires: Boolean
get() {
return entryExpiresCheckBox.isChecked
}
set(value) {
if (!value) {
expiresInstant = DateInstant.IN_ONE_MONTH
}
entryExpiresCheckBox.isChecked = value
assignExpiresDateText()
}
var expiryTime: DateInstant
get() {
return if (expires)
expiresInstant
else
DateInstant.NEVER_EXPIRE
}
set(value) {
if (expires)
expiresInstant = value
assignExpiresDateText()
}
}

View File

@@ -141,45 +141,11 @@
android:hint="@string/entry_url"/>
</com.google.android.material.textfield.TextInputLayout>
<!-- Expires -->
<androidx.constraintlayout.widget.ConstraintLayout
<!-- Expiration -->
<com.kunzisoft.keepass.view.ExpirationView
android:id="@+id/entry_edit_expiration"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/entry_edit_expires_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:text="@string/entry_expires"
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/entry_edit_expires_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
app:layout_constraintTop_toBottomOf="@+id/entry_edit_expires_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
style="@style/KeepassDXStyle.TextAppearance.Large"
tools:text="2020-03-04 05:00"/>
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/entry_edit_expires_presets"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/entry_edit_expires_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/entry_edit_expires_text"
app:layout_constraintEnd_toStartOf="@+id/entry_edit_expires_checkbox"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/entry_edit_expires_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/entry_edit_expires_label"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
android:layout_height="wrap_content" />
<!-- Notes -->
<com.google.android.material.textfield.TextInputLayout

View File

@@ -62,5 +62,10 @@
android:maxLines="5"
android:hint="@string/entry_notes"/>
</com.google.android.material.textfield.TextInputLayout>
<com.kunzisoft.keepass.view.ExpirationView
android:id="@+id/group_edit_expiration"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp" />
</LinearLayout>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/expiration_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:text="@string/entry_expires"
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/expiration_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
app:layout_constraintTop_toBottomOf="@+id/expiration_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
style="@style/KeepassDXStyle.TextAppearance.Large"
tools:text="2020-03-04 05:00"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/expiration_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/expiration_label"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>