added productView and things around

This commit is contained in:
leca 2024-10-15 02:08:29 +03:00
parent efcd9361b2
commit 5066ded9c7
8 changed files with 456 additions and 23 deletions

View File

@ -1,11 +1,13 @@
package org.foxarmy.barcodescannerforemployees
import android.content.ContentValues
import android.content.Context
import android.database.DatabaseUtils
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.provider.BaseColumns
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import java.io.File
object AbstractProductContract {
@ -166,6 +168,17 @@ class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE
db.delete(AbstractProductContract.AbstractProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
}
fun insertNewProduct(db: SQLiteDatabase, product: Product) {
val values = ContentValues().apply {
put(ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID, product.abstractProductId)
put(ProductContract.ProductEntry.AMOUNT, product.amount)
put(ProductContract.ProductEntry.DATE_OF_PRODUCTION, product.dateOfProduction)
put(ProductContract.ProductEntry.EXPIRY_DATE, product.dateOfExpiry)
}
db.insert(ProductContract.ProductEntry.TABLE_NAME, null, values)
}
fun findAbstractProductByBarcode (db: SQLiteDatabase, barcode: String) : AbstractProduct? {
var abstractProduct: AbstractProduct? = null
val projection = arrayOf(
@ -196,6 +209,38 @@ class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE
return abstractProduct
}
fun findAbstractProductById(db: SQLiteDatabase, id: Int): AbstractProduct? {
var abstractProduct: AbstractProduct? = null
val projection = arrayOf(
BaseColumns._ID,
AbstractProductContract.AbstractProductEntry.PRODUCT_NAME,
AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT,
AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME,
AbstractProductContract.AbstractProductEntry.CATEGORY,
AbstractProductContract.AbstractProductEntry.BARCODE
)
val selection = "${BaseColumns._ID} = ?"
val selectionArgs = arrayOf(id.toString())
val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)
with (cursor) {
while (moveToNext()) {
val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
val barcode = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.BARCODE))
val name = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME))
val netWeight = getDouble(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT))
val imageHash = getString((getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME)))
val category = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.CATEGORY))
abstractProduct = AbstractProduct(productId, barcode, name, netWeight, imageHash, category)
}
}
return abstractProduct
}
companion object {
const val DATABASE_VERSION = 1
const val DATABASE_NAME = "database.db"

View File

@ -1,19 +1,40 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.app.DatePickerDialog
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
import java.util.*
class AddProductActivity : AppCompatActivity() {
private lateinit var scanButton: Button
private lateinit var abstractProductView: AbstractProductView
private lateinit var expiryDateRadioButton: RadioButton
private lateinit var shelfLifeRadioButton: RadioButton
private lateinit var expiryDateTextView: TextView
private lateinit var expiryDateSelectButton: Button
private lateinit var shelfLifeTextView: TextView
private lateinit var shelfLifeTextEdit: EditText
private lateinit var amountTextEdit: EditText
private lateinit var dateOfProductionSelectButton: Button
private lateinit var saveProductButton: Button
private var expiryDateOverShelfLife: Boolean? = null
private var dateOfProduction: String = ""
private var dateOfExpiry: String = ""
private var barcode: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -21,27 +42,139 @@ class AddProductActivity : AppCompatActivity() {
setContentView(R.layout.fragment_add_product)
val extras = intent.extras
val product = extras!!.get("product") as Product?
var product = extras!!.get("product") as Product?
scanButton = findViewById(R.id.scanButton)
abstractProductView = findViewById(R.id.abstractProductView)
/*
scanButton = findViewById(R.id.scanButton)
abstractProductView = AbstractProductView(this, this, AbstractProduct())
findViewById<ConstraintLayout>(R.id.addProductConstraintLayout).addView(abstractProductView)
*/
expiryDateRadioButton = findViewById(R.id.expiryDateRadio)
shelfLifeRadioButton = findViewById(R.id.shelfLifeRadio)
expiryDateTextView = findViewById(R.id.expiryDateTextView)
expiryDateSelectButton = findViewById(R.id.selectExpiryDateButton)
shelfLifeTextView = findViewById(R.id.shelfLife)
shelfLifeTextEdit = findViewById(R.id.shelfLifeTextEdit)
amountTextEdit = findViewById(R.id.amountTextEdit)
dateOfProductionSelectButton = findViewById(R.id.selectDateOfProductionButton)
saveProductButton = findViewById(R.id.saveProductButton)
scanButton.setOnClickListener {
val scanner = GmsBarcodeScanning.getClient(this)
scanner.startScan()
.addOnSuccessListener { barcode ->
findAndDisplayAbstractProductByBarcode(barcode.rawValue.toString())
.addOnSuccessListener { bc ->
barcode = bc.rawValue.toString()
findAndDisplayAbstractProductByBarcode(barcode)
}
.addOnFailureListener {
Toast.makeText(this, "Cannot scan barcode", Toast.LENGTH_SHORT).show()
}
}
expiryDateRadioButton.setOnClickListener {
expiryDateTextView.visibility = View.VISIBLE
expiryDateSelectButton.visibility = View.VISIBLE
shelfLifeTextEdit.visibility = View.INVISIBLE
shelfLifeTextView.visibility = View.INVISIBLE
expiryDateOverShelfLife = true
}
shelfLifeRadioButton.setOnClickListener {
expiryDateTextView.visibility = View.INVISIBLE
expiryDateSelectButton.visibility = View.INVISIBLE
shelfLifeTextEdit.visibility = View.VISIBLE
shelfLifeTextView.visibility = View.VISIBLE
expiryDateOverShelfLife = false
}
// shelfLifeTextEdit.addTextChangedListener {
// if (shelfLifeTextEdit.text.toString() == "") {
// return@addTextChangedListener
// }
//
// evaluateDateOfExpiry()
// }
dateOfProductionSelectButton.setOnClickListener {
val c = Calendar.getInstance()
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
val dpd = DatePickerDialog(this, { _, y, m, d ->
dateOfProduction = "$d.${m+1}.$y"
findViewById<TextView>(R.id.dateOfProductionTextView).text = "Date of production: $dateOfProduction"
}, year, month, day)
dpd.show()
}
expiryDateSelectButton.setOnClickListener {
val c = Calendar.getInstance()
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
val dpd = DatePickerDialog(this, { view, y, m, d ->
dateOfExpiry = "$d.${m+1}.$y"
expiryDateTextView.text = "Expiry date: $dateOfExpiry"
}, year, month, day)
dpd.show()
}
saveProductButton.setOnClickListener {
if (expiryDateOverShelfLife == null) {
Toast.makeText(this, "Please, choose and fill in shelf life or expiry date.", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (dateOfProduction == "") {
Toast.makeText(this, "Please, choose date of production.", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (!expiryDateOverShelfLife!!) {
if (shelfLifeTextEdit.text.toString() != "") {
evaluateDateOfExpiry(shelfLifeTextEdit.text.toString().toInt())
}
} else {
if (dateOfExpiry == "") {
Toast.makeText(this, "Please, choose date of expiry.", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
}
if (amountTextEdit.text.toString() == "") {
Toast.makeText(this, "Please, specify an amount of product.", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
val abstractProduct = DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, barcode)
product = Product(0, abstractProduct!!.id, amountTextEdit.text.toString().toInt(), dateOfProduction, dateOfExpiry)
DBStorageController(this).insertNewProduct(DBStorageController(this).writableDatabase, product!!)
finish()
}
}
fun evaluateDateOfExpiry(shelfLifeMonths: Int) {
if (dateOfProduction == "" ) {
return
}
val c = Calendar.getInstance()
val dateOfProductionSplit = dateOfProduction.split(".")
c.set(Calendar.YEAR, dateOfProductionSplit[0].toInt())
c.set(Calendar.MONTH, dateOfProductionSplit[1].toInt())
c.set(Calendar.DAY_OF_MONTH, dateOfProductionSplit[2].toInt())
c.add(Calendar.MONTH, shelfLifeMonths)
dateOfExpiry = "${c.get(Calendar.YEAR)}.${c.get(Calendar.MONTH)}.${c.get(Calendar.DAY_OF_MONTH)}"
}
fun findAndDisplayAbstractProductByBarcode(barcode: String) {

View File

@ -4,23 +4,34 @@ import android.os.Parcel
import android.os.Parcelable
class Product() : Parcelable {
var id = 0
var abstractProductId = 0
var amount = 0
var dateOfProduction = "01.01.1970"
var dateOfExpiry = "01.01.1970"
constructor(abstractProductId: Int, amount: Int) : this() {
constructor(id: Int, abstractProductId: Int, amount: Int, dateOfProduction: String, dateOfExpiry: String) : this() {
this.id = id
this.abstractProductId = abstractProductId
this.amount = amount
this.dateOfProduction = dateOfProduction
this.dateOfExpiry = dateOfExpiry
}
constructor(parcel: Parcel) : this() {
id = parcel.readInt()
abstractProductId = parcel.readInt()
amount = parcel.readInt()
dateOfProduction = parcel.readString()!!
dateOfExpiry = parcel.readString()!!
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
parcel.writeInt(abstractProductId)
parcel.writeInt(amount)
parcel.writeString(dateOfProduction)
parcel.writeString(dateOfExpiry)
}
override fun describeContents(): Int {
@ -36,5 +47,4 @@ class Product() : Parcelable {
return arrayOfNulls(size)
}
}
}

View File

@ -1,17 +1,71 @@
package org.foxarmy.barcodescannerforemployees.fragments
import android.os.Bundle
import android.provider.BaseColumns
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.gridlayout.widget.GridLayout
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.ProductContract
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import org.foxarmy.barcodescannerforemployees.views.ProductView
class ShelfFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_shelf, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateContent()
}
override fun onResume() {
super.onResume()
updateContent()
}
fun updateContent() {
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
grv?.removeAllViews()
val db = DBStorageController(requireContext()).readableDatabase
val projection = arrayOf(
BaseColumns._ID,
ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID,
ProductContract.ProductEntry.AMOUNT,
ProductContract.ProductEntry.DATE_OF_PRODUCTION,
ProductContract.ProductEntry.EXPIRY_DATE,
)
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, null, null, null, null)
with (cursor) {
while(moveToNext()) {
val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
val abstractProductId = getInt(getColumnIndexOrThrow(ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID))
val amount = getInt(getColumnIndexOrThrow(ProductContract.ProductEntry.AMOUNT))
val dateOfProduction = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.DATE_OF_PRODUCTION))
val dateOfExpiry = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.EXPIRY_DATE))
val product = Product(productId, abstractProductId, amount, dateOfProduction, dateOfExpiry)
val productView = ProductView(
requireActivity(),
requireContext(),
product
)
grv?.addView(productView)
}
}
}
}

View File

@ -0,0 +1,78 @@
package org.foxarmy.barcodescannerforemployees.views
import android.app.Activity
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import org.foxarmy.barcodescannerforemployees.getActivity
import org.foxarmy.barcodescannerforemployees.getImageUri
import java.io.File
class ProductView: LinearLayout {
var product: Product = Product()
var isProductSelected = false
private lateinit var activity: Activity
private lateinit var productImageView: ImageView
private lateinit var productNameTextView: TextView
private lateinit var productNetWeightTextView: TextView
private lateinit var productAmountTextView: TextView
private lateinit var productCategoryView: TextView
private lateinit var productLifeSpan: TextView
constructor(context: Context, a: AttributeSet) : super(context, a) {
activity = getActivity(context)!!
val inflater: LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.product_view, this)
}
constructor(activity: Activity, context: Context, product: Product) : super(context) {
this.product = product
this.activity = activity
val inflater: LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.product_view, this)
productImageView = findViewById(R.id.productPicture)
productNameTextView = findViewById(R.id.productNameView)
productNetWeightTextView = findViewById(R.id.productNetWeightView)
productAmountTextView = findViewById(R.id.amountView)
productCategoryView = findViewById(R.id.categoryView)
productLifeSpan = findViewById(R.id.dateSpan)
update()
}
fun update () {
val linkedAbstractProduct: AbstractProduct = DBStorageController(activity).findAbstractProductById(DBStorageController(activity).readableDatabase, product.abstractProductId)!!
val picturesDir = File(activity.filesDir, "pictures")
picturesDir.mkdirs()
val pictureFile = File(picturesDir, "${linkedAbstractProduct.imageHash}.png")
val imageUri = getImageUri(activity, pictureFile)
productImageView.setImageURI(imageUri)
productNameTextView.text = linkedAbstractProduct.name
productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString()
productAmountTextView.text = product.amount.toString()
productCategoryView.text = DBStorageController(activity).getCategoryNameById(DBStorageController(activity).readableDatabase, linkedAbstractProduct.category)
productLifeSpan.text = "${product.dateOfProduction}-${product.dateOfExpiry}"
this.background = ContextCompat.getDrawable(context, if (isProductSelected) R.drawable.outline_selected else R.drawable.outline)
findViewById<ConstraintLayout>(R.id.productLayout).setOnLongClickListener {
isProductSelected = !isProductSelected
this.background = ContextCompat.getDrawable(context, if (isProductSelected) R.drawable.outline_selected else R.drawable.outline)
true
}
}
}

View File

@ -32,24 +32,72 @@
<Button
android:text="Select"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/button2"
android:layout_height="wrap_content" android:id="@+id/selectDateOfProductionButton"
app:layout_constraintTop_toBottomOf="@+id/scanButton"
app:layout_constraintStart_toEndOf="@+id/dateOfProductionTextView"
app:layout_constraintEnd_toEndOf="parent"/>
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/button2"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/selectDateOfProductionButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp"
android:orientation="horizontal">
android:orientation="horizontal" android:id="@+id/radioGroup">
<RadioButton
android:text="Expiry date"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/ExpiryDateRadio"/>
android:layout_height="wrap_content" android:id="@+id/expiryDateRadio"/>
<RadioButton
android:text="Shell life"
android:text="Shelf life"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/shellLifeRadio"/>
android:layout_height="wrap_content" android:id="@+id/shelfLifeRadio"/>
</RadioGroup>
<TextView
android:text="Expiry date: "
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/expiryDateTextView"
app:layout_constraintTop_toBottomOf="@+id/radioGroup" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:visibility="invisible"/>
<Button
android:text="Select"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/selectExpiryDateButton"
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
app:layout_constraintStart_toEndOf="@+id/expiryDateTextView" android:layout_marginStart="16dp"
android:visibility="invisible"/>
<TextView
android:text="Shelf life:"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/shelfLife"
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
android:layout_marginTop="16dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp" android:visibility="invisible"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:ems="10"
android:id="@+id/shelfLifeTextEdit" app:layout_constraintTop_toBottomOf="@+id/radioGroup"
app:layout_constraintStart_toEndOf="@+id/expiryDateTextView" android:layout_marginStart="16dp"
android:visibility="invisible"/>
<TextView
android:text="Amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/amountText"
app:layout_constraintTop_toBottomOf="@+id/shelfLifeTextEdit"
app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="16dp"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="numberSigned"
android:ems="10"
android:id="@+id/amountTextEdit"
app:layout_constraintTop_toBottomOf="@+id/selectExpiryDateButton"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/amountText"/>
<Button
android:text="@string/saveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/saveProductButton"
app:layout_constraintTop_toBottomOf="@+id/amountTextEdit" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -1,6 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.gridlayout.widget.GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2"
app:rowCount="100" android:nestedScrollingEnabled="true">
</androidx.gridlayout.widget.GridLayout>
</ScrollView>
</FrameLayout>

View File

@ -1,7 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical"
android:layout_width="200dp"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/productLayout" android:outlineProvider="bounds"
android:background="#00FFFEFE" android:clickable="true">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp" app:srcCompat="@android:drawable/ic_menu_gallery"
android:id="@+id/productPicture"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
android:text="@string/sample_product_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/productNameView"
app:layout_constraintTop_toBottomOf="@+id/productPicture"
android:layout_marginTop="15dp" app:layout_constraintStart_toStartOf="parent"
android:fontFamily="monospace" android:textSize="20sp"
android:maxLines="2" android:layout_marginStart="10dp"/>
<TextView
android:text="@string/sample_product_net_weight"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/productNetWeightView" android:layout_weight="1"
app:layout_constraintTop_toBottomOf="@+id/productNameView"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="25dp"
android:layout_marginTop="10dp" android:textSize="12sp" android:fontFamily="monospace"
app:layout_constraintEnd_toStartOf="@+id/unitView" android:layout_marginEnd="2dp"
/>
<TextView
android:text="@string/sample_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/unitView"
app:layout_constraintStart_toEndOf="@+id/productNetWeightView"
app:layout_constraintTop_toBottomOf="@+id/productNameView" android:layout_marginTop="10dp"
android:fontFamily="monospace" android:textSize="12sp" android:layout_marginStart="8dp"/>
<TextView
android:text="10"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/amountView"
app:layout_constraintStart_toEndOf="@+id/unitView"
app:layout_constraintTop_toBottomOf="@+id/productNameView" android:layout_marginTop="10dp"
android:layout_marginStart="16dp"/>
<TextView
android:text="@string/sample_category"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/categoryView"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="25dp" android:fontFamily="monospace" android:textSize="10sp"
app:layout_constraintTop_toBottomOf="@+id/amountView" android:layout_marginTop="5dp"/>
<TextView
android:text="01.01.1970 - 01.01.1971"
android:layout_width="wrap_content"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/categoryView"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="8dp" app:layout_constraintEnd_toEndOf="parent" android:id="@+id/dateSpan"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>