Fix attachments deletion and change entry edit layout

This commit is contained in:
J-Jamet
2020-08-15 15:28:25 +02:00
parent 2d2489443a
commit f13f6dc01f
13 changed files with 220 additions and 132 deletions

View File

@@ -332,22 +332,16 @@ class EntryActivity : LockingActivity() {
entryContentsView?.setHiddenPasswordStyle(!mShowPassword)
// Manage attachments
val attachments = entry.getAttachments()
val showAttachmentsView = attachments.isNotEmpty()
entryContentsView?.showAttachments(showAttachmentsView)
if (showAttachmentsView) {
entryContentsView?.assignAttachments(attachments)
entryContentsView?.onAttachmentClick { attachmentItem, _ ->
when (attachmentItem.downloadState) {
AttachmentState.NULL, AttachmentState.ERROR, AttachmentState.COMPLETE -> {
createDocument(this, attachmentItem.name)?.let { requestCode ->
mAttachmentsToDownload[requestCode] = attachmentItem
}
}
else -> {
// TODO Stop download
entryContentsView?.assignAttachments(entry.getAttachments()) { attachmentItem ->
when (attachmentItem.downloadState) {
AttachmentState.NULL, AttachmentState.ERROR, AttachmentState.COMPLETE -> {
createDocument(this, attachmentItem.name)?.let { requestCode ->
mAttachmentsToDownload[requestCode] = attachmentItem
}
}
else -> {
// TODO Stop download
}
}
}

View File

@@ -301,10 +301,9 @@ class EntryEditActivity : LockingActivity(),
assignExtraFields(newEntry.customFields.mapTo(ArrayList()) {
Field(it.key, it.value)
})
assignAttachments(newEntry.getAttachments())
onAttachmentDeleted { attachment, _ ->
assignAttachments(newEntry.getAttachments(), { attachment ->
newEntry.removeAttachment(attachment)
}
})
}
}

View File

@@ -32,14 +32,17 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.model.AttachmentState
import com.kunzisoft.keepass.model.EntryAttachment
import com.kunzisoft.keepass.view.collapse
class EntryAttachmentsAdapter(val context: Context, private val editable: Boolean)
: RecyclerView.Adapter<EntryAttachmentsAdapter.EntryBinariesViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
var entryAttachmentsList: MutableList<EntryAttachment> = ArrayList()
var onItemClickListener: ((item: EntryAttachment, position: Int)->Unit)? = null
var onDeleteListener: ((item: EntryAttachment, position: Int)->Unit)? = null
var onItemClickListener: ((item: EntryAttachment)->Unit)? = null
var onDeleteListener: ((item: EntryAttachment, lastOne: Boolean)->Unit)? = null
private var mAttachmentToRemove: EntryAttachment? = null
private val mDatabase = Database.getInstance()
@@ -67,9 +70,16 @@ class EntryAttachmentsAdapter(val context: Context, private val editable: Boolea
holder.binaryFileProgressContainer.visibility = View.GONE
holder.binaryFileDeleteButton.apply {
visibility = View.VISIBLE
setOnClickListener {
onDeleteListener?.invoke(entryAttachment, position)
deleteAttachment(entryAttachment)
if (mAttachmentToRemove == entryAttachment) {
holder.itemView.collapse(true) {
deleteAttachment(entryAttachment)
}
setOnClickListener(null)
} else {
setOnClickListener {
mAttachmentToRemove = entryAttachment
notifyItemChanged(position)
}
}
}
} else {
@@ -83,7 +93,7 @@ class EntryAttachmentsAdapter(val context: Context, private val editable: Boolea
progress = entryAttachment.downloadProgression
}
holder.itemView.setOnClickListener {
onItemClickListener?.invoke(entryAttachment, position)
onItemClickListener?.invoke(entryAttachment)
}
}
}
@@ -100,13 +110,17 @@ class EntryAttachmentsAdapter(val context: Context, private val editable: Boolea
notifyDataSetChanged()
}
fun deleteAttachment(attachment: EntryAttachment) {
private fun deleteAttachment(attachment: EntryAttachment) {
val position = entryAttachmentsList.indexOf(attachment)
if (position >= 0) {
entryAttachmentsList.removeAt(position)
notifyItemRemoved(position)
onDeleteListener?.invoke(attachment, entryAttachmentsList.isEmpty())
mAttachmentToRemove = null
for (i in 0 until entryAttachmentsList.size) {
notifyItemChanged(i)
}
}
// TODO Animation
}
fun updateProgress(entryAttachment: EntryAttachment) {

View File

@@ -75,8 +75,8 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
private val notesContainerView: View
private val notesView: TextView
private val extrasContainerView: View
private val extrasView: ViewGroup
private val entryExtraFieldsContainerParent: View
private val entryExtraFieldsContainer: ViewGroup
private val creationDateView: TextView
private val modificationDateView: TextView
@@ -124,8 +124,8 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
notesContainerView = findViewById(R.id.entry_notes_container)
notesView = findViewById(R.id.entry_notes)
extrasContainerView = findViewById(R.id.extra_strings_container)
extrasView = findViewById(R.id.extra_strings)
entryExtraFieldsContainerParent = findViewById(R.id.extra_fields_container_parent)
entryExtraFieldsContainer = findViewById(R.id.extra_fields_container)
attachmentsContainerView = findViewById(R.id.entry_attachments_container)
attachmentsListView = findViewById(R.id.entry_attachments_list)
@@ -206,10 +206,10 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
}
fun atLeastOneFieldProtectedPresent(): Boolean {
extrasView.let {
entryExtraFieldsContainer.let {
for (i in 0 until it.childCount) {
val childCustomView = it.getChildAt(i)
if (childCustomView is EntryCustomField)
if (childCustomView is EntryExtraField)
if (childCustomView.isProtected)
return true
}
@@ -220,10 +220,10 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
fun setHiddenPasswordStyle(hiddenStyle: Boolean) {
passwordView.applyHiddenStyle(hiddenStyle)
// Hidden style for custom fields
extrasView.let {
entryExtraFieldsContainer.let {
for (i in 0 until it.childCount) {
val childCustomView = it.getChildAt(i)
if (childCustomView is EntryCustomField)
if (childCustomView is EntryExtraField)
childCustomView.setHiddenPasswordStyle(hiddenStyle)
}
}
@@ -304,30 +304,6 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
}
}
fun addExtraField(title: String,
value: ProtectedString,
enableActionButton: Boolean,
onActionClickListener: OnClickListener?) {
val entryCustomField: EntryCustomField? = EntryCustomField(context, attrs, defStyle)
entryCustomField?.apply {
setLabel(title)
setValue(value.toString(), value.isProtected)
enableActionButton(enableActionButton)
assignActionButtonClickListener(onActionClickListener)
applyFontVisibility(fontInVisibility)
}
entryCustomField?.let {
extrasView.addView(it)
}
extrasContainerView.visibility = View.VISIBLE
}
fun clearExtraFields() {
extrasView.removeAllViews()
extrasContainerView.visibility = View.GONE
}
fun assignCreationDate(date: DateInstant) {
creationDateView.text = date.getDateTimeString(resources)
}
@@ -357,29 +333,61 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
uuidReferenceView.text = UuidUtil.toHexString(uuid)
}
/* -------------
* Extra Fields
* -------------
*/
private fun showOrHideExtraFieldsContainer(hide: Boolean) {
entryExtraFieldsContainerParent.visibility = if (hide) View.GONE else View.VISIBLE
}
fun addExtraField(title: String,
value: ProtectedString,
enableActionButton: Boolean,
onActionClickListener: OnClickListener?) {
val entryCustomField: EntryExtraField? = EntryExtraField(context, attrs, defStyle)
entryCustomField?.apply {
setLabel(title)
setValue(value.toString(), value.isProtected)
enableActionButton(enableActionButton)
assignActionButtonClickListener(onActionClickListener)
applyFontVisibility(fontInVisibility)
}
entryCustomField?.let {
entryExtraFieldsContainer.addView(it)
}
showOrHideExtraFieldsContainer(false)
}
fun clearExtraFields() {
entryExtraFieldsContainer.removeAllViews()
showOrHideExtraFieldsContainer(true)
}
/* -------------
* Attachments
* -------------
*/
fun showAttachments(show: Boolean) {
private fun showAttachments(show: Boolean) {
attachmentsContainerView.visibility = if (show) View.VISIBLE else View.GONE
}
fun assignAttachments(attachments: ArrayList<EntryAttachment>) {
fun assignAttachments(attachments: ArrayList<EntryAttachment>,
onAttachmentClicked: (attachment: EntryAttachment)->Unit) {
showAttachments(attachments.isNotEmpty())
attachmentsAdapter.assignAttachments(attachments)
attachmentsAdapter.onItemClickListener = { item ->
onAttachmentClicked.invoke(item)
}
}
fun updateAttachmentDownloadProgress(attachmentToDownload: EntryAttachment) {
attachmentsAdapter.updateProgress(attachmentToDownload)
}
fun onAttachmentClick(action: (attachment: EntryAttachment, position: Int)->Unit) {
attachmentsAdapter.onItemClickListener = { item, position ->
action.invoke(item, position)
}
}
/* -------------
* History
* -------------

View File

@@ -60,7 +60,9 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
private val entryExpiresCheckBox: CompoundButton
private val entryExpiresTextView: TextView
private val entryNotesView: EditText
private val entryExtraFieldsContainerParent: ViewGroup
private val entryExtraFieldsContainer: ViewGroup
private val attachmentsContainerView: View
private val attachmentsListView: RecyclerView
private val attachmentsAdapter = EntryAttachmentsAdapter(context, true)
@@ -92,7 +94,9 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
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_extra_fields_container)
entryExtraFieldsContainerParent = findViewById(R.id.extra_fields_container_parent)
entryExtraFieldsContainer = findViewById(R.id.extra_fields_container)
attachmentsContainerView = findViewById(R.id.entry_attachments_container)
attachmentsListView = findViewById(R.id.entry_attachments_list)
attachmentsListView?.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
@@ -218,6 +222,11 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
entryNotesView.applyFontVisibility()
}
/* -------------
* Extra Fields
* -------------
*/
val customFields: MutableList<Field>
get() {
val customFieldsArray = ArrayList<Field>()
@@ -226,7 +235,7 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
try {
for (i in 0 until it.childCount) {
val view = it.getChildAt(i) as EntryEditExtraField
customFieldsArray.add(view.customField)
customFieldsArray.add(view.extraField)
}
} catch (exception: Exception) {
// Extra field container contains another type of view
@@ -235,11 +244,11 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
return customFieldsArray
}
private fun getCustomFieldByLabel(label: String): EntryEditExtraField? {
private fun getExtraFieldByLabel(label: String): EntryEditExtraField? {
for (i in 0..entryExtraFieldsContainer.childCount) {
try {
val extraFieldView = entryExtraFieldsContainer.getChildAt(i) as EntryEditExtraField?
if (extraFieldView?.customField?.name == label) {
if (extraFieldView?.extraField?.name == label) {
return extraFieldView
}
} catch(e: Exception) {
@@ -249,15 +258,34 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
return null
}
private fun showOrHideExtraFieldsContainer(hide: Boolean, animate: Boolean = false) {
entryExtraFieldsContainerParent.apply {
if (!animate)
visibility = if (hide) View.GONE else View.VISIBLE
else
if (hide) collapse(true) else expand(true)
}
}
private fun buildNewEntryEditExtraField(): EntryEditExtraField {
return EntryEditExtraField(context).apply {
applyFontVisibility(fontInVisibility)
onViewDeletedListener = {
// remove callback
showOrHideExtraFieldsContainer(entryExtraFieldsContainer.childCount <= 1, true)
}
}
}
/**
* Remove all children and add new views for each field
*/
fun assignExtraFields(fields: List<Field>) {
showOrHideExtraFieldsContainer(fields.isEmpty())
entryExtraFieldsContainer.removeAllViews()
fields.forEach { extraField ->
entryExtraFieldsContainer.addView(EntryEditExtraField(context).apply {
setFontVisibility(fontInVisibility)
customField = extraField
entryExtraFieldsContainer.addView(buildNewEntryEditExtraField().apply {
this.extraField = extraField
})
}
}
@@ -265,27 +293,46 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
/**
* Update an extra field or create a new one if doesn't exists
*/
fun putExtraField(customField: Field)
fun putExtraField(extraField: Field)
: EntryEditExtraField {
var extraFieldView = getCustomFieldByLabel(customField.name)
showOrHideExtraFieldsContainer(false)
var extraFieldView = getExtraFieldByLabel(extraField.name)
// Create new view if not exists
if (extraFieldView == null) {
extraFieldView = EntryEditExtraField(context)
extraFieldView.setFontVisibility(fontInVisibility)
extraFieldView = buildNewEntryEditExtraField()
// No need animation because of scroll
entryExtraFieldsContainer.addView(extraFieldView)
}
extraFieldView.customField = customField
extraFieldView.extraField = extraField
return extraFieldView
}
fun assignAttachments(attachments: java.util.ArrayList<EntryAttachment>) {
attachmentsAdapter.assignAttachments(attachments)
/* -------------
* Attachments
* -------------
*/
private fun showOrHideAttachmentsContainer(hide: Boolean, animate: Boolean = false) {
attachmentsContainerView.apply {
if (!animate)
visibility = if (hide) View.GONE else View.VISIBLE
else
if (hide) collapse(true) else expand(true)
}
}
fun onAttachmentDeleted(action: (attachment: EntryAttachment, position: Int)->Unit) {
attachmentsAdapter.onDeleteListener = { item, position ->
action.invoke(item, position)
fun assignAttachments(attachments: java.util.ArrayList<EntryAttachment>,
onAttachmentClicked: (attachment: EntryAttachment)->Unit,
onAttachmentDeleted: ((attachment: EntryAttachment)->Unit)? = null) {
showOrHideAttachmentsContainer(attachments.isEmpty())
attachmentsAdapter.assignAttachments(attachments)
attachmentsAdapter.onItemClickListener = { item ->
onAttachmentClicked.invoke(item)
}
attachmentsAdapter.onDeleteListener = { item, lastOne ->
onAttachmentDeleted?.invoke(item)
if (lastOne)
showOrHideAttachmentsContainer(hide = true, animate = true)
}
}
@@ -301,11 +348,11 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context,
val customFieldLabelSet = HashSet<String>()
for (i in 0 until it.childCount) {
val entryEditCustomField = it.getChildAt(i) as EntryEditExtraField
if (customFieldLabelSet.contains(entryEditCustomField.customField.name)) {
if (customFieldLabelSet.contains(entryEditCustomField.extraField.name)) {
entryEditCustomField.setError(R.string.error_label_exists)
return false
}
customFieldLabelSet.add(entryEditCustomField.customField.name)
customFieldLabelSet.add(entryEditCustomField.extraField.name)
if (!entryEditCustomField.isValid()) {
return false
}

View File

@@ -47,7 +47,9 @@ class EntryEditExtraField @JvmOverloads constructor(context: Context,
private var isProtected = false
private var mValueViewInputType: Int = 0
var customField: Field
var onViewDeletedListener: ((Field) -> Unit)? = null
var extraField: Field
get() {
return Field(valueLayoutView.hint.toString(), ProtectedString(isProtected, valueView.text.toString()))
}
@@ -75,6 +77,8 @@ class EntryEditExtraField @JvmOverloads constructor(context: Context,
mValueViewInputType = valueView.inputType
deleteButton.setOnClickListener {
collapse(true) {
onViewDeletedListener?.invoke(extraField)
onViewDeletedListener = null
(parent as ViewGroup?)?.apply {
removeView(this@EntryEditExtraField)
}
@@ -82,6 +86,12 @@ class EntryEditExtraField @JvmOverloads constructor(context: Context,
}
}
fun applyFontVisibility(applyFontVisibility: Boolean) {
mApplyFontVisibility = applyFontVisibility
if (applyFontVisibility)
valueView.applyFontVisibility()
}
/**
* Validate or not the entry form
*
@@ -104,12 +114,6 @@ class EntryEditExtraField @JvmOverloads constructor(context: Context,
}
}
fun setFontVisibility(applyFontVisibility: Boolean) {
mApplyFontVisibility = applyFontVisibility
if (applyFontVisibility)
valueView.applyFontVisibility()
}
override fun requestFocus(direction: Int, previouslyFocusedRect: Rect?): Boolean {
valueView.requestFocus(direction, previouslyFocusedRect)
return true

View File

@@ -29,9 +29,9 @@ import android.widget.TextView
import androidx.core.content.ContextCompat
import com.kunzisoft.keepass.R
class EntryCustomField @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0)
class EntryExtraField @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0)
: LinearLayout(context, attrs, defStyle) {
private val labelView: TextView

View File

@@ -116,8 +116,8 @@
<com.kunzisoft.keepass.view.EntryContentsView
android:id="@+id/entry_contents"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintWidth_percent="@dimen/content_percent"
app:layout_constraintTop_toBottomOf="@+id/history_container"
app:layout_constraintStart_toStartOf="parent"

View File

@@ -50,15 +50,20 @@
android:scrollbarStyle="insideOverlay"
android:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.kunzisoft.keepass.view.EntryEditContentsView
android:id="@+id/entry_edit_contents"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="@dimen/content_percent" />
android:layout_height="match_parent"
android:paddingBottom="?attr/actionBarSize">
<com.kunzisoft.keepass.view.EntryEditContentsView
android:id="@+id/entry_edit_contents"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintWidth_percent="@dimen/content_percent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.bottomappbar.BottomAppBar

View File

@@ -79,7 +79,7 @@
android:padding="6dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/ic_close_white_24dp"
android:src="@drawable/ic_content_delete_white_24dp"
app:tint="?attr/colorAccent"
android:contentDescription="@string/content_description_remove_field">
</ImageView>

View File

@@ -53,7 +53,7 @@
android:layout_marginTop="12dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/ic_close_white_24dp"
android:src="@drawable/ic_content_delete_white_24dp"
android:contentDescription="@string/content_description_remove_field"
android:tint="?attr/colorAccent" />

View File

@@ -20,8 +20,8 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/entry_table"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
@@ -185,9 +185,8 @@
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Extras -->
<androidx.cardview.widget.CardView
android:id="@+id/extra_strings_container"
android:id="@+id/extra_fields_container_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
@@ -197,7 +196,7 @@
android:layout_marginRight="@dimen/card_view_margin"
android:layout_marginBottom="@dimen/card_view_margin_bottom">
<LinearLayout
android:id="@+id/extra_strings"
android:id="@+id/extra_fields_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_view_padding"

View File

@@ -29,17 +29,18 @@
android:id="@+id/entry_edit_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/card_view_margin"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:layout_marginTop="@dimen/card_view_margin_bottom"
android:layout_marginStart="@dimen/card_view_margin"
android:layout_marginEnd="@dimen/card_view_margin"
android:layout_marginLeft="@dimen/card_view_margin"
android:layout_marginRight="@dimen/card_view_margin"
android:layout_marginBottom="@dimen/card_view_margin_bottom"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin">
android:layout_margin="@dimen/card_view_padding">
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/entry_edit_icon_button"
@@ -199,39 +200,56 @@
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/entry_edit_extra_fields_container_parent"
android:id="@+id/extra_fields_container_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginTop="@dimen/card_view_margin_bottom"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:layout_marginStart="@dimen/card_view_margin"
android:layout_marginEnd="@dimen/card_view_margin"
android:layout_marginLeft="@dimen/card_view_margin"
android:layout_marginRight="@dimen/card_view_margin"
android:layout_marginBottom="@dimen/card_view_margin_bottom"
app:layout_constraintTop_toBottomOf="@+id/entry_edit_container">
<LinearLayout
android:id="@+id/entry_edit_extra_fields_container"
android:id="@+id/extra_fields_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:layout_margin="@dimen/card_view_padding"
android:orientation="vertical">
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/entry_edit_attachments_container_parent"
android:id="@+id/entry_attachments_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/card_view_margin_bottom"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
app:layout_constraintTop_toBottomOf="@+id/entry_edit_extra_fields_container_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/entry_attachments_list"
android:layout_width="match_parent"
android:layout_marginStart="@dimen/card_view_margin"
android:layout_marginEnd="@dimen/card_view_margin"
android:layout_marginLeft="@dimen/card_view_margin"
android:layout_marginRight="@dimen/card_view_margin"
app:layout_constraintTop_toBottomOf="@+id/extra_fields_container_parent">
<LinearLayout
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin" />
android:layout_width="match_parent"
android:layout_margin="@dimen/card_view_padding"
android:orientation="vertical">
<!-- Binary files -->
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/entry_attachments_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/entry_attachments"
style="@style/KeepassDXStyle.TextAppearance.LabelTextStyle" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/entry_attachments_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>