mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Add thread to load images
This commit is contained in:
@@ -126,6 +126,7 @@ class EntryActivity : LockingActivity() {
|
|||||||
historyView = findViewById(R.id.history_container)
|
historyView = findViewById(R.id.history_container)
|
||||||
entryContentsView = findViewById(R.id.entry_contents)
|
entryContentsView = findViewById(R.id.entry_contents)
|
||||||
entryContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this))
|
entryContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this))
|
||||||
|
entryContentsView?.setAttachmentCipherKey(mDatabase?.loadedCipherKey)
|
||||||
entryProgress = findViewById(R.id.entry_progress)
|
entryProgress = findViewById(R.id.entry_progress)
|
||||||
lockView = findViewById(R.id.lock_button)
|
lockView = findViewById(R.id.lock_button)
|
||||||
|
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ class EntryEditActivity : LockingActivity(),
|
|||||||
// Build fragment to manage entry modification
|
// Build fragment to manage entry modification
|
||||||
entryEditFragment = supportFragmentManager.findFragmentByTag(ENTRY_EDIT_FRAGMENT_TAG) as? EntryEditFragment?
|
entryEditFragment = supportFragmentManager.findFragmentByTag(ENTRY_EDIT_FRAGMENT_TAG) as? EntryEditFragment?
|
||||||
if (entryEditFragment == null) {
|
if (entryEditFragment == null) {
|
||||||
entryEditFragment = EntryEditFragment.getInstance(tempEntryInfo)
|
entryEditFragment = EntryEditFragment.getInstance(tempEntryInfo, mDatabase?.loadedCipherKey)
|
||||||
}
|
}
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.entry_edit_contents, entryEditFragment!!, ENTRY_EDIT_FRAGMENT_TAG)
|
.replace(R.id.entry_edit_contents, entryEditFragment!!, ENTRY_EDIT_FRAGMENT_TAG)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrCha
|
|||||||
import com.kunzisoft.keepass.activities.stylish.StylishFragment
|
import com.kunzisoft.keepass.activities.stylish.StylishFragment
|
||||||
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
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.DateInstant
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||||
import com.kunzisoft.keepass.education.EntryEditActivityEducation
|
import com.kunzisoft.keepass.education.EntryEditActivityEducation
|
||||||
@@ -86,6 +87,7 @@ class EntryEditFragment: StylishFragment() {
|
|||||||
|
|
||||||
// Elements to modify the current entry
|
// Elements to modify the current entry
|
||||||
private var mEntryInfo = EntryInfo()
|
private var mEntryInfo = EntryInfo()
|
||||||
|
private var mBinaryCipherKey: Database.LoadedKey? = null
|
||||||
private var mLastFocusedEditField: FocusedEditField? = null
|
private var mLastFocusedEditField: FocusedEditField? = null
|
||||||
private var mExtraViewToRequestFocus: EditText? = null
|
private var mExtraViewToRequestFocus: EditText? = null
|
||||||
|
|
||||||
@@ -127,6 +129,7 @@ class EntryEditFragment: StylishFragment() {
|
|||||||
attachmentsContainerView = rootView.findViewById(R.id.entry_attachments_container)
|
attachmentsContainerView = rootView.findViewById(R.id.entry_attachments_container)
|
||||||
attachmentsListView = rootView.findViewById(R.id.entry_attachments_list)
|
attachmentsListView = rootView.findViewById(R.id.entry_attachments_list)
|
||||||
attachmentsAdapter = EntryAttachmentsItemsAdapter(requireContext())
|
attachmentsAdapter = EntryAttachmentsItemsAdapter(requireContext())
|
||||||
|
attachmentsAdapter.binaryCipherKey = arguments?.getSerializable(KEY_BINARY_CIPHER_KEY) as? Database.LoadedKey?
|
||||||
attachmentsAdapter.onListSizeChangedListener = { previousSize, newSize ->
|
attachmentsAdapter.onListSizeChangedListener = { previousSize, newSize ->
|
||||||
if (previousSize > 0 && newSize == 0) {
|
if (previousSize > 0 && newSize == 0) {
|
||||||
attachmentsContainerView.collapse(true)
|
attachmentsContainerView.collapse(true)
|
||||||
@@ -528,6 +531,7 @@ class EntryEditFragment: StylishFragment() {
|
|||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
populateEntryWithViews()
|
populateEntryWithViews()
|
||||||
outState.putParcelable(KEY_TEMP_ENTRY_INFO, mEntryInfo)
|
outState.putParcelable(KEY_TEMP_ENTRY_INFO, mEntryInfo)
|
||||||
|
outState.putSerializable(KEY_BINARY_CIPHER_KEY, mBinaryCipherKey)
|
||||||
outState.putParcelable(KEY_LAST_FOCUSED_FIELD, mLastFocusedEditField)
|
outState.putParcelable(KEY_LAST_FOCUSED_FIELD, mLastFocusedEditField)
|
||||||
|
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
@@ -535,12 +539,15 @@ class EntryEditFragment: StylishFragment() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val KEY_TEMP_ENTRY_INFO = "KEY_TEMP_ENTRY_INFO"
|
const val KEY_TEMP_ENTRY_INFO = "KEY_TEMP_ENTRY_INFO"
|
||||||
|
const val KEY_BINARY_CIPHER_KEY = "KEY_BINARY_CIPHER_KEY"
|
||||||
const val KEY_LAST_FOCUSED_FIELD = "KEY_LAST_FOCUSED_FIELD"
|
const val KEY_LAST_FOCUSED_FIELD = "KEY_LAST_FOCUSED_FIELD"
|
||||||
|
|
||||||
fun getInstance(entryInfo: EntryInfo?): EntryEditFragment {
|
fun getInstance(entryInfo: EntryInfo?,
|
||||||
|
loadedKey: Database.LoadedKey?): EntryEditFragment {
|
||||||
return EntryEditFragment().apply {
|
return EntryEditFragment().apply {
|
||||||
arguments = Bundle().apply {
|
arguments = Bundle().apply {
|
||||||
putParcelable(KEY_TEMP_ENTRY_INFO, entryInfo)
|
putParcelable(KEY_TEMP_ENTRY_INFO, entryInfo)
|
||||||
|
putSerializable(KEY_BINARY_CIPHER_KEY, loadedKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,15 @@ package com.kunzisoft.keepass.activities
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import com.igreenwood.loupe.Loupe
|
import com.igreenwood.loupe.Loupe
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import kotlinx.android.synthetic.main.activity_image_viewer.*
|
import kotlinx.android.synthetic.main.activity_image_viewer.*
|
||||||
|
|
||||||
class ImageViewerActivity : LockingActivity() {
|
class ImageViewerActivity : LockingActivity() {
|
||||||
@@ -38,13 +39,20 @@ class ImageViewerActivity : LockingActivity() {
|
|||||||
|
|
||||||
setContentView(R.layout.activity_image_viewer)
|
setContentView(R.layout.activity_image_viewer)
|
||||||
val imageView: ImageView = findViewById(R.id.image_viewer_image)
|
val imageView: ImageView = findViewById(R.id.image_viewer_image)
|
||||||
|
val progressView: View = findViewById(R.id.image_viewer_progress)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
progressView.visibility = View.VISIBLE
|
||||||
intent.getParcelableExtra<Attachment>(IMAGE_ATTACHMENT_TAG)?.let { attachment ->
|
intent.getParcelableExtra<Attachment>(IMAGE_ATTACHMENT_TAG)?.let { attachment ->
|
||||||
BitmapFactory.decodeStream(attachment.binaryAttachment.getUnGzipInputDataStream())?.let { imageBitmap ->
|
Attachment.loadBitmap(attachment, Database.getInstance().loadedCipherKey) { bitmapLoaded ->
|
||||||
imageView.setImageBitmap(imageBitmap)
|
if (bitmapLoaded == null) {
|
||||||
} ?: finish()
|
finish()
|
||||||
}
|
} else {
|
||||||
|
progressView.visibility = View.GONE
|
||||||
|
imageView.setImageBitmap(bitmapLoaded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: finish()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Unable to view the binary", e)
|
Log.e(TAG, "Unable to view the binary", e)
|
||||||
finish()
|
finish()
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.adapters
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.TypedArray
|
import android.content.res.TypedArray
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.text.format.Formatter
|
import android.text.format.Formatter
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
@@ -33,6 +33,8 @@ import android.widget.TextView
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.ImageViewerActivity
|
import com.kunzisoft.keepass.activities.ImageViewerActivity
|
||||||
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
||||||
import com.kunzisoft.keepass.model.AttachmentState
|
import com.kunzisoft.keepass.model.AttachmentState
|
||||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||||
@@ -42,8 +44,12 @@ import com.kunzisoft.keepass.model.StreamDirection
|
|||||||
class EntryAttachmentsItemsAdapter(context: Context)
|
class EntryAttachmentsItemsAdapter(context: Context)
|
||||||
: AnimatedItemsAdapter<EntryAttachmentState, EntryAttachmentsItemsAdapter.EntryBinariesViewHolder>(context) {
|
: AnimatedItemsAdapter<EntryAttachmentState, EntryAttachmentsItemsAdapter.EntryBinariesViewHolder>(context) {
|
||||||
|
|
||||||
|
var binaryCipherKey: Database.LoadedKey? = null
|
||||||
var onItemClickListener: ((item: EntryAttachmentState)->Unit)? = null
|
var onItemClickListener: ((item: EntryAttachmentState)->Unit)? = null
|
||||||
|
|
||||||
|
// To manage image preview loading
|
||||||
|
private val imageBinariesSet = HashMap<Int, Bitmap?>()
|
||||||
|
|
||||||
private var mTitleColor: Int
|
private var mTitleColor: Int
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -65,14 +71,27 @@ class EntryAttachmentsItemsAdapter(context: Context)
|
|||||||
|
|
||||||
holder.itemView.visibility = View.VISIBLE
|
holder.itemView.visibility = View.VISIBLE
|
||||||
holder.binaryFileThumbnail.apply {
|
holder.binaryFileThumbnail.apply {
|
||||||
BitmapFactory.decodeStream(entryAttachmentState.attachment.binaryAttachment.getUnGzipInputDataStream())?.let { imageBitmap ->
|
// Show the bitmap image if loaded
|
||||||
setImageBitmap(imageBitmap)
|
if (imageBinariesSet.containsKey(position)) {
|
||||||
setOnClickListener {
|
if (imageBinariesSet[position] != null) {
|
||||||
ImageViewerActivity.getInstance(context, entryAttachmentState.attachment)
|
setImageBitmap(imageBinariesSet[position])
|
||||||
}
|
}
|
||||||
visibility = View.VISIBLE
|
} else {
|
||||||
} ?: run {
|
imageBinariesSet[position] = null
|
||||||
visibility = View.GONE
|
// Load the bitmap image
|
||||||
|
Attachment.loadBitmap(entryAttachmentState.attachment, binaryCipherKey) { imageLoaded ->
|
||||||
|
imageBinariesSet[position] = imageLoaded
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setOnClickListener {
|
||||||
|
ImageViewerActivity.getInstance(context, entryAttachmentState.attachment)
|
||||||
|
}
|
||||||
|
visibility = if (imageBinariesSet.containsKey(position)) {
|
||||||
|
setImageBitmap(imageBinariesSet[position])
|
||||||
|
View.VISIBLE
|
||||||
|
} else {
|
||||||
|
View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
holder.binaryFileBroken.apply {
|
holder.binaryFileBroken.apply {
|
||||||
|
|||||||
@@ -19,9 +19,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.element
|
package com.kunzisoft.keepass.database.element
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.kunzisoft.keepass.database.element.database.BinaryAttachment
|
import com.kunzisoft.keepass.database.element.database.BinaryAttachment
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
data class Attachment(var name: String,
|
data class Attachment(var name: String,
|
||||||
var binaryAttachment: BinaryAttachment) : Parcelable {
|
var binaryAttachment: BinaryAttachment) : Parcelable {
|
||||||
@@ -65,5 +68,25 @@ data class Attachment(var name: String,
|
|||||||
override fun newArray(size: Int): Array<Attachment?> {
|
override fun newArray(size: Int): Array<Attachment?> {
|
||||||
return arrayOfNulls(size)
|
return arrayOfNulls(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadBitmap(attachment: Attachment,
|
||||||
|
binaryCipherKey: Database.LoadedKey?,
|
||||||
|
actionOnFinish: (Bitmap?) -> Unit) {
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val asyncResult: Deferred<Bitmap?> = async {
|
||||||
|
runCatching {
|
||||||
|
binaryCipherKey?.let { binaryKey ->
|
||||||
|
BitmapFactory.decodeStream(attachment.binaryAttachment
|
||||||
|
.getUnGzipInputDataStream(binaryKey))
|
||||||
|
}
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
actionOnFinish(asyncResult.await())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,14 +346,14 @@ class Database {
|
|||||||
this.loaded = true
|
this.loaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoadedKey(val key: Key, val iv: IvParameterSpec) {
|
class LoadedKey(val key: Key, val iv: ByteArray): Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
const val BINARY_CIPHER = "Blowfish/CBC/PKCS5Padding"
|
const val BINARY_CIPHER = "Blowfish/CBC/PKCS5Padding"
|
||||||
|
|
||||||
fun generateNewCipherKey(): LoadedKey {
|
fun generateNewCipherKey(): LoadedKey {
|
||||||
val iv = ByteArray(8)
|
val iv = ByteArray(8)
|
||||||
SecureRandom().nextBytes(iv)
|
SecureRandom().nextBytes(iv)
|
||||||
return LoadedKey(KeyGenerator.getInstance("Blowfish").generateKey(), IvParameterSpec(iv))
|
return LoadedKey(KeyGenerator.getInstance("Blowfish").generateKey(), iv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import java.util.zip.GZIPOutputStream
|
|||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.CipherInputStream
|
import javax.crypto.CipherInputStream
|
||||||
import javax.crypto.CipherOutputStream
|
import javax.crypto.CipherOutputStream
|
||||||
|
import javax.crypto.spec.IvParameterSpec
|
||||||
|
|
||||||
class BinaryAttachment : Parcelable {
|
class BinaryAttachment : Parcelable {
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ class BinaryAttachment : Parcelable {
|
|||||||
private fun buildInputStream(file: File?, cipherKey: Database.LoadedKey): InputStream {
|
private fun buildInputStream(file: File?, cipherKey: Database.LoadedKey): InputStream {
|
||||||
return when {
|
return when {
|
||||||
file != null && file.length() > 0 -> {
|
file != null && file.length() > 0 -> {
|
||||||
cipherDecryption.init(Cipher.DECRYPT_MODE, cipherKey.key, cipherKey.iv)
|
cipherDecryption.init(Cipher.DECRYPT_MODE, cipherKey.key, IvParameterSpec(cipherKey.iv))
|
||||||
Base64InputStream(CipherInputStream(FileInputStream(file), cipherDecryption), Base64.NO_WRAP)
|
Base64InputStream(CipherInputStream(FileInputStream(file), cipherDecryption), Base64.NO_WRAP)
|
||||||
}
|
}
|
||||||
else -> ByteArrayInputStream(ByteArray(0))
|
else -> ByteArrayInputStream(ByteArray(0))
|
||||||
@@ -112,7 +113,7 @@ class BinaryAttachment : Parcelable {
|
|||||||
private fun buildOutputStream(file: File?, cipherKey: Database.LoadedKey): OutputStream {
|
private fun buildOutputStream(file: File?, cipherKey: Database.LoadedKey): OutputStream {
|
||||||
return when {
|
return when {
|
||||||
file != null -> {
|
file != null -> {
|
||||||
cipherEncryption.init(Cipher.ENCRYPT_MODE, cipherKey.key, cipherKey.iv)
|
cipherEncryption.init(Cipher.ENCRYPT_MODE, cipherKey.key, IvParameterSpec(cipherKey.iv))
|
||||||
BinaryCountingOutputStream(Base64OutputStream(CipherOutputStream(FileOutputStream(file), cipherEncryption), Base64.NO_WRAP))
|
BinaryCountingOutputStream(Base64OutputStream(CipherOutputStream(FileOutputStream(file), cipherEncryption), Base64.NO_WRAP))
|
||||||
}
|
}
|
||||||
else -> throw IOException("Unable to write in an unknown file")
|
else -> throw IOException("Unable to write in an unknown file")
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
||||||
import com.kunzisoft.keepass.adapters.EntryHistoryAdapter
|
import com.kunzisoft.keepass.adapters.EntryHistoryAdapter
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
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.DateInstant
|
||||||
import com.kunzisoft.keepass.database.element.Entry
|
import com.kunzisoft.keepass.database.element.Entry
|
||||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||||
@@ -321,6 +322,10 @@ class EntryContentsView @JvmOverloads constructor(context: Context,
|
|||||||
* -------------
|
* -------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
fun setAttachmentCipherKey(cipherKey: Database.LoadedKey?) {
|
||||||
|
attachmentsAdapter.binaryCipherKey = cipherKey
|
||||||
|
}
|
||||||
|
|
||||||
private fun showAttachments(show: Boolean) {
|
private fun showAttachments(show: Boolean) {
|
||||||
attachmentsContainerView.visibility = if (show) View.VISIBLE else View.GONE
|
attachmentsContainerView.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?android:attr/windowBackground">
|
android:background="?android:attr/windowBackground">
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/image_viewer_progress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/image_viewer_image"
|
android:id="@+id/image_viewer_image"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -31,17 +31,7 @@
|
|||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/constraintLayout"
|
android:id="@+id/constraintLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:layout_alignParentBottom="true">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/item_attachment_thumbnail"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="72dp"
|
|
||||||
android:src="@drawable/material_00_32dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/item_attachment_broken"
|
android:id="@+id/item_attachment_broken"
|
||||||
@@ -50,7 +40,7 @@
|
|||||||
android:contentDescription="@string/entry_attachments"
|
android:contentDescription="@string/entry_attachments"
|
||||||
android:src="@drawable/ic_attach_file_broken_white_24dp"
|
android:src="@drawable/ic_attach_file_broken_white_24dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/item_attachment_thumbnail"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
@@ -59,11 +49,23 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/item_attachment_size_container"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/item_attachment_broken"
|
app:layout_constraintStart_toEndOf="@+id/item_attachment_broken"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/item_attachment_thumbnail"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="BinaryFile.attach" />
|
tools:text="BinaryFile.attach" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/item_attachment_thumbnail"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/item_attachment_size_container"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/item_attachment_title"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/item_attachment_size_container"
|
android:id="@+id/item_attachment_size_container"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
Reference in New Issue
Block a user