Merge branch 'feature/Edit_Expired_Date' into develop

This commit is contained in:
J-Jamet
2020-03-18 11:53:46 +01:00
8 changed files with 302 additions and 37 deletions

View File

@@ -19,6 +19,8 @@
package com.kunzisoft.keepass.activities
import android.app.Activity
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.Intent
import android.os.Bundle
import android.os.Handler
@@ -26,14 +28,14 @@ import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.DatePicker
import android.widget.TimePicker
import androidx.appcompat.widget.ActionMenuView
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.NestedScrollView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.GeneratePasswordDialogFragment
import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment
import com.kunzisoft.keepass.activities.dialogs.SetOTPDialogFragment
import com.kunzisoft.keepass.activities.dialogs.*
import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.DateInstant
@@ -53,12 +55,15 @@ import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.view.EntryEditContentsView
import com.kunzisoft.keepass.view.showActionError
import org.joda.time.DateTime
import java.util.*
class EntryEditActivity : LockingActivity(),
IconPickerDialogFragment.IconPickerListener,
GeneratePasswordDialogFragment.GeneratePasswordListener,
SetOTPDialogFragment.CreateOtpListener {
SetOTPDialogFragment.CreateOtpListener,
DatePickerDialog.OnDateSetListener,
TimePickerDialog.OnTimeSetListener {
private var mDatabase: Database? = null
@@ -96,6 +101,16 @@ class EntryEditActivity : LockingActivity(),
entryEditContentsView = findViewById(R.id.entry_edit_contents)
entryEditContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this))
entryEditContentsView?.onDateClickListener = View.OnClickListener {
entryEditContentsView?.expiresDate?.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(supportFragmentManager, "DatePickerFragment")
}
}
// Focus view to reinitialize timeout
resetAppTimeoutWhenViewFocusedOrChanged(entryEditContentsView)
@@ -241,6 +256,9 @@ class EntryEditActivity : LockingActivity(),
username = if (newEntry.username.isEmpty()) mDatabase?.defaultUsername ?:"" else newEntry.username
url = newEntry.url
password = newEntry.password
expires = newEntry.expires
if (expires)
expiresDate = newEntry.expiryTime
notes = newEntry.notes
for (entry in newEntry.customFields.entries) {
post {
@@ -262,7 +280,11 @@ class EntryEditActivity : LockingActivity(),
username = entryView.username
url = entryView.url
password = entryView.password
notes = entryView.notes
expires = entryView.expires
if (entryView.expires) {
expiryTime = entryView.expiresDate
}
notes = entryView. notes
entryView.customFields.forEach { customField ->
putExtraField(customField.name, customField.protectedValue)
}
@@ -408,6 +430,39 @@ class EntryEditActivity : 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) {
entryEditContentsView?.expiresDate?.date?.let { expiresDate ->
// Save the date
entryEditContentsView?.expiresDate =
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(timePicker: TimePicker?, hours: Int, minutes: Int) {
entryEditContentsView?.expiresDate?.date?.let { expiresDate ->
// Save the date
entryEditContentsView?.expiresDate =
DateInstant(DateTime(expiresDate)
.withHourOfDay(hours)
.withMinuteOfHour(minutes)
.toDate())
}
}
override fun onSaveInstanceState(outState: Bundle) {
mNewEntry?.let {
populateEntryWithViews(it)

View File

@@ -0,0 +1,61 @@
package com.kunzisoft.keepass.activities.dialogs
import android.app.DatePickerDialog
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.DialogFragment
class DatePickerFragment : DialogFragment() {
private var mDefaultYear: Int = 2000
private var mDefaultMonth: Int = 1
private var mDefaultDay: Int = 1
private var mListener: DatePickerDialog.OnDateSetListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
try {
mListener = context as DatePickerDialog.OnDateSetListener
} catch (e: ClassCastException) {
throw ClassCastException(context.toString()
+ " must implement " + DatePickerDialog.OnDateSetListener::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Create a new instance of DatePickerDialog and return it
return context?.let {
arguments?.apply {
if (containsKey(DEFAULT_YEAR_BUNDLE_KEY))
mDefaultYear = getInt(DEFAULT_YEAR_BUNDLE_KEY)
if (containsKey(DEFAULT_MONTH_BUNDLE_KEY))
mDefaultMonth = getInt(DEFAULT_MONTH_BUNDLE_KEY)
if (containsKey(DEFAULT_DAY_BUNDLE_KEY))
mDefaultDay = getInt(DEFAULT_DAY_BUNDLE_KEY)
}
DatePickerDialog(it, mListener, mDefaultYear, mDefaultMonth, mDefaultDay)
} ?: super.onCreateDialog(savedInstanceState)
}
companion object {
private const val DEFAULT_YEAR_BUNDLE_KEY = "DEFAULT_YEAR_BUNDLE_KEY"
private const val DEFAULT_MONTH_BUNDLE_KEY = "DEFAULT_MONTH_BUNDLE_KEY"
private const val DEFAULT_DAY_BUNDLE_KEY = "DEFAULT_DAY_BUNDLE_KEY"
fun getInstance(defaultYear: Int,
defaultMonth: Int,
defaultDay: Int): DatePickerFragment {
return DatePickerFragment().apply {
arguments = Bundle().apply {
putInt(DEFAULT_YEAR_BUNDLE_KEY, defaultYear)
putInt(DEFAULT_MONTH_BUNDLE_KEY, defaultMonth)
putInt(DEFAULT_DAY_BUNDLE_KEY, defaultDay)
}
}
}
}
}

View File

@@ -0,0 +1,57 @@
package com.kunzisoft.keepass.activities.dialogs
import android.app.DatePickerDialog
import android.app.Dialog
import android.app.TimePickerDialog
import android.content.Context
import android.os.Bundle
import android.text.format.DateFormat
import androidx.fragment.app.DialogFragment
class TimePickerFragment : DialogFragment() {
private var defaultHour: Int = 0
private var defaultMinute: Int = 0
private var mListener: TimePickerDialog.OnTimeSetListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
try {
mListener = context as TimePickerDialog.OnTimeSetListener
} catch (e: ClassCastException) {
throw ClassCastException(context.toString()
+ " must implement " + DatePickerDialog.OnDateSetListener::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Create a new instance of DatePickerDialog and return it
return context?.let {
arguments?.apply {
if (containsKey(DEFAULT_HOUR_BUNDLE_KEY))
defaultHour = getInt(DEFAULT_HOUR_BUNDLE_KEY)
if (containsKey(DEFAULT_MINUTE_BUNDLE_KEY))
defaultMinute = getInt(DEFAULT_MINUTE_BUNDLE_KEY)
}
TimePickerDialog(it, mListener, defaultHour, defaultMinute, DateFormat.is24HourFormat(activity))
} ?: super.onCreateDialog(savedInstanceState)
}
companion object {
private const val DEFAULT_HOUR_BUNDLE_KEY = "DEFAULT_HOUR_BUNDLE_KEY"
private const val DEFAULT_MINUTE_BUNDLE_KEY = "DEFAULT_MINUTE_BUNDLE_KEY"
fun getInstance(defaultHour: Int,
defaultMinute: Int): TimePickerFragment {
return TimePickerFragment().apply {
arguments = Bundle().apply {
putInt(DEFAULT_HOUR_BUNDLE_KEY, defaultHour)
putInt(DEFAULT_MINUTE_BUNDLE_KEY, defaultMinute)
}
}
}
}
}

View File

@@ -142,7 +142,7 @@ class DateInstant : Parcelable {
fun getDateTimeString(resources: Resources, date: Date): String {
return java.text.DateFormat.getDateTimeInstance(
java.text.DateFormat.MEDIUM,
java.text.DateFormat.MEDIUM,
java.text.DateFormat.SHORT,
ConfigurationCompat.getLocales(resources.configuration)[0])
.format(date)
}

View File

@@ -21,20 +21,21 @@ package com.kunzisoft.keepass.view
import android.content.Context
import android.graphics.Color
import com.google.android.material.textfield.TextInputLayout
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.*
import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.icons.IconDrawableFactory
import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.icons.assignDefaultDatabaseIcon
import com.kunzisoft.keepass.model.Field
import org.joda.time.Duration
import org.joda.time.Instant
class EntryEditContentsView @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
@@ -51,10 +52,22 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
private val entryPasswordLayoutView: TextInputLayout
private val entryPasswordView: EditText
private val entryConfirmationPasswordView: EditText
private val entryCommentView: EditText
private val entryExpiresCheckBox: CompoundButton
private val entryExpiresTextView: TextView
private val entryNotesView: EditText
private val entryExtraFieldsContainer: ViewGroup
private var iconColor: Int = 0
private var expiresInstant: DateInstant = DateInstant(Instant.now().plus(Duration.standardDays(30)).toDate())
var onDateClickListener: OnClickListener? = null
set(value) {
field = value
if (entryExpiresCheckBox.isChecked)
entryExpiresTextView.setOnClickListener(value)
else
entryExpiresTextView.setOnClickListener(null)
}
init {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
@@ -68,9 +81,15 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
entryPasswordLayoutView = findViewById(R.id.entry_edit_container_password)
entryPasswordView = findViewById(R.id.entry_edit_password)
entryConfirmationPasswordView = findViewById(R.id.entry_edit_confirmation_password)
entryCommentView = findViewById(R.id.entry_edit_notes)
entryExpiresCheckBox = findViewById(R.id.entry_edit_expires_checkbox)
entryExpiresTextView = findViewById(R.id.entry_edit_expires_text)
entryNotesView = findViewById(R.id.entry_edit_notes)
entryExtraFieldsContainer = findViewById(R.id.entry_edit_advanced_container)
entryExpiresCheckBox.setOnCheckedChangeListener { _, _ ->
assignExpiresDateText()
}
// Retrieve the textColor to tint the icon
val taIconColor = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor))
iconColor = taIconColor.getColor(0, Color.WHITE)
@@ -136,14 +155,44 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
}
}
var notes: String
private fun assignExpiresDateText() {
entryExpiresTextView.text = if (entryExpiresCheckBox.isChecked) {
entryExpiresTextView.setOnClickListener(onDateClickListener)
expiresInstant.getDateTimeString(resources)
} else {
entryExpiresTextView.setOnClickListener(null)
resources.getString(R.string.never)
}
if (fontInVisibility)
entryExpiresTextView.applyFontVisibility()
}
var expires: Boolean
get() {
return entryCommentView.text.toString()
return entryExpiresCheckBox.isChecked
}
set(value) {
entryCommentView.setText(value)
entryExpiresCheckBox.isChecked = value
assignExpiresDateText()
}
var expiresDate: DateInstant
get() {
return expiresInstant
}
set(value) {
expiresInstant = value
assignExpiresDateText()
}
var notes: String
get() {
return entryNotesView.text.toString()
}
set(value) {
entryNotesView.setText(value)
if (fontInVisibility)
entryCommentView.applyFontVisibility()
entryNotesView.applyFontVisibility()
}
val customFields: MutableList<Field>

View File

@@ -146,6 +146,46 @@
android:hint="@string/entry_url"/>
</com.google.android.material.textfield.TextInputLayout>
<!-- Expires -->
<androidx.constraintlayout.widget.ConstraintLayout
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>
<!-- Notes -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"

View File

@@ -22,10 +22,14 @@
<style name="KeepassDXStyle.Light" parent="KeepassDXStyle.Light.v21" >
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
<item name="android:statusBarColor">?attr/colorPrimaryDark</item>
<item name="android:timePickerDialogTheme">@style/KeepassDXStyle.Light.DateTime.Dialog</item>
<item name="android:datePickerDialogTheme">@style/KeepassDXStyle.Light.DateTime.Dialog</item>
</style>
<style name="KeepassDXStyle.Night" parent="KeepassDXStyle.Night.v21" >
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
<item name="android:statusBarColor">?attr/colorPrimaryDark</item>
<item name="android:timePickerDialogTheme">@style/KeepassDXStyle.Night.DateTime.Dialog</item>
<item name="android:datePickerDialogTheme">@style/KeepassDXStyle.Night.DateTime.Dialog</item>
</style>
<!-- Button Style -->

View File

@@ -68,8 +68,8 @@
<item name="iconPreferenceColor">@color/text_color_secondary_light</item>
<!-- Dialog -->
<item name="buttonBarNegativeButtonStyle">@style/KeepassDXStyle.Dialog.NegativeButton</item>
<item name="buttonBarPositiveButtonStyle">@style/KeepassDXStyle.Dialog.PositiveButton</item>
<item name="android:alertDialogTheme">@style/KeepassDXStyle.Light.Dialog</item>
<item name="alertDialogTheme">@style/KeepassDXStyle.Light.Dialog</item>
<!-- Toolbar -->
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Light</item>
@@ -121,13 +121,10 @@
<item name="iconPreferenceColor">@color/text_color_secondary_night</item>
<!-- Dialog -->
<item name="android:alertDialogTheme">@style/KeepassDXStyle.Night.Dialog</item>
<item name="alertDialogTheme">@style/KeepassDXStyle.Night.Dialog</item>
<!-- Dialog -->
<item name="buttonBarNegativeButtonStyle">@style/KeepassDXStyle.Dialog.NegativeButton</item>
<item name="buttonBarPositiveButtonStyle">@style/KeepassDXStyle.Dialog.PositiveButton</item>
<!-- Toolbar -->
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Night</item>
<item name="toolbarPopupAppearance">@style/KeepassDXStyle.Night.Toolbar.Popup</item>
@@ -178,13 +175,10 @@
<item name="iconPreferenceColor">@color/text_color_secondary_night</item>
<!-- Dialog -->
<item name="android:alertDialogTheme">@style/KeepassDXStyle.Black.Dialog</item>
<item name="alertDialogTheme">@style/KeepassDXStyle.Black.Dialog</item>
<!-- Dialog -->
<item name="buttonBarNegativeButtonStyle">@style/KeepassDXStyle.Dialog.NegativeButton</item>
<item name="buttonBarPositiveButtonStyle">@style/KeepassDXStyle.Dialog.PositiveButton</item>
<!-- Toolbar -->
<item name="toolbarAppearance">@style/KeepassDXStyle.Toolbar.Black</item>
<item name="toolbarPopupAppearance">@style/KeepassDXStyle.Black.Toolbar.Popup</item>
@@ -266,25 +260,30 @@
</style>
<!-- Dialog -->
<style name="KeepassDXStyle.Night.Dialog" parent="Theme.AppCompat.Dialog.Alert">
<style name="KeepassDXStyle.Light.Dialog" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">@color/orange</item>
<item name="android:textColorPrimary">@color/green</item>
<item name="android:background">@color/background_light</item>
</style>
<style name="KeepassDXStyle.Night.Dialog" parent="Theme.AppCompat.DayNight.Dialog.Alert">
<item name="colorAccent">@color/orange</item>
<item name="android:textColorPrimary">@color/green</item>
<item name="android:background">@color/background_night</item>
<item name="buttonBarNegativeButtonStyle">@style/KeepassDXStyle.Dialog.NegativeButton</item>
<item name="buttonBarPositiveButtonStyle">@style/KeepassDXStyle.Dialog.PositiveButton</item>
</style>
<style name="KeepassDXStyle.Black.Dialog" parent="Theme.AppCompat.Dialog.Alert">
<style name="KeepassDXStyle.Black.Dialog" parent="Theme.AppCompat.DayNight.Dialog.Alert">
<item name="colorAccent">@color/orange_dark</item>
<item name="android:textColorPrimary">@color/green_dark</item>
<item name="android:background">@color/dark</item>
<item name="buttonBarNegativeButtonStyle">@style/KeepassDXStyle.Dialog.NegativeButton</item>
<item name="buttonBarPositiveButtonStyle">@style/KeepassDXStyle.Dialog.PositiveButton</item>
<item name="android:background">@color/black</item>
</style>
<style name="KeepassDXStyle.Dialog.NegativeButton" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
<item name="android:textColor">?attr/colorAccent</item>
<style name="KeepassDXStyle.Light.DateTime.Dialog" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">@color/orange</item>
<item name="android:textColorPrimary">@color/green_dark</item>
<item name="android:windowBackground">@color/background_light</item>
</style>
<style name="KeepassDXStyle.Dialog.PositiveButton" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<style name="KeepassDXStyle.Night.DateTime.Dialog" parent="Theme.AppCompat.DayNight.Dialog.Alert">
<item name="colorAccent">@color/orange_dark</item>
<item name="android:textColorPrimary">@color/green_light</item>
<item name="android:windowBackground">@color/background_night</item>
</style>
<!-- CheckBox -->