mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Compare commits
27 Commits
3.0.0_beta
...
3.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4f2a1eb89 | ||
|
|
e9fc9cbc2a | ||
|
|
b809180a1b | ||
|
|
ecc75df3a1 | ||
|
|
d1b6863143 | ||
|
|
faf27143aa | ||
|
|
aee58a4475 | ||
|
|
c5e07f643f | ||
|
|
818f5820d5 | ||
|
|
77c6e28876 | ||
|
|
fb7f66012d | ||
|
|
0f7f7bbe6c | ||
|
|
8dedb8deb4 | ||
|
|
d284db4d3c | ||
|
|
82450c0ae8 | ||
|
|
8dd6c33901 | ||
|
|
f920d40db5 | ||
|
|
19be6c1acc | ||
|
|
7d9d8ad0e4 | ||
|
|
85f8237d5f | ||
|
|
c542894734 | ||
|
|
d348987077 | ||
|
|
3718610595 | ||
|
|
9c36ec0623 | ||
|
|
c6917b5d74 | ||
|
|
4eaa179789 | ||
|
|
9008cd4549 |
11
CHANGELOG
11
CHANGELOG
@@ -1,3 +1,12 @@
|
|||||||
|
KeePassDX(3.0.1)
|
||||||
|
* Fix text size and smallest margin #1085
|
||||||
|
* Fix number of lines during an edition #1073
|
||||||
|
* Fix Magikeyboard URL auto action #1100
|
||||||
|
* Fix exception after group name change and save #1112
|
||||||
|
* Fix timeout reset #1107
|
||||||
|
* Fix search actions #1091 #1092
|
||||||
|
* Small changes #1106 #1085
|
||||||
|
|
||||||
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
|
||||||
@@ -5,7 +14,7 @@ KeePassDX(3.0.0)
|
|||||||
* Fix timeout in dialogs #716
|
* Fix timeout in dialogs #716
|
||||||
* Check URI permissions #626
|
* Check URI permissions #626
|
||||||
* Better autofill implementation #943 #946 #984 #1070 (Thx @uduerholz)
|
* Better autofill implementation #943 #946 #984 #1070 (Thx @uduerholz)
|
||||||
* Improvements #680 #1035 #1043 #942 #1021 #1027
|
* Improvements #680 #1035 #1043 #942 #1021 #1027 #1046 #1082 #1083 (Thx @chenxiaolong)
|
||||||
|
|
||||||
KeePassDX(2.10.5)
|
KeePassDX(2.10.5)
|
||||||
* Increase the saving speed of database #1028
|
* Increase the saving speed of database #1028
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId "com.kunzisoft.keepass"
|
applicationId "com.kunzisoft.keepass"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode = 86
|
versionCode = 88
|
||||||
versionName = "3.0.0_beta03"
|
versionName = "3.0.1"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
||||||
testApplicationId = "com.kunzisoft.keepass.tests"
|
testApplicationId = "com.kunzisoft.keepass.tests"
|
||||||
|
|||||||
@@ -653,6 +653,8 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
Log.e(TAG, "Node can't be cast in Entry")
|
Log.e(TAG, "Node can't be cast in Entry")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reloadGroupIfSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun entrySelectedForSave(database: Database, entry: Entry, searchInfo: SearchInfo) {
|
private fun entrySelectedForSave(database: Database, entry: Entry, searchInfo: SearchInfo) {
|
||||||
@@ -738,6 +740,12 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
actionNodeMode?.finish()
|
actionNodeMode?.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun reloadGroupIfSearch() {
|
||||||
|
if (Intent.ACTION_SEARCH == intent.action) {
|
||||||
|
reloadCurrentGroup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onNodeSelected(
|
override fun onNodeSelected(
|
||||||
database: Database,
|
database: Database,
|
||||||
nodes: List<Node>
|
nodes: List<Node>
|
||||||
@@ -793,6 +801,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
(node as Entry).nodeId
|
(node as Entry).nodeId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
reloadGroupIfSearch()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,6 +856,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
deleteNodes(nodes)
|
deleteNodes(nodes)
|
||||||
finishNodeAction()
|
finishNodeAction()
|
||||||
|
reloadGroupIfSearch()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -934,9 +944,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
// If no node, show education to add new one
|
// If no node, show education to add new one
|
||||||
val addNodeButtonEducationPerformed = mGroupFragment != null
|
val addNodeButtonEducationPerformed = actionNodeMode == null
|
||||||
&& mGroupFragment!!.isEmpty
|
|
||||||
&& actionNodeMode == null
|
|
||||||
&& addNodeButtonView?.addButtonView != null
|
&& addNodeButtonView?.addButtonView != null
|
||||||
&& addNodeButtonView!!.isEnable
|
&& addNodeButtonView!!.isEnable
|
||||||
&& groupActivityEducation.checkAndPerformedAddNodeButtonEducation(
|
&& groupActivityEducation.checkAndPerformedAddNodeButtonEducation(
|
||||||
@@ -1095,7 +1103,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
try {
|
try {
|
||||||
mGroupViewModel.loadGroup(mDatabase, mCurrentGroupState)
|
mGroupViewModel.loadGroup(mDatabase, mCurrentGroupState)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Unable to rebuild the list after deletion", e)
|
Log.e(TAG, "Unable to rebuild the group", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,9 +74,6 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen
|
|||||||
private var mRecycleBinEnable: Boolean = false
|
private var mRecycleBinEnable: Boolean = false
|
||||||
private var mRecycleBin: Group? = null
|
private var mRecycleBin: Group? = null
|
||||||
|
|
||||||
val isEmpty: Boolean
|
|
||||||
get() = mAdapter == null || mAdapter?.itemCount?:0 <= 0
|
|
||||||
|
|
||||||
private var mRecycleViewScrollListener = object : RecyclerView.OnScrollListener() {
|
private var mRecycleViewScrollListener = object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
super.onScrollStateChanged(recyclerView, newState)
|
super.onScrollStateChanged(recyclerView, newState)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import android.view.ViewGroup
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
@@ -41,9 +42,11 @@ 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.database.element.template.TemplateField
|
||||||
import com.kunzisoft.keepass.otp.OtpElement
|
import com.kunzisoft.keepass.otp.OtpElement
|
||||||
import com.kunzisoft.keepass.otp.OtpType
|
import com.kunzisoft.keepass.otp.OtpType
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
|
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
||||||
import com.kunzisoft.keepass.view.setTextSize
|
import com.kunzisoft.keepass.view.setTextSize
|
||||||
import com.kunzisoft.keepass.view.strikeOut
|
import com.kunzisoft.keepass.view.strikeOut
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -64,8 +67,10 @@ class NodeAdapter (private val context: Context,
|
|||||||
private var mCalculateViewTypeTextSize = Array(2) { true } // number of view type
|
private var mCalculateViewTypeTextSize = Array(2) { true } // number of view type
|
||||||
private var mTextSizeUnit: Int = TypedValue.COMPLEX_UNIT_PX
|
private var mTextSizeUnit: Int = TypedValue.COMPLEX_UNIT_PX
|
||||||
private var mPrefSizeMultiplier: Float = 0F
|
private var mPrefSizeMultiplier: Float = 0F
|
||||||
private var mSubtextDefaultDimension: Float = 0F
|
private var mTextDefaultDimension: Float = 0F
|
||||||
private var mInfoTextDefaultDimension: Float = 0F
|
private var mSubTextDefaultDimension: Float = 0F
|
||||||
|
private var mMetaTextDefaultDimension: Float = 0F
|
||||||
|
private var mOtpTokenTextDefaultDimension: Float = 0F
|
||||||
private var mNumberChildrenTextDefaultDimension: Float = 0F
|
private var mNumberChildrenTextDefaultDimension: Float = 0F
|
||||||
private var mIconDefaultDimension: Float = 0F
|
private var mIconDefaultDimension: Float = 0F
|
||||||
|
|
||||||
@@ -77,6 +82,7 @@ class NodeAdapter (private val context: Context,
|
|||||||
|
|
||||||
private var mActionNodesList = LinkedList<Node>()
|
private var mActionNodesList = LinkedList<Node>()
|
||||||
private var mNodeClickCallback: NodeClickCallback? = null
|
private var mNodeClickCallback: NodeClickCallback? = null
|
||||||
|
private var mClipboardHelper = ClipboardHelper(context)
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private val mContentSelectionColor: Int
|
private val mContentSelectionColor: Int
|
||||||
@@ -299,8 +305,10 @@ class NodeAdapter (private val context: Context,
|
|||||||
mInflater.inflate(R.layout.item_list_nodes_entry, parent, false)
|
mInflater.inflate(R.layout.item_list_nodes_entry, parent, false)
|
||||||
}
|
}
|
||||||
val nodeViewHolder = NodeViewHolder(view)
|
val nodeViewHolder = NodeViewHolder(view)
|
||||||
mInfoTextDefaultDimension = nodeViewHolder.text.textSize
|
mTextDefaultDimension = nodeViewHolder.text.textSize
|
||||||
mSubtextDefaultDimension = nodeViewHolder.subText.textSize
|
mSubTextDefaultDimension = nodeViewHolder.subText?.textSize ?: mSubTextDefaultDimension
|
||||||
|
mMetaTextDefaultDimension = nodeViewHolder.meta.textSize
|
||||||
|
mOtpTokenTextDefaultDimension = nodeViewHolder.otpToken?.textSize ?: mOtpTokenTextDefaultDimension
|
||||||
nodeViewHolder.numberChildren?.let {
|
nodeViewHolder.numberChildren?.let {
|
||||||
mNumberChildrenTextDefaultDimension = it.textSize
|
mNumberChildrenTextDefaultDimension = it.textSize
|
||||||
}
|
}
|
||||||
@@ -311,7 +319,9 @@ class NodeAdapter (private val context: Context,
|
|||||||
val subNode = mNodeSortedList.get(position)
|
val subNode = mNodeSortedList.get(position)
|
||||||
|
|
||||||
// Node selection
|
// Node selection
|
||||||
holder.container.isSelected = mActionNodesList.contains(subNode)
|
holder.container.apply {
|
||||||
|
isSelected = mActionNodesList.contains(subNode)
|
||||||
|
}
|
||||||
|
|
||||||
// Assign image
|
// Assign image
|
||||||
val iconColor = if (holder.container.isSelected)
|
val iconColor = if (holder.container.isSelected)
|
||||||
@@ -333,19 +343,18 @@ class NodeAdapter (private val context: Context,
|
|||||||
// Assign text
|
// Assign text
|
||||||
holder.text.apply {
|
holder.text.apply {
|
||||||
text = subNode.title
|
text = subNode.title
|
||||||
setTextSize(mTextSizeUnit, mInfoTextDefaultDimension, mPrefSizeMultiplier)
|
setTextSize(mTextSizeUnit, mTextDefaultDimension, mPrefSizeMultiplier)
|
||||||
strikeOut(subNode.isCurrentlyExpires)
|
strikeOut(subNode.isCurrentlyExpires)
|
||||||
}
|
}
|
||||||
// Add subText with username
|
|
||||||
holder.subText.apply {
|
|
||||||
text = ""
|
|
||||||
strikeOut(subNode.isCurrentlyExpires)
|
|
||||||
visibility = View.GONE
|
|
||||||
}
|
|
||||||
// Add meta text to show UUID
|
// Add meta text to show UUID
|
||||||
holder.meta.apply {
|
holder.meta.apply {
|
||||||
text = subNode.nodeId.toString()
|
if (mShowUUID) {
|
||||||
visibility = if (mShowUUID) View.VISIBLE else View.GONE
|
text = subNode.nodeId.toString()
|
||||||
|
setTextSize(mTextSizeUnit, mMetaTextDefaultDimension, mPrefSizeMultiplier)
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific elements for entry
|
// Specific elements for entry
|
||||||
@@ -354,12 +363,16 @@ class NodeAdapter (private val context: Context,
|
|||||||
database.startManageEntry(entry)
|
database.startManageEntry(entry)
|
||||||
|
|
||||||
holder.text.text = entry.getVisualTitle()
|
holder.text.text = entry.getVisualTitle()
|
||||||
holder.subText.apply {
|
// Add subText with username
|
||||||
|
holder.subText?.apply {
|
||||||
val username = entry.username
|
val username = entry.username
|
||||||
if (mShowUserNames && username.isNotEmpty()) {
|
if (mShowUserNames && username.isNotEmpty()) {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = username
|
text = username
|
||||||
setTextSize(mTextSizeUnit, mSubtextDefaultDimension, mPrefSizeMultiplier)
|
setTextSize(mTextSizeUnit, mSubTextDefaultDimension, mPrefSizeMultiplier)
|
||||||
|
strikeOut(subNode.isCurrentlyExpires)
|
||||||
|
} else {
|
||||||
|
visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +440,21 @@ class NodeAdapter (private val context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
holder?.otpToken?.text = otpElement?.token
|
holder?.otpToken?.apply {
|
||||||
|
text = otpElement?.token
|
||||||
|
setTextSize(mTextSizeUnit, mOtpTokenTextDefaultDimension, mPrefSizeMultiplier)
|
||||||
|
}
|
||||||
|
holder?.otpContainer?.setOnClickListener {
|
||||||
|
otpElement?.token?.let { token ->
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.copy_field,
|
||||||
|
TemplateField.getLocalizedName(context, TemplateField.LABEL_TOKEN)),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
mClipboardHelper.copyToClipboard(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OtpRunnable(val view: View?): Runnable {
|
class OtpRunnable(val view: View?): Runnable {
|
||||||
@@ -468,7 +495,7 @@ class NodeAdapter (private val context: Context,
|
|||||||
var imageIdentifier: ImageView? = itemView.findViewById(R.id.node_image_identifier)
|
var imageIdentifier: ImageView? = itemView.findViewById(R.id.node_image_identifier)
|
||||||
var icon: ImageView = itemView.findViewById(R.id.node_icon)
|
var icon: ImageView = itemView.findViewById(R.id.node_icon)
|
||||||
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 otpContainer: ViewGroup? = itemView.findViewById(R.id.node_otp_container)
|
||||||
var otpProgress: ProgressBar? = itemView.findViewById(R.id.node_otp_progress)
|
var otpProgress: ProgressBar? = itemView.findViewById(R.id.node_otp_progress)
|
||||||
|
|||||||
@@ -53,8 +53,10 @@ class StructureParser(private val structure: AssistStructure) {
|
|||||||
applicationId = windowNode.title.toString().split("/")[0]
|
applicationId = windowNode.title.toString().split("/")[0]
|
||||||
Log.d(TAG, "Autofill applicationId: $applicationId")
|
Log.d(TAG, "Autofill applicationId: $applicationId")
|
||||||
|
|
||||||
if (parseViewNode(windowNode.rootViewNode))
|
if (applicationId?.contains("PopupWindow:") == false) {
|
||||||
break@mainLoop
|
if (parseViewNode(windowNode.rootViewNode))
|
||||||
|
break@mainLoop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If not explicit username field found, add the field just before password field.
|
// If not explicit username field found, add the field just before password field.
|
||||||
if (usernameId == null && passwordId != null && usernameIdCandidate != null) {
|
if (usernameId == null && passwordId != null && usernameIdCandidate != null) {
|
||||||
|
|||||||
@@ -177,16 +177,20 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
|
|||||||
fun addChildrenFrom(group: Group) {
|
fun addChildrenFrom(group: Group) {
|
||||||
group.groupKDB?.getChildEntries()?.forEach { entryToAdd ->
|
group.groupKDB?.getChildEntries()?.forEach { entryToAdd ->
|
||||||
groupKDB?.addChildEntry(entryToAdd)
|
groupKDB?.addChildEntry(entryToAdd)
|
||||||
|
entryToAdd.parent = groupKDB
|
||||||
}
|
}
|
||||||
group.groupKDB?.getChildGroups()?.forEach { groupToAdd ->
|
group.groupKDB?.getChildGroups()?.forEach { groupToAdd ->
|
||||||
groupKDB?.addChildGroup(groupToAdd)
|
groupKDB?.addChildGroup(groupToAdd)
|
||||||
|
groupToAdd.parent = groupKDB
|
||||||
}
|
}
|
||||||
|
|
||||||
group.groupKDBX?.getChildEntries()?.forEach { entryToAdd ->
|
group.groupKDBX?.getChildEntries()?.forEach { entryToAdd ->
|
||||||
groupKDBX?.addChildEntry(entryToAdd)
|
groupKDBX?.addChildEntry(entryToAdd)
|
||||||
|
entryToAdd.parent = groupKDBX
|
||||||
}
|
}
|
||||||
group.groupKDBX?.getChildGroups()?.forEach { groupToAdd ->
|
group.groupKDBX?.getChildGroups()?.forEach { groupToAdd ->
|
||||||
groupKDBX?.addChildGroup(groupToAdd)
|
groupKDBX?.addChildGroup(groupToAdd)
|
||||||
|
groupToAdd.parent = groupKDBX
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -178,6 +178,11 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
|||||||
assignKeyboardView()
|
assignKeyboardView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onEvaluateFullscreenMode(): Boolean {
|
||||||
|
return resources.getBoolean(R.bool.magikeyboard_allow_fullscreen_mode)
|
||||||
|
&& super.onEvaluateFullscreenMode()
|
||||||
|
}
|
||||||
|
|
||||||
private fun playVibration(keyCode: Int) {
|
private fun playVibration(keyCode: Int) {
|
||||||
when (keyCode) {
|
when (keyCode) {
|
||||||
Keyboard.KEYCODE_DELETE -> {}
|
Keyboard.KEYCODE_DELETE -> {}
|
||||||
@@ -267,7 +272,7 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
|
|||||||
if (entryInfoKey != null) {
|
if (entryInfoKey != null) {
|
||||||
currentInputConnection.commitText(entryInfoKey!!.url, 1)
|
currentInputConnection.commitText(entryInfoKey!!.url, 1)
|
||||||
}
|
}
|
||||||
actionTabAutomatically()
|
actionGoAutomatically()
|
||||||
}
|
}
|
||||||
KEY_FIELDS -> {
|
KEY_FIELDS -> {
|
||||||
if (entryInfoKey != null) {
|
if (entryInfoKey != null) {
|
||||||
|
|||||||
@@ -64,11 +64,13 @@ class DurationDialogFragmentCompat : InputPreferenceDialogFragmentCompat() {
|
|||||||
|
|
||||||
private fun durationToDaysHoursMinutesSeconds(duration: Long) {
|
private fun durationToDaysHoursMinutesSeconds(duration: Long) {
|
||||||
if (duration < 0) {
|
if (duration < 0) {
|
||||||
|
mEnabled = false
|
||||||
mDays = 0
|
mDays = 0
|
||||||
mHours = 0
|
mHours = 0
|
||||||
mMinutes = 0
|
mMinutes = 0
|
||||||
mSeconds = 0
|
mSeconds = 0
|
||||||
} else {
|
} else {
|
||||||
|
mEnabled = true
|
||||||
mDays = (duration / (24L * 60L * 60L * 1000L)).toInt()
|
mDays = (duration / (24L * 60L * 60L * 1000L)).toInt()
|
||||||
val daysMilliseconds = mDays * 24L * 60L * 60L * 1000L
|
val daysMilliseconds = mDays * 24L * 60L * 60L * 1000L
|
||||||
mHours = ((duration - daysMilliseconds) / (60L * 60L * 1000L)).toInt()
|
mHours = ((duration - daysMilliseconds) / (60L * 60L * 1000L)).toInt()
|
||||||
@@ -125,10 +127,9 @@ class DurationDialogFragmentCompat : InputPreferenceDialogFragmentCompat() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mEnabled = isSwitchActivated()
|
|
||||||
setSwitchAction({ isChecked ->
|
setSwitchAction({ isChecked ->
|
||||||
mEnabled = isChecked
|
mEnabled = isChecked
|
||||||
}, mDays + mHours + mMinutes + mSeconds > 0)
|
}, mEnabled)
|
||||||
|
|
||||||
assignValuesInViews()
|
assignValuesInViews()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ class DateTimeFieldView @JvmOverloads constructor(context: Context,
|
|||||||
|
|
||||||
private var mDefault: DateInstant = DateInstant.NEVER_EXPIRES
|
private var mDefault: DateInstant = DateInstant.NEVER_EXPIRES
|
||||||
|
|
||||||
var setOnDateClickListener: ((DateInstant) -> Unit)? = null
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
|
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
|
||||||
inflater?.inflate(R.layout.view_date_time, this)
|
inflater?.inflate(R.layout.view_date_time, this)
|
||||||
|
|||||||
@@ -275,22 +275,26 @@ abstract class TemplateAbstractView<
|
|||||||
templateAttribute: TemplateAttribute,
|
templateAttribute: TemplateAttribute,
|
||||||
entryInfoValue: String,
|
entryInfoValue: String,
|
||||||
showEmptyFields: Boolean) {
|
showEmptyFields: Boolean) {
|
||||||
var fieldView: TEntryFieldView? = findViewWithTag(fieldTag)
|
try {
|
||||||
if (!showEmptyFields && entryInfoValue.isEmpty()) {
|
var fieldView: TEntryFieldView? = findViewWithTag(fieldTag)
|
||||||
fieldView?.isFieldVisible = false
|
if (!showEmptyFields && entryInfoValue.isEmpty()) {
|
||||||
} else if (fieldView == null && entryInfoValue.isNotEmpty()) {
|
fieldView?.isFieldVisible = false
|
||||||
// Add new not referenced view if standard field not in template
|
} else if (fieldView == null && entryInfoValue.isNotEmpty()) {
|
||||||
fieldView = buildViewForNotReferencedField(
|
// Add new not referenced view if standard field not in template
|
||||||
Field(templateAttribute.label,
|
fieldView = buildViewForNotReferencedField(
|
||||||
ProtectedString(templateAttribute.protected, "")),
|
Field(templateAttribute.label,
|
||||||
templateAttribute
|
ProtectedString(templateAttribute.protected, "")),
|
||||||
) as? TEntryFieldView?
|
templateAttribute
|
||||||
fieldView?.let {
|
) as? TEntryFieldView?
|
||||||
addNotReferencedView(it as View)
|
fieldView?.let {
|
||||||
|
addNotReferencedView(it as View)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fieldView?.value = entryInfoValue
|
||||||
|
fieldView?.applyFontVisibility(mFontInVisibility)
|
||||||
|
} catch(e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to populate entry field view", e)
|
||||||
}
|
}
|
||||||
fieldView?.value = entryInfoValue
|
|
||||||
fieldView?.applyFontVisibility(mFontInVisibility)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -299,22 +303,25 @@ abstract class TemplateAbstractView<
|
|||||||
expires: Boolean,
|
expires: Boolean,
|
||||||
expiryTime: DateInstant,
|
expiryTime: DateInstant,
|
||||||
showEmptyFields: Boolean) {
|
showEmptyFields: Boolean) {
|
||||||
|
try {
|
||||||
var fieldView: TDateTimeView? = findViewWithTag(fieldTag)
|
var fieldView: TDateTimeView? = findViewWithTag(fieldTag)
|
||||||
if (!showEmptyFields && !expires) {
|
if (!showEmptyFields && !expires) {
|
||||||
fieldView?.isFieldVisible = false
|
fieldView?.isFieldVisible = false
|
||||||
} else if (fieldView == null && expires) {
|
} else if (fieldView == null && expires) {
|
||||||
fieldView = buildViewForNotReferencedField(
|
fieldView = buildViewForNotReferencedField(
|
||||||
Field(templateAttribute.label,
|
Field(templateAttribute.label,
|
||||||
ProtectedString(templateAttribute.protected, "")),
|
ProtectedString(templateAttribute.protected, "")),
|
||||||
templateAttribute
|
templateAttribute
|
||||||
) as? TDateTimeView?
|
) as? TDateTimeView?
|
||||||
fieldView?.let {
|
fieldView?.let {
|
||||||
addNotReferencedView(it as View)
|
addNotReferencedView(it as View)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fieldView?.activation = expires
|
||||||
|
fieldView?.dateTime = expiryTime
|
||||||
|
} catch(e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to populate date time view", e)
|
||||||
}
|
}
|
||||||
fieldView?.activation = expires
|
|
||||||
fieldView?.dateTime = expiryTime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ class TemplateView @JvmOverloads constructor(context: Context,
|
|||||||
label = templateAttribute.alias
|
label = templateAttribute.alias
|
||||||
?: TemplateField.getLocalizedName(context, field.name)
|
?: TemplateField.getLocalizedName(context, field.name)
|
||||||
setMaxChars(templateAttribute.options.getNumberChars())
|
setMaxChars(templateAttribute.options.getNumberChars())
|
||||||
setMaxLines(templateAttribute.options.getNumberLines())
|
|
||||||
// TODO Linkify
|
// TODO Linkify
|
||||||
value = field.protectedValue.stringValue
|
value = field.protectedValue.stringValue
|
||||||
// Here the value is often empty
|
// Here the value is often empty
|
||||||
|
|||||||
@@ -187,6 +187,6 @@ class TextEditFieldView @JvmOverloads constructor(context: Context,
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MAX_CHARS_LIMIT = Integer.MAX_VALUE
|
const val MAX_CHARS_LIMIT = Integer.MAX_VALUE
|
||||||
const val MAX_LINES_LIMIT = 40
|
const val MAX_LINES_LIMIT = Integer.MAX_VALUE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,18 +214,6 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setMaxLines(numberLines: Int) {
|
|
||||||
when {
|
|
||||||
numberLines <= 0 -> {
|
|
||||||
valueView.maxLines = MAX_LINES_LIMIT
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val lines = if (numberLines > MAX_LINES_LIMIT) MAX_LINES_LIMIT else numberLines
|
|
||||||
valueView.maxLines = lines
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setProtection(protection: Boolean, hiddenProtectedValue: Boolean = false) {
|
fun setProtection(protection: Boolean, hiddenProtectedValue: Boolean = false) {
|
||||||
showButton.isVisible = protection
|
showButton.isVisible = protection
|
||||||
showButton.isSelected = hiddenProtectedValue
|
showButton.isSelected = hiddenProtectedValue
|
||||||
@@ -343,6 +331,5 @@ class TextFieldView @JvmOverloads constructor(context: Context,
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MAX_CHARS_LIMIT = Integer.MAX_VALUE
|
const val MAX_CHARS_LIMIT = Integer.MAX_VALUE
|
||||||
const val MAX_LINES_LIMIT = 40
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
android:layout_marginStart="20dp"
|
android:layout_marginStart="20dp"
|
||||||
android:layout_marginEnd="20dp"
|
android:layout_marginEnd="20dp"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Title"
|
style="@style/KeepassDXStyle.TextAppearance.Title"
|
||||||
|
android:textStyle="bold"
|
||||||
android:textColor="?android:attr/textColor"/>
|
android:textColor="?android:attr/textColor"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -62,7 +63,7 @@
|
|||||||
app:indicatorColor="?attr/colorAccent"
|
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="20dp"
|
||||||
android:indeterminate="true"
|
android:indeterminate="true"
|
||||||
android:max="100"/>
|
android:max="100"/>
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:minHeight="56dp"
|
android:minHeight="48dp"
|
||||||
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_constraintBottom_toBottomOf="parent"
|
||||||
@@ -104,10 +103,12 @@
|
|||||||
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C" />
|
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<LinearLayout
|
||||||
android:id="@+id/node_options"
|
android:id="@+id/node_options"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="end"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginLeft="12dp"
|
android:layout_marginLeft="12dp"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
@@ -118,9 +119,11 @@
|
|||||||
android:id="@+id/node_otp_container"
|
android:id="@+id/node_otp_container"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="12dp"
|
||||||
|
android:padding="4dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/node_attachment_icon"
|
app:layout_constraintBottom_toTopOf="@+id/node_attachment_icon"
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
@@ -162,7 +165,7 @@
|
|||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/node_otp_container" />
|
app:layout_constraintTop_toBottomOf="@+id/node_otp_container" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -30,8 +30,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:minHeight="56dp"
|
android:minHeight="48dp"
|
||||||
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_constraintBottom_toBottomOf="parent"
|
||||||
@@ -89,18 +88,6 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
tools:text="Node Title" />
|
tools:text="Node Title" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/node_subtext"
|
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Group.SubTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="-4dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:lines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:text="Node SubTitle" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/node_meta"
|
android:id="@+id/node_meta"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Group.Meta"
|
style="@style/KeepassDXStyle.TextAppearance.Group.Meta"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:cursorVisible="false"
|
android:cursorVisible="false"
|
||||||
android:focusableInTouchMode="false"
|
android:focusableInTouchMode="false"
|
||||||
style="@style/KeepassDXStyle.TextAppearance.Large"
|
style="@style/KeepassDXStyle.TextAppearance.TextEntryItem"
|
||||||
tools:text="2020-03-04 05:00" />
|
tools:text="2020-03-04 05:00" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
<androidx.appcompat.widget.SwitchCompat
|
||||||
|
|||||||
22
app/src/main/res/values-land/config.xml
Normal file
22
app/src/main/res/values-land/config.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||||
|
|
||||||
|
This file is part of KeePassDX.
|
||||||
|
|
||||||
|
KeePassDX is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
KeePassDX is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
<bool name="magikeyboard_allow_fullscreen_mode">true</bool>
|
||||||
|
</resources>
|
||||||
22
app/src/main/res/values-sw430dp/config.xml
Normal file
22
app/src/main/res/values-sw430dp/config.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||||
|
|
||||||
|
This file is part of KeePassDX.
|
||||||
|
|
||||||
|
KeePassDX is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
KeePassDX is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
<bool name="magikeyboard_allow_fullscreen_mode">false</bool>
|
||||||
|
</resources>
|
||||||
22
app/src/main/res/values/config.xml
Normal file
22
app/src/main/res/values/config.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||||
|
|
||||||
|
This file is part of KeePassDX.
|
||||||
|
|
||||||
|
KeePassDX is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
KeePassDX is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
<bool name="magikeyboard_allow_fullscreen_mode">false</bool>
|
||||||
|
</resources>
|
||||||
@@ -367,7 +367,7 @@
|
|||||||
<string name="clipboard_warning">If automatic deletion of clipboard fails, delete its history manually.</string>
|
<string name="clipboard_warning">If automatic deletion of clipboard fails, delete its history manually.</string>
|
||||||
<string name="lock">Lock</string>
|
<string name="lock">Lock</string>
|
||||||
<string name="lock_database_screen_off_title">Screen lock</string>
|
<string name="lock_database_screen_off_title">Screen lock</string>
|
||||||
<string name="lock_database_screen_off_summary">Lock the database when the screen is off</string>
|
<string name="lock_database_screen_off_summary">Lock the database after a few seconds once the screen is off</string>
|
||||||
<string name="lock_database_back_root_title">Press \'Back\' to lock</string>
|
<string name="lock_database_back_root_title">Press \'Back\' to lock</string>
|
||||||
<string name="lock_database_back_root_summary">Lock the database when the user clicks the back button on the root screen</string>
|
<string name="lock_database_back_root_summary">Lock the database when the user clicks the back button on the root screen</string>
|
||||||
<string name="lock_database_show_button_title">Show lock button</string>
|
<string name="lock_database_show_button_title">Show lock button</string>
|
||||||
|
|||||||
@@ -4,4 +4,4 @@
|
|||||||
* Fix timeout in dialogs #716
|
* Fix timeout in dialogs #716
|
||||||
* Check URI permissions #626
|
* Check URI permissions #626
|
||||||
* Better autofill implementation #943 #946 #984 #1070 (Thx @uduerholz)
|
* Better autofill implementation #943 #946 #984 #1070 (Thx @uduerholz)
|
||||||
* Improvements #680 #1035 #1043 #942 #1021 #1027
|
* Improvements #680 #1035 #1043 #942 #1021 #1027 #1046 #1082 #1083 (Thx @chenxiaolong)
|
||||||
7
fastlane/metadata/android/en-US/changelogs/88.txt
Normal file
7
fastlane/metadata/android/en-US/changelogs/88.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
* Fix text size and smallest margin #1085
|
||||||
|
* Fix number of lines during an edition #1073
|
||||||
|
* Fix Magikeyboard URL auto action #1100
|
||||||
|
* Fix exception after group name change and save #1112
|
||||||
|
* Fix timeout reset #1107
|
||||||
|
* Fix search actions #1091 #1092
|
||||||
|
* Small changes #1106 #1085
|
||||||
@@ -4,4 +4,4 @@
|
|||||||
* 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
|
||||||
* Meilleure implémentation du remplissage auto #943 #946 #984 #1070 (Thx @uduerholz)
|
* Meilleure implémentation du remplissage auto #943 #946 #984 #1070 (Thx @uduerholz)
|
||||||
* Améliorations #680 #1035 #1043 #942 #1021 #1027
|
* Améliorations #680 #1035 #1043 #942 #1021 #1027 #1046 #1082 #1083 (Thx @chenxiaolong)
|
||||||
7
fastlane/metadata/android/fr-FR/changelogs/88.txt
Normal file
7
fastlane/metadata/android/fr-FR/changelogs/88.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
* Correction de la taille de texte et plus petites marges #1085
|
||||||
|
* Correction du nombre de lignes d'édition #1073
|
||||||
|
* Correction de l'action auto de l'URL Magiclavier #1100
|
||||||
|
* Correction de l'exception après un changement de nom de groupe et sauvegarde #1112
|
||||||
|
* Correction de la réinitialisation du délai d'expiration #1107
|
||||||
|
* Correction des actions de recherche #1091 #1092
|
||||||
|
* Petits changements #1106 #1085
|
||||||
Reference in New Issue
Block a user