18 Commits

Author SHA1 Message Date
6e497323c0 ability to add and link barcodeless products 2024-10-23 02:21:35 +03:00
6e32545b9d cleanup 2024-10-22 04:16:12 +03:00
62cc4db615 Huge load of shit, hope I'll be able to do it normally 2024-10-22 04:01:34 +03:00
60aa1973b1 fixed parser behaviour on null net weight 2024-10-20 17:14:32 +03:00
9b9946b280 cleanup 2024-10-20 17:08:03 +03:00
4f1845dd0e updated date input 2024-10-20 17:07:25 +03:00
79ab5c83c0 fixed persisted scan state on scan cancelling 2024-10-20 16:02:47 +03:00
e05f223075 translated some text 2024-10-20 12:48:57 +03:00
3a0d8cbf7f added things my wife asked 2024-10-20 00:54:31 +03:00
54693ff15d remade scaling, fixed updating 2024-10-19 23:50:01 +03:00
6ce23c2081 handling non-existend abstract products 2024-10-19 23:09:50 +03:00
8a018903f0 translated app to russian 2024-10-19 19:25:39 +03:00
cba0e8594f fixed negative percents of freshness 2024-10-19 18:03:52 +03:00
244ef95a99 UI improvements 2024-10-19 18:02:08 +03:00
05f95ef30d removed google play's barcodescanner dependency 2024-10-19 17:54:22 +03:00
385a19a6ee update gitignore 2024-10-19 13:04:49 +03:00
d89d32a793 remove binaries from repo 2024-10-19 13:04:27 +03:00
dfe6fa9c3e fixed ignoring manual barcode typing 2024-10-18 17:35:48 +03:00
46 changed files with 960 additions and 332 deletions

2
.gitignore vendored
View File

@@ -146,3 +146,5 @@ dist
.externalNativeBuild .externalNativeBuild
.cxx .cxx
local.properties local.properties
release

View File

@@ -32,7 +32,10 @@ android {
} }
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
buildConfig = true
} }
} }
dependencies { dependencies {
@@ -50,14 +53,14 @@ dependencies {
testImplementation(libs.junit) testImplementation(libs.junit)
implementation(libs.volley) implementation(libs.volley)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
implementation (libs.play.services.code.scanner) implementation(libs.zxing.android.embedded)
implementation("com.google.zxing:core:3.4.1")
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)
// implementation("com.google.android.material:1.2.0")
// Barcode scanning API // Barcode scanning API
implementation (libs.barcode.scanning) implementation (libs.barcode.scanning)
// CameraX library
// CameraX library
implementation (libs.androidx.camera.camera2) implementation (libs.androidx.camera.camera2)
implementation (libs.androidx.camera.lifecycle) implementation (libs.androidx.camera.lifecycle)

View File

@@ -36,6 +36,18 @@
android:name=".activities.AddAbstractProductActivity" android:name=".activities.AddAbstractProductActivity"
android:exported="false" android:exported="false"
android:theme="@style/Theme.BarcodeScannerForEmployees"/> android:theme="@style/Theme.BarcodeScannerForEmployees"/>
<activity
android:name=".activities.ExpiryCalendarActivity"
android:exported="false"
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
<activity
android:name=".activities.ExpiryCalendarGroupActivity"
android:exported="false"
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
<activity
android:name=".activities.FindBarcodelessAbstractProduct"
android:exported="false"
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
<activity <activity
android:name=".activities.FullscreenActivity" android:name=".activities.FullscreenActivity"
android:configChanges="orientation|keyboardHidden|screenSize" android:configChanges="orientation|keyboardHidden|screenSize"
@@ -43,9 +55,13 @@
android:label="@string/title_activity_fullscreen" android:label="@string/title_activity_fullscreen"
android:theme="@style/Theme.BarcodeScannerForEmployees.Fullscreen"/> android:theme="@style/Theme.BarcodeScannerForEmployees.Fullscreen"/>
<activity android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="fullSensor"
tools:replace="android:screenOrientation"/>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="com.google.firebase.components.activities.MainActivity.provider;com.google.firebase.components.activities.FullscreenActivity.provider;com.google.firebase.components.activities.AddAbstractProductActivity.provider;com.google.firebase.components.activities.AddProductActivity.provider" android:authorities="com.google.firebase.components.activities.MainActivity.provider;com.google.firebase.components.activities.FullscreenActivity.provider;com.google.firebase.components.activities.AddAbstractProductActivity.provider;com.google.firebase.components.activities.AddProductActivity.provider;com.google.firebase.components.activities.ExpiryCalendarActivity.provider;com.google.firebase.components.activities.FindBarcodelessAbstractProduct.provider;com.google.firebase.components.activities.ExpiryCalendarGroupActivity.provider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data <meta-data
@@ -53,11 +69,6 @@
android:resource="@xml/file_path"/> android:resource="@xml/file_path"/>
</provider> </provider>
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="barcode_ui">
</meta-data>
<activity <activity
android:name=".activities.MainActivity" android:name=".activities.MainActivity"
android:exported="true" android:exported="true"

View File

@@ -200,6 +200,49 @@ class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE
db.insert(ProductContract.ProductEntry.TABLE_NAME, null, values) db.insert(ProductContract.ProductEntry.TABLE_NAME, null, values)
} }
fun findAmountOfProductsWithExpiryDate(db: SQLiteDatabase, date: Long): Int {
var amount = 0
val projection = arrayOf(
ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID,
ProductContract.ProductEntry.AMOUNT,
ProductContract.ProductEntry.DATE_OF_PRODUCTION,
)
val selection = "${ProductContract.ProductEntry.EXPIRY_DATE} = ?"
val selectionArgs = arrayOf(date.toString())
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)
with(cursor) {
while (moveToNext()) {
amount++
}
}
return amount
}
fun findAllExpiryDates(db:SQLiteDatabase): Set<Long> {
val dates: MutableSet<Long> = mutableSetOf<Long>()
val projection = arrayOf(
ProductContract.ProductEntry.EXPIRY_DATE
)
val orderBy = "${ProductContract.ProductEntry.EXPIRY_DATE} ASC"
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, null, null, null, orderBy)
with(cursor) {
while (moveToNext()) {
dates.add(getLong(getColumnIndexOrThrow(ProductContract.ProductEntry.EXPIRY_DATE)))
}
}
return dates
}
fun findAbstractProductByBarcode (db: SQLiteDatabase, barcode: String) : AbstractProduct? { fun findAbstractProductByBarcode (db: SQLiteDatabase, barcode: String) : AbstractProduct? {
var abstractProduct: AbstractProduct? = null var abstractProduct: AbstractProduct? = null
val projection = arrayOf( val projection = arrayOf(

View File

@@ -1,6 +1,5 @@
package org.foxarmy.barcodescannerforemployees package org.foxarmy.barcodescannerforemployees
import android.util.Log
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
class Parser constructor() { class Parser constructor() {
@@ -14,11 +13,9 @@ class Parser constructor() {
val found = foundByRegex.groupValues[0] val found = foundByRegex.groupValues[0]
text = text.replace(found, "") text = text.replace(found, "")
netWeight = stripNetWeight(found) netWeight = stripNetWeight(found)
// when (found.lowercase().strip()) {
// "кг" -> netWeight *= 1000
// "мл" -> netWeight /= 1000
// }
return Triple(text, netWeight, found) return Triple(text, netWeight, found)
} else {
return Triple(text, 0.0, "")
} }
} }
return Triple("", 0.0, "") return Triple("", 0.0, "")
@@ -28,11 +25,11 @@ class Parser constructor() {
val (name, netWeight, unit) = findNetWeightInText( val (name, netWeight, unit) = findNetWeightInText(
text, text,
listOf( listOf(
Regex("[0-9]+,?[0-9]*\\s*[лЛ]"), Regex("\\d*[.,]?\\d+\\s*[лЛ]"),
Regex("[0-9]+,?[0-9]*\\s*((мл)|(МЛ)|(Мл))"), Regex("[0-9]+((,?)|(.?))+[0-9]*\\s*((мл)|(МЛ)|(Мл))"),
Regex("[0-9]+,?[0-9]*\\s*((кг)|(Кг))"), Regex("[0-9]+((,?)|(.?))+[0-9]*\\s*((кг)|(Кг))"),
Regex("[0-9]+,?[0-9]*\\s*[гГ]"), Regex("[0-9]+((,?)|(.?))+[0-9]*\\s*[гГ]"),
Regex("[0-9]+,?[0-9*]\\s*((шт)|(Шт))") Regex("\\d+\\s*(шт)|(Шт)")
) )
) )
var unitNumber = -1 var unitNumber = -1
@@ -44,9 +41,10 @@ class Parser constructor() {
"мл" -> { 3 } "мл" -> { 3 }
"шт" -> { 4 } "шт" -> { 4 }
else -> { -1 } else -> {
4
}
} }
Log.d("QWERTYUIOP", "Unit: ${strippedUnit}, number: ${unitNumber}")
return AbstractProduct(0, "", name, netWeight, "", 0, unitNumber) return AbstractProduct(0, "", name, netWeight, "", 0, unitNumber)
} }

View File

@@ -5,11 +5,11 @@ import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.graphics.scale
import com.google.firebase.components.BuildConfig import com.google.firebase.components.BuildConfig
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@@ -33,8 +33,12 @@ fun generateThumbnailForImage(context: Context, imageHash: String) {
val imageFile = File(picturesDir, "$imageHash.png") val imageFile = File(picturesDir, "$imageHash.png")
val imageContent = imageFile.inputStream().readBytes() val imageContent = imageFile.inputStream().readBytes()
var img = BitmapFactory.decodeByteArray(imageContent, 0, imageContent.size) var img = BitmapFactory.decodeByteArray(imageContent, 0, imageContent.size)
img = img.scale(img.width/4,img.height/4)
img.compress(Bitmap.CompressFormat.WEBP_LOSSY, 25, FileOutputStream(thumbnailFile)) val matrix = Matrix();
matrix.postRotate(90f)
val scaled = Bitmap.createScaledBitmap(img, img.width/4, img.height/4, true)
val rotated = Bitmap.createBitmap(scaled, 0, 0, scaled.width, scaled.height, matrix, true)
rotated.compress(Bitmap.CompressFormat.WEBP_LOSSY, 25, FileOutputStream(thumbnailFile))
} }
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)

View File

@@ -1,6 +1,5 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.Manifest
import android.content.ContentValues import android.content.ContentValues
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
@@ -14,9 +13,10 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.google.mlkit.vision.barcode.common.Barcode import androidx.core.widget.addTextChangedListener
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions import com.journeyapps.barcodescanner.ScanContract
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions
import org.foxarmy.barcodescannerforemployees.* import org.foxarmy.barcodescannerforemployees.*
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import java.io.File import java.io.File
@@ -32,6 +32,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
private lateinit var scanButton: Button private lateinit var scanButton: Button
private lateinit var barcodeText: EditText private lateinit var barcodeText: EditText
private lateinit var noBarcodeCheckBox: CheckBox
private lateinit var productNameText: TextView private lateinit var productNameText: TextView
private lateinit var netWeightText: TextView private lateinit var netWeightText: TextView
private lateinit var unitTypeSpinner: Spinner private lateinit var unitTypeSpinner: Spinner
@@ -42,19 +43,15 @@ class AddAbstractProductActivity : AppCompatActivity() {
private lateinit var pictureFile: File private lateinit var pictureFile: File
private lateinit var picturesPath: File private lateinit var picturesPath: File
private var barcode: String = "" private var barcode: String = ""
private var action: String = "new"
private var scanningBarcode = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_add_abstract_product) setContentView(R.layout.fragment_add_abstract_product)
val extras = intent.extras
abstractProduct = extras!!.get("abstractProduct") as AbstractProduct?
if (abstractProduct != null) {
barcode = abstractProduct!!.barcode
}
picturesPath = File(filesDir, "pictures") picturesPath = File(filesDir, "pictures")
val thumbnailsDir = File(cacheDir, "thumbnails") val thumbnailsDir = File(cacheDir, "thumbnails")
@@ -67,6 +64,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
scanButton = findViewById(R.id.scan_button) scanButton = findViewById(R.id.scan_button)
barcodeText = findViewById(R.id.barcodeTextEdit) barcodeText = findViewById(R.id.barcodeTextEdit)
noBarcodeCheckBox = findViewById(R.id.noBarcodeCheckBox)
productNameText = findViewById(R.id.productName) productNameText = findViewById(R.id.productName)
netWeightText = findViewById(R.id.netWeight) netWeightText = findViewById(R.id.netWeight)
unitTypeSpinner = findViewById(R.id.unitTypeSpinner) unitTypeSpinner = findViewById(R.id.unitTypeSpinner)
@@ -76,15 +74,33 @@ class AddAbstractProductActivity : AppCompatActivity() {
fillupCategorySpinner() fillupCategorySpinner()
fillupUnitsSpinner() fillupUnitsSpinner()
if (abstractProduct?.name == "" && abstractProduct?.barcode != "") { noBarcodeCheckBox.setOnClickListener {
performRequest(abstractProduct?.barcode!!) if (noBarcodeCheckBox.isChecked) {
barcodeText.setText("")
}
}
barcodeText.addTextChangedListener {
this.barcode = barcodeText.text.toString()
}
val extras = intent.extras
action = extras!!.get("action") as String
when (action) {
"update" -> {
abstractProduct = extras.get("abstractProduct") as AbstractProduct?
}
"new_from_barcode" -> {
abstractProduct = extras.get("abstractProduct") as AbstractProduct?
barcode = abstractProduct!!.barcode
performRequest(abstractProduct!!.barcode)
}
} }
if (abstractProduct != null) { if (abstractProduct != null) {
val imageThumbnailUri = getImageUri(this, File(thumbnailsDir, "${abstractProduct!!.imageHash}.webp")) val imageThumbnailUri = getImageUri(this, File(thumbnailsDir, "${abstractProduct!!.imageHash}.webp"))
pictureFile = File(picturesPath, "${abstractProduct!!.imageHash}.png]") pictureFile = File(picturesPath, "${abstractProduct!!.imageHash}.png]")
imageView.setImageURI(imageThumbnailUri) imageView.setImageURI(imageThumbnailUri)
imageView.rotation = 90f
barcodeText.setText(abstractProduct!!.barcode) barcodeText.setText(abstractProduct!!.barcode)
productNameText.text = abstractProduct!!.name productNameText.text = abstractProduct!!.name
netWeightText.text = abstractProduct!!.netWeight.toString() netWeightText.text = abstractProduct!!.netWeight.toString()
@@ -95,22 +111,22 @@ class AddAbstractProductActivity : AppCompatActivity() {
saveButton.setOnClickListener { saveButton.setOnClickListener {
val productName = productNameText.text.toString() val productName = productNameText.text.toString()
val netWeight = netWeightText.text val netWeight = netWeightText.text
if (abstractProduct == null && (!this::pictureFile.isInitialized || !pictureFile.exists())) { if (action != "update" && (!this::pictureFile.isInitialized || !pictureFile.exists())) {
Toast.makeText(this, "Please, make a picture of a product!", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.product_picture_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
if (barcode == "") { if (barcode == "" && !noBarcodeCheckBox.isChecked) {
Toast.makeText(this, "Please, scan barcode on a product", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.product_barcode_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
if (productName == "") { if (productName == "") {
Toast.makeText(this, "Please, write a name of a product!", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.product_name_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
if (netWeight.toString() == "" || netWeight.toString().toDoubleOrNull() == null) { if (netWeight.toString() == "" || netWeight.toString().toDoubleOrNull() == null) {
Toast.makeText(this, "Please, write a valid net weight of a product!", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.product_net_weight_request), Toast.LENGTH_SHORT).show()
} }
val db = DBStorageController(this).writableDatabase val db = DBStorageController(this).writableDatabase
@@ -123,39 +139,27 @@ class AddAbstractProductActivity : AppCompatActivity() {
put(AbstractProductContract.AbstractProductEntry.UNIT, unitTypeSpinner.selectedItemPosition) put(AbstractProductContract.AbstractProductEntry.UNIT, unitTypeSpinner.selectedItemPosition)
} }
if (abstractProduct == null) { if (action == "update") {
db.update(
AbstractProductContract.AbstractProductEntry.TABLE_NAME,
values,
"${BaseColumns._ID} = ?",
arrayOf(abstractProduct!!.id.toString())
)
} else if (action == "new" || action == "new_from_barcode"){
db.insert(AbstractProductContract.AbstractProductEntry.TABLE_NAME, null, values) db.insert(AbstractProductContract.AbstractProductEntry.TABLE_NAME, null, values)
} else {
db.update(AbstractProductContract.AbstractProductEntry.TABLE_NAME, values, "${BaseColumns._ID} = ?", arrayOf(abstractProduct!!.id.toString()))
} }
finish() finish()
} }
takePictureButton.setOnClickListener { takePictureButton.setOnClickListener {
requestPermissionLauncher.launch(Manifest.permission.CAMERA) requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
} }
scanButton.setOnClickListener { scanButton.setOnClickListener {
val options = GmsBarcodeScannerOptions.Builder() scanningBarcode = true
.setBarcodeFormats( requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
Barcode.FORMAT_EAN_13
)
.build()
val scanner = GmsBarcodeScanning.getClient(this)
scanner.startScan()
.addOnSuccessListener { barcode ->
this.barcode = barcode.rawValue.toString()
performRequest(this.barcode)
}
.addOnFailureListener { e ->
Toast.makeText(
this,
"Failed to scan barcode. Please, try again or enter data manually",
Toast.LENGTH_LONG
).show()
}
} }
} }
@@ -165,16 +169,23 @@ class AddAbstractProductActivity : AppCompatActivity() {
requester.request(this, barcode) requester.request(this, barcode)
var abstractProduct: AbstractProduct var abstractProduct: AbstractProduct
if (DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, this.barcode) != null) { if (DBStorageController(this).findAbstractProductByBarcode(
DBStorageController(this).readableDatabase,
this.barcode
) != null
) {
AlertDialog.Builder(this) AlertDialog.Builder(this)
.setMessage("You've got an abstract product with such barcode in your database") .setMessage(getString(R.string.abstract_product_already_exists))
.setPositiveButton("Quit") { _: DialogInterface, _: Int -> .setPositiveButton(getString(R.string.quit)) { _: DialogInterface, _: Int ->
finish() finish()
} }
.setNegativeButton("Edit existing") { _: DialogInterface, _: Int -> .setNegativeButton(getString(R.string.edit_existing)) { _: DialogInterface, _: Int ->
val addProductIntent = Intent(this, AddAbstractProductActivity::class.java) val addProductIntent = Intent(this, AddAbstractProductActivity::class.java)
val extras = Bundle() val extras = Bundle()
val existingAbstractProduct = DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, this.barcode) val existingAbstractProduct = DBStorageController(this).findAbstractProductByBarcode(
DBStorageController(this).readableDatabase,
this.barcode
)
extras.putParcelable("abstractProduct", existingAbstractProduct) extras.putParcelable("abstractProduct", existingAbstractProduct)
addProductIntent.putExtras(extras) addProductIntent.putExtras(extras)
ContextCompat.startActivity(this, addProductIntent, extras) ContextCompat.startActivity(this, addProductIntent, extras)
@@ -184,10 +195,12 @@ class AddAbstractProductActivity : AppCompatActivity() {
thread { thread {
// Я сам в ахуях какой это костыль, пока хз как фиксить, потом придумаю :)) // Я сам в ахуях какой это костыль, пока хз как фиксить, потом придумаю :))
while (requester.response == "") { } while (requester.response == "") {
}
if (requester.response == "Not found 404") { if (requester.response == "Not found 404") {
runOnUiThread { runOnUiThread {
Toast.makeText(this, "Product not found. Please, try again or type manually", Toast.LENGTH_LONG).show() Toast.makeText(this, getString(R.string.no_product_in_online_database), Toast.LENGTH_LONG)
.show()
} }
return@thread return@thread
} }
@@ -212,7 +225,8 @@ class AddAbstractProductActivity : AppCompatActivity() {
) )
val arrayAdapter = ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, units) val arrayAdapter =
ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, units)
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item) arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
unitTypeSpinner.adapter = arrayAdapter unitTypeSpinner.adapter = arrayAdapter
} }
@@ -226,15 +240,24 @@ class AddAbstractProductActivity : AppCompatActivity() {
CategoriesContract.CategoryEntry.CATEGORY_NAME CategoriesContract.CategoryEntry.CATEGORY_NAME
) )
val cursor = db.query(CategoriesContract.CategoryEntry.TABLE_NAME, projection, null, null, null, null, BaseColumns._ID+" ASC") val cursor = db.query(
CategoriesContract.CategoryEntry.TABLE_NAME,
projection,
null,
null,
null,
null,
BaseColumns._ID + " ASC"
)
with (cursor) { with(cursor) {
while (moveToNext()) { while (moveToNext()) {
categories.add(getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME))) categories.add(getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME)))
} }
} }
val arrayAdapter = ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, categories) val arrayAdapter =
ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, categories)
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item) arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
categorySpinner.adapter = arrayAdapter categorySpinner.adapter = arrayAdapter
} }
@@ -259,7 +282,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
} }
@RequiresApi(Build.VERSION_CODES.R) @RequiresApi(Build.VERSION_CODES.R)
fun getPicture () { fun getPicture() {
//Saving picture to a temp file for further hash calculation and moving to a proper directory //Saving picture to a temp file for further hash calculation and moving to a proper directory
val imageFile = File(this.filesDir, "image.png") val imageFile = File(this.filesDir, "image.png")
val imageUri = getImageUri(this, imageFile) val imageUri = getImageUri(this, imageFile)
@@ -274,9 +297,38 @@ class AddAbstractProductActivity : AppCompatActivity() {
ActivityResultContracts.RequestPermission() ActivityResultContracts.RequestPermission()
) { isGranted: Boolean -> ) { isGranted: Boolean ->
if (isGranted) { if (isGranted) {
getPicture() if (scanningBarcode) {
prepareBarcodeScanner()
} else {
getPicture()
}
} else { } else {
Toast.makeText(this, "I need permission in order to take a picture", Toast.LENGTH_LONG).show() Toast.makeText(this, getString(R.string.camera_permission_for_picture_request), Toast.LENGTH_LONG).show()
} }
} }
private val scanLauncher = registerForActivityResult(ScanContract()) { result: ScanIntentResult ->
run {
if (result.contents == null) {
Toast.makeText(this, getString(R.string.cancelled), Toast.LENGTH_SHORT).show()
} else {
val scannedBarcode = result.contents
barcodeText.setText(scannedBarcode)
performRequest(scannedBarcode)
}
scanningBarcode = false
}
}
private fun prepareBarcodeScanner() {
val options = ScanOptions()
options.setDesiredBarcodeFormats(ScanOptions.EAN_13)
options.setPrompt(getString(R.string.scan_barcode_of_a_product))
options.setCameraId(0)
options.setBeepEnabled(false)
options.setBarcodeImageEnabled(true)
options.setOrientationLocked(false)
scanLauncher.launch(options)
}
} }

View File

@@ -6,10 +6,11 @@ import android.os.Bundle
import android.provider.BaseColumns import android.provider.BaseColumns
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.Toast
import org.foxarmy.barcodescannerforemployees.CategoriesContract import org.foxarmy.barcodescannerforemployees.CategoriesContract
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.DBStorageController import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
class AddCategoryActivity : Activity() { class AddCategoryActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -27,6 +28,11 @@ class AddCategoryActivity : Activity() {
findViewById<Button>(R.id.saveButton).setOnClickListener { findViewById<Button>(R.id.saveButton).setOnClickListener {
val db = DBStorageController(this).writableDatabase val db = DBStorageController(this).writableDatabase
if (categoryNameTextEdit.text.toString() == "") {
Toast.makeText(this, getString(R.string.category_name_required), Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (category.id == 0) { // Inserting new category if (category.id == 0) { // Inserting new category
val values = ContentValues().apply { val values = ContentValues().apply {
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, categoryNameTextEdit.text.toString()) put(CategoriesContract.CategoryEntry.CATEGORY_NAME, categoryNameTextEdit.text.toString())

View File

@@ -1,12 +1,20 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.app.DatePickerDialog import android.app.Activity
import android.content.Intent
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.* import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions
import org.foxarmy.barcodescannerforemployees.DBStorageController import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
@@ -18,19 +26,18 @@ import java.util.*
class AddProductActivity : AppCompatActivity() { class AddProductActivity : AppCompatActivity() {
private lateinit var scanButton: Button private lateinit var scanButton: Button
private lateinit var noBarcodeButton: Button
private lateinit var abstractProductView: AbstractProductView private lateinit var abstractProductView: AbstractProductView
private lateinit var expiryDateRadioButton: RadioButton private lateinit var expiryDateRadioButton: RadioButton
private lateinit var shelfLifeRadioButton: RadioButton private lateinit var shelfLifeRadioButton: RadioButton
private lateinit var expiryDateTextView: TextView private lateinit var expiryDatePicker: DatePicker
private lateinit var expiryDateSelectButton: Button
private lateinit var shelfLifeTextView: TextView
private lateinit var shelfLifeTextEdit: EditText private lateinit var shelfLifeTextEdit: EditText
private lateinit var amountTextEdit: EditText private lateinit var amountTextEdit: EditText
private lateinit var dateOfProductionSelectButton: Button private lateinit var dateOfProductionDatePicker: DatePicker
private lateinit var saveProductButton: Button private lateinit var saveProductButton: Button
private var expiryDateOverShelfLife: Boolean? = null private var expiryDateOverShelfLife: Boolean? = null
@@ -44,19 +51,19 @@ class AddProductActivity : AppCompatActivity() {
setContentView(R.layout.fragment_add_product) setContentView(R.layout.fragment_add_product)
scanButton = findViewById(R.id.scanButton) scanButton = findViewById(R.id.scanButton)
noBarcodeButton = findViewById(R.id.noBarcodeButton)
abstractProductView = findViewById(R.id.abstractProductView) abstractProductView = findViewById(R.id.abstractProductView)
expiryDateRadioButton = findViewById(R.id.expiryDateRadio) expiryDateRadioButton = findViewById(R.id.expiryDateRadio)
shelfLifeRadioButton = findViewById(R.id.shelfLifeRadio) shelfLifeRadioButton = findViewById(R.id.shelfLifeRadio)
expiryDateTextView = findViewById(R.id.expiryDateTextView) expiryDatePicker = findViewById(R.id.expiryDatePicker)
expiryDateSelectButton = findViewById(R.id.selectExpiryDateButton)
shelfLifeTextView = findViewById(R.id.shelfLife)
shelfLifeTextEdit = findViewById(R.id.shelfLifeTextEdit) shelfLifeTextEdit = findViewById(R.id.shelfLifeTextEdit)
amountTextEdit = findViewById(R.id.amountTextEdit) amountTextEdit = findViewById(R.id.amountTextEdit)
amountTextEdit.setText("1")
dateOfProductionSelectButton = findViewById(R.id.selectDateOfProductionButton) dateOfProductionDatePicker = findViewById(R.id.dateOfProductionDatePicker)
saveProductButton = findViewById(R.id.saveProductButton) saveProductButton = findViewById(R.id.saveProductButton)
val extras = intent.extras val extras = intent.extras
@@ -73,21 +80,14 @@ class AddProductActivity : AppCompatActivity() {
update() update()
} else { } else {
product = Product(0, 0, 0, 0, 0) product = Product(0, 0, 0, 0, 0)
abstractProduct = AbstractProduct(0, "", "", 0.0, "", 0, 0)
} }
scanButton.setOnClickListener { scanButton.setOnClickListener {
val scanner = GmsBarcodeScanning.getClient(this) requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
scanner.startScan() }
.addOnSuccessListener { bc ->
abstractProduct = DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, bc.rawValue.toString())
product!!.abstractProductId = abstractProduct!!.id
displayAbstractProduct(abstractProduct!!) noBarcodeButton.setOnClickListener {
} intentLauncher.launch(Intent(this, FindBarcodelessAbstractProduct::class.java))
.addOnFailureListener {
Toast.makeText(this, "Cannot scan barcode", Toast.LENGTH_SHORT).show()
}
} }
expiryDateRadioButton.setOnClickListener { expiryDateRadioButton.setOnClickListener {
@@ -100,32 +100,14 @@ class AddProductActivity : AppCompatActivity() {
update() update()
} }
dateOfProductionSelectButton.setOnClickListener { dateOfProductionDatePicker.setOnDateChangedListener { _, year, monthOfYear, dayOfMonth ->
val c = Calendar.getInstance() product!!.dateOfProduction = SimpleDateFormat("dd.MM.yyyy").parse("$dayOfMonth.$monthOfYear.$year")!!.time / 1000
val year = c.get(Calendar.YEAR) update()
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
val dpd = DatePickerDialog(this, { _, y, m, d ->
product!!.dateOfProduction = SimpleDateFormat("dd.MM.yyyy").parse("$d.${m+1}.$y")!!.time / 1000
update()
}, year, month, day)
dpd.show()
} }
expiryDateSelectButton.setOnClickListener { expiryDatePicker.setOnDateChangedListener { _, year, monthOfYear, dayOfMonth ->
val c = Calendar.getInstance() product!!.dateOfExpiry = SimpleDateFormat("dd.MM.yyyy").parse("$dayOfMonth.$monthOfYear.$year")!!.time / 1000
val year = c.get(Calendar.YEAR) update()
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
val dpd = DatePickerDialog(this, { _, y, m, d ->
product!!.dateOfExpiry = SimpleDateFormat("dd.MM.yyyy").parse("$d.${m+1}.$y")!!.time / 1000
update()
}, year, month, day)
dpd.show()
} }
amountTextEdit.addTextChangedListener { amountTextEdit.addTextChangedListener {
@@ -135,12 +117,17 @@ class AddProductActivity : AppCompatActivity() {
} }
saveProductButton.setOnClickListener { saveProductButton.setOnClickListener {
if (abstractProduct == null) {
Toast.makeText(this, getString(R.string.abstract_product_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (expiryDateOverShelfLife == null) { if (expiryDateOverShelfLife == null) {
Toast.makeText(this, "Please, choose and fill in shelf life or expiry date.", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.shell_life_or_expiry_date_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
if (product!!.dateOfProduction == 0.toLong()) { if (product!!.dateOfProduction == 0.toLong()) {
Toast.makeText(this, "Please, choose date of production.", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.date_of_production_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
@@ -150,13 +137,13 @@ class AddProductActivity : AppCompatActivity() {
} }
} else { } else {
if (product!!.dateOfExpiry == 0.toLong()) { if (product!!.dateOfExpiry == 0.toLong()) {
Toast.makeText(this, "Please, choose date of expiry.", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.expiry_date_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
} }
if (product!!.amount == 0) { if (product!!.amount == 0) {
Toast.makeText(this, "Please, specify an amount of product.", Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.product_amount_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
@@ -168,27 +155,35 @@ class AddProductActivity : AppCompatActivity() {
finish() finish()
} }
update()
val today = SimpleDateFormat("dd.MM.yyyy").format(Calendar.getInstance().time).split(".")
dateOfProductionDatePicker.updateDate(today[2].toInt(), today[1].toInt(), today[0].toInt())
expiryDatePicker.updateDate(today[2].toInt(), today[1].toInt(), today[0].toInt())
} }
private val intentLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val selectedAbstractProduct = (result.data?.extras!!.getParcelable("abstractProduct") as AbstractProduct?)!!
product!!.abstractProductId = selectedAbstractProduct.id
abstractProduct = selectedAbstractProduct
displayAbstractProduct(selectedAbstractProduct, "")
}
}
private fun update () { private fun update () {
if (expiryDateOverShelfLife == true) { if (expiryDateOverShelfLife == true) {
expiryDateTextView.visibility = View.VISIBLE expiryDatePicker.visibility = View.VISIBLE
expiryDateSelectButton.visibility = View.VISIBLE
shelfLifeTextEdit.visibility = View.INVISIBLE shelfLifeTextEdit.visibility = View.INVISIBLE
shelfLifeTextView.visibility = View.INVISIBLE
} else if (expiryDateOverShelfLife == false){ } else if (expiryDateOverShelfLife == false){
expiryDateTextView.visibility = View.INVISIBLE expiryDatePicker.visibility = View.INVISIBLE
expiryDateSelectButton.visibility = View.INVISIBLE
shelfLifeTextEdit.visibility = View.VISIBLE shelfLifeTextEdit.visibility = View.VISIBLE
shelfLifeTextView.visibility = View.VISIBLE
} }
val dateOfProductionParsed = SimpleDateFormat("dd.MM.yyyy").format(Date(product!!.dateOfProduction * 1000))
findViewById<TextView>(R.id.dateOfProductionTextView).text = "Date of production: $dateOfProductionParsed"
val expiryDateParsed = SimpleDateFormat("dd.MM.yyyy").format(Date(product!!.dateOfExpiry * 1000))
expiryDateTextView.text = "Expiry date: $expiryDateParsed"
if (amountTextEdit.text.toString() == "") { if (amountTextEdit.text.toString() == "") {
amountTextEdit.setText(product!!.amount.toString()) amountTextEdit.setText(product!!.amount.toString())
@@ -211,8 +206,64 @@ class AddProductActivity : AppCompatActivity() {
product!!.dateOfExpiry = c.timeInMillis / 1000 product!!.dateOfExpiry = c.timeInMillis / 1000
} }
private fun displayAbstractProduct(abstractProduct: AbstractProduct) { private fun displayAbstractProduct(abstractProduct: AbstractProduct?, scannedBarcode: String) {
abstractProductView.abstractProduct = abstractProduct if (abstractProduct == null) {
abstractProductView.update() AlertDialog.Builder(this)
.setMessage(getString(R.string.abstract_product_does_not_exist))
.setPositiveButton(R.string.yes) { _, _ ->
val addAbstractProductIntent = Intent(this, AddAbstractProductActivity::class.java)
val extras = Bundle()
extras.putParcelable("abstractProduct", AbstractProduct(0, scannedBarcode, "", 0.0, "", 0, 0))
extras.putString("action", "new_from_barcode")
addAbstractProductIntent.putExtras(extras)
ContextCompat.startActivity(this, addAbstractProductIntent, extras)
}
.setNegativeButton(R.string.no) {_, _ ->
}.show()
} else {
abstractProductView.abstractProduct = abstractProduct
abstractProductView.update()
}
} }
}
@RequiresApi(Build.VERSION_CODES.R)
val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
prepareBarcodeScanner()
} else {
Toast.makeText(this, getString(R.string.camera_permission_for_scanning_request), Toast.LENGTH_LONG).show()
}
}
private val scanLauncher = registerForActivityResult(ScanContract()) { result: ScanIntentResult ->
run {
if (result.contents == null) {
Toast.makeText(this, getString(R.string.cancelled), Toast.LENGTH_SHORT).show()
} else {
val scannedBarcode = result.contents
abstractProduct = DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, scannedBarcode)
displayAbstractProduct(abstractProduct, scannedBarcode)
if (abstractProduct != null) {
product?.abstractProductId = abstractProduct!!.id
}
}
}
}
private fun prepareBarcodeScanner() {
val options = ScanOptions()
options.setDesiredBarcodeFormats(ScanOptions.EAN_13)
options.setPrompt(getString(R.string.scan_barcode_of_a_product))
options.setCameraId(0)
options.setBeepEnabled(false)
options.setBarcodeImageEnabled(true)
options.setOrientationLocked(false)
scanLauncher.launch(options)
}
}

View File

@@ -0,0 +1,43 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityExpiryCalendarBinding
import org.foxarmy.barcodescannerforemployees.views.ExpiryGroupView
class ExpiryCalendarActivity : AppCompatActivity() {
private lateinit var binding: ActivityExpiryCalendarBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_expiry_dates)
fillUp()
}
fun displayDate(date:Long) {
val expiryCalendarGroupActivityIntent = Intent(this, ExpiryCalendarGroupActivity::class.java)
val extras = Bundle()
extras.putLong("date", date)
expiryCalendarGroupActivityIntent.putExtras(extras)
ContextCompat.startActivity(this, expiryCalendarGroupActivityIntent, extras)
}
private fun fillUp() {
val dates = DBStorageController(this).findAllExpiryDates(DBStorageController(this).readableDatabase)
val container = findViewById<LinearLayout>(R.id.datesLinearLayout)
dates.forEach { date ->
val newDate = ExpiryGroupView(this, this as Context, date)
container.addView(newDate)
}
}
}

View File

@@ -0,0 +1,25 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityExpiryCalendarGroupBinding
import org.foxarmy.barcodescannerforemployees.fragments.ShelfFragment
class ExpiryCalendarGroupActivity : AppCompatActivity() {
private lateinit var binding: ActivityExpiryCalendarGroupBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityExpiryCalendarGroupBinding.inflate(layoutInflater)
val date = intent.extras!!.getLong("date")
val ft = supportFragmentManager.beginTransaction()
ft.replace(R.id.content, ShelfFragment.newInstance(date))
ft.commit()
setContentView(binding.root)
}
}

View File

@@ -0,0 +1,34 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityFindBarcodelessAbstractProductBinding
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.fragments.StorageFragment
class FindBarcodelessAbstractProduct() : AppCompatActivity() {
private lateinit var binding: ActivityFindBarcodelessAbstractProductBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFindBarcodelessAbstractProductBinding.inflate(layoutInflater)
val ft = supportFragmentManager.beginTransaction()
val fragment = StorageFragment.newInstance("barcodeless", arrayOf(""))
ft.replace(R.id.content, fragment)
ft.commit()
setContentView(binding.root)
}
fun selected(abstractProduct: AbstractProduct) {
val data = Intent()
data.putExtra("abstractProduct", abstractProduct)
setResult(Activity.RESULT_OK, data)
finish()
}
}

View File

@@ -32,6 +32,13 @@ class MainActivity : AppCompatActivity() {
setupViewPager(binding.tabViewpager) setupViewPager(binding.tabViewpager)
binding.tabTablayout.setupWithViewPager(binding.tabViewpager) binding.tabTablayout.setupWithViewPager(binding.tabViewpager)
binding.expiryCalendarFab.setOnClickListener { _ ->
val expiryCalendarIntent = Intent(this, ExpiryCalendarActivity::class.java)
val extras = Bundle()
expiryCalendarIntent.putExtras(extras)
ContextCompat.startActivity(this, expiryCalendarIntent, extras)
}
binding.newElementFab.setOnClickListener { view -> binding.newElementFab.setOnClickListener { view ->
val currentPosition = binding.tabTablayout.selectedTabPosition val currentPosition = binding.tabTablayout.selectedTabPosition
val fragment = adapter.getItem(currentPosition) val fragment = adapter.getItem(currentPosition)
@@ -43,6 +50,7 @@ class MainActivity : AppCompatActivity() {
// I reuse the same stuff for editing and adding new product. // I reuse the same stuff for editing and adding new product.
// if abstractProduct == null, it means that we need to create new object // if abstractProduct == null, it means that we need to create new object
extras.putParcelable("abstractProduct", null) extras.putParcelable("abstractProduct", null)
extras.putString("action", "new")
addAbstractProductIntent.putExtras(extras) addAbstractProductIntent.putExtras(extras)
ContextCompat.startActivity(this, addAbstractProductIntent, extras) ContextCompat.startActivity(this, addAbstractProductIntent, extras)
} }
@@ -50,7 +58,7 @@ class MainActivity : AppCompatActivity() {
"CategoriesFragment" -> { "CategoriesFragment" -> {
val addCategoryIntent = Intent(this, AddCategoryActivity::class.java) val addCategoryIntent = Intent(this, AddCategoryActivity::class.java)
val extras = Bundle() val extras = Bundle()
extras.putParcelable("category", Category(0, "New category")) extras.putParcelable("category", Category(0, ""))
addCategoryIntent.putExtras(extras) addCategoryIntent.putExtras(extras)
ContextCompat.startActivity(this, addCategoryIntent, extras) ContextCompat.startActivity(this, addCategoryIntent, extras)
} }
@@ -69,13 +77,14 @@ class MainActivity : AppCompatActivity() {
private fun setupViewPager(viewpager: ViewPager) { private fun setupViewPager(viewpager: ViewPager) {
adapter = ViewPagerAdapter(supportFragmentManager) adapter = ViewPagerAdapter(supportFragmentManager)
adapter.addFragment(CategoriesFragment(), "Categories") adapter.addFragment(StorageFragment.newInstance("", arrayOf("")), getString(R.string.storage_title))
adapter.addFragment(StorageFragment(), "Storage") adapter.addFragment(ShelfFragment(), getString(R.string.shelf_title))
adapter.addFragment(ShelfFragment(), "Shelf") adapter.addFragment(CategoriesFragment(), getString(R.string.categories_title))
//TODO: settings fragments //TODO: settings fragments
// setting adapter to view pager. // setting adapter to view pager.
viewpager.setAdapter(adapter) viewpager.adapter = adapter
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -99,24 +108,24 @@ class MainActivity : AppCompatActivity() {
when (fragment::class.simpleName.toString()) { when (fragment::class.simpleName.toString()) {
"StorageFragment" -> { "StorageFragment" -> {
AlertDialog.Builder(this) AlertDialog.Builder(this)
.setMessage("Deleting an abstract product will also delete ALL the products, that belong to it. Do you want to proceed?") .setMessage(getString(R.string.deleting_abstract_product_warning))
.setPositiveButton("Yes") { _: DialogInterface, _: Int -> .setPositiveButton(getString(R.string.yes)) { _: DialogInterface, _: Int ->
val storageFragment = fragment as StorageFragment val storageFragment = fragment as StorageFragment
storageFragment.removeSelected() storageFragment.removeSelected()
} }
.setNegativeButton("No") { _: DialogInterface, _: Int -> .setNegativeButton(getString(R.string.no)) { _: DialogInterface, _: Int ->
}.show() }.show()
} }
"CategoriesFragment" -> { "CategoriesFragment" -> {
AlertDialog.Builder(this) AlertDialog.Builder(this)
.setMessage("Deleting a category will also delete ALL the products, that belong to that category. Do you want to proceed?") .setMessage(getString(R.string.deleting_category_warning))
.setPositiveButton("Yes") { _: DialogInterface, _: Int -> .setPositiveButton(getString(R.string.yes)) { _: DialogInterface, _: Int ->
val categoriesFragment = fragment as CategoriesFragment val categoriesFragment = fragment as CategoriesFragment
categoriesFragment.removeSelected() categoriesFragment.removeSelected()
} }
.setNegativeButton("No") { _: DialogInterface, _: Int -> .setNegativeButton(getString(R.string.no)) { _: DialogInterface, _: Int ->
}.show() }.show()
} }
@@ -151,4 +160,14 @@ class MainActivity : AppCompatActivity() {
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
} }
fun filterAbstractProductsByCategory(id: Int) {
binding.tabViewpager.setCurrentItem(0, true)
val currentPosition = binding.tabTablayout.selectedTabPosition
val fragment = adapter.getItem(currentPosition)
val storageFragment = fragment as StorageFragment
storageFragment.filterByCategory(id)
}
} }

View File

@@ -5,7 +5,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.FragmentAddAbstractProductBinding import org.foxarmy.barcodescannerforemployees.databinding.FragmentAddAbstractProductBinding
class AddAbstractProductFragment : Fragment() { class AddAbstractProductFragment : Fragment() {
@@ -16,6 +15,6 @@ class AddAbstractProductFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
binding = FragmentAddAbstractProductBinding.inflate(layoutInflater) binding = FragmentAddAbstractProductBinding.inflate(layoutInflater)
return inflater.inflate(R.layout.fragment_add_abstract_product, container, false) return binding.root
} }
} }

View File

@@ -5,7 +5,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.FragmentAddProductBinding import org.foxarmy.barcodescannerforemployees.databinding.FragmentAddProductBinding
class AddProductFragment : Fragment() { class AddProductFragment : Fragment() {
@@ -16,6 +15,6 @@ class AddProductFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
binding = FragmentAddProductBinding.inflate(layoutInflater) binding = FragmentAddProductBinding.inflate(layoutInflater)
return inflater.inflate(R.layout.fragment_add_product, container, false) return binding.root
} }
} }

View File

@@ -45,7 +45,7 @@ class CategoriesFragment : Fragment() {
} }
if (!deleted) { if (!deleted) {
Toast.makeText(requireContext(), "Nothing to delete", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), getString(R.string.nothing_to_delete), Toast.LENGTH_SHORT).show()
} }
updateContent() updateContent()
} }

View File

@@ -0,0 +1,21 @@
package org.foxarmy.barcodescannerforemployees.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import org.foxarmy.barcodescannerforemployees.databinding.FragmentExpiryDatesBinding
class ExpiryDatesFragment : Fragment() {
private lateinit var binding: FragmentExpiryDatesBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentExpiryDatesBinding.inflate(layoutInflater)
return binding.root
}
}

View File

@@ -1,5 +1,6 @@
package org.foxarmy.barcodescannerforemployees.fragments package org.foxarmy.barcodescannerforemployees.fragments
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.provider.BaseColumns import android.provider.BaseColumns
@@ -28,6 +29,8 @@ class ShelfFragment : Fragment() {
private lateinit var binding: FragmentShelfBinding private lateinit var binding: FragmentShelfBinding
private var updateInProgress = false private var updateInProgress = false
private var filterBy = ""
private var filter = ""
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@@ -37,6 +40,12 @@ class ShelfFragment : Fragment() {
fillUpSortBySpinner() fillUpSortBySpinner()
val filterByDate = arguments?.getLong("filterByExpiryDate") as Long?
if (filterByDate != null) {
filter = filterByDate.toString()
filterBy = "expiryDate"
}
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) { override fun onNothingSelected(parent: AdapterView<*>?) {
updateContent() updateContent()
@@ -69,13 +78,13 @@ class ShelfFragment : Fragment() {
} }
if (!updated) { if (!updated) {
Toast.makeText(requireContext(), "Nothing to update", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), getString(R.string.nothing_to_update), Toast.LENGTH_SHORT).show()
} }
updateContent() updateContent()
} }
private fun fillUpSortBySpinner() { private fun fillUpSortBySpinner() {
val sorts = mutableListOf("Name", "Category", "Freshness", "Date of production", "Date of expiry") val sorts = resources.getStringArray(R.array.product_sort_types)
val arrayAdapter = val arrayAdapter =
ArrayAdapter(requireContext(), androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, sorts) ArrayAdapter(requireContext(), androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, sorts)
@@ -109,13 +118,13 @@ class ShelfFragment : Fragment() {
var orderBy: String = "" var orderBy: String = ""
when (binding.spinner.selectedItem) { when (binding.spinner.selectedItemPosition) {
"Name" -> { 0 -> {
orderBy = orderBy =
"(SELECT ${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID} = ${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID}) ASC" "(SELECT ${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID} = ${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID}) ASC"
} }
"Category" -> { 1 -> {
orderBy = orderBy =
"(SELECT ${AbstractProductContract.AbstractProductEntry.CATEGORY} FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID} = ${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID}) ASC" "(SELECT ${AbstractProductContract.AbstractProductEntry.CATEGORY} FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID} = ${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID}) ASC"
} }
@@ -126,16 +135,29 @@ class ShelfFragment : Fragment() {
// "(SELECT ( (julianday(${ProductContract.ProductEntry.EXPIRY_DATE}) - julianday('now', 'localtime')) / (julianday(${ProductContract.ProductEntry.EXPIRY_DATE}) - julianday(${ProductContract.ProductEntry.DATE_OF_PRODUCTION})) )) ASC" // "(SELECT ( (julianday(${ProductContract.ProductEntry.EXPIRY_DATE}) - julianday('now', 'localtime')) / (julianday(${ProductContract.ProductEntry.EXPIRY_DATE}) - julianday(${ProductContract.ProductEntry.DATE_OF_PRODUCTION})) )) ASC"
// } // }
"Date of production" -> { 3 -> {
orderBy = "${ProductContract.ProductEntry.DATE_OF_PRODUCTION} ASC" orderBy = "${ProductContract.ProductEntry.DATE_OF_PRODUCTION} ASC"
} }
"Date of expiry" -> { 4 -> {
orderBy = "${ProductContract.ProductEntry.EXPIRY_DATE} ASC" orderBy = "${ProductContract.ProductEntry.EXPIRY_DATE} ASC"
} }
} }
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, null, null, null, orderBy) var selection: String? = null
var selectionArgs: Array<String>? = null
when (filterBy) {
"expiryDate" -> {
selection = "${ProductContract.ProductEntry.EXPIRY_DATE} = ?"
selectionArgs = arrayOf(filter)
}
"" -> {}
}
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy)
val products = mutableListOf<Product>() val products = mutableListOf<Product>()
@@ -151,7 +173,7 @@ class ShelfFragment : Fragment() {
val product = Product(productId, abstractProductId, amount, dateOfProduction, dateOfExpiry) val product = Product(productId, abstractProductId, amount, dateOfProduction, dateOfExpiry)
if (binding.spinner.selectedItem == "Freshness") { if (binding.spinner.selectedItemPosition == 2) { //freshness
products.add(product) products.add(product)
} else { } else {
val productView = ProductView( val productView = ProductView(
@@ -200,10 +222,22 @@ class ShelfFragment : Fragment() {
if (!deleted) { if (!deleted) {
activity!!.runOnUiThread { activity!!.runOnUiThread {
Toast.makeText(requireContext(), "Nothing to delete", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), getString(R.string.nothing_to_delete), Toast.LENGTH_SHORT).show()
} }
} }
updateContent() updateContent()
} }
} }
companion object {
fun newInstance(date: Long):ShelfFragment = ShelfFragment().apply {
val fragment = ShelfFragment()
val args = Bundle()
args.putLong("filterByExpiryDate", date)
fragment.setArguments(args)
return fragment
}
}
} }

View File

@@ -3,14 +3,10 @@ package org.foxarmy.barcodescannerforemployees.fragments
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.provider.BaseColumns import android.provider.BaseColumns
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.*
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@@ -18,6 +14,7 @@ import org.foxarmy.barcodescannerforemployees.AbstractProductContract
import org.foxarmy.barcodescannerforemployees.DBStorageController import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.AddAbstractProductActivity import org.foxarmy.barcodescannerforemployees.activities.AddAbstractProductActivity
import org.foxarmy.barcodescannerforemployees.activities.FindBarcodelessAbstractProduct
import org.foxarmy.barcodescannerforemployees.databinding.FragmentStorageBinding import org.foxarmy.barcodescannerforemployees.databinding.FragmentStorageBinding
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.generateThumbnailForImage import org.foxarmy.barcodescannerforemployees.generateThumbnailForImage
@@ -27,7 +24,8 @@ import kotlin.concurrent.thread
class StorageFragment : Fragment() { class StorageFragment : Fragment() {
private lateinit var binding: FragmentStorageBinding private lateinit var binding: FragmentStorageBinding
private var filterBy = ""
private lateinit var filter: Array<String>
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
@@ -46,6 +44,14 @@ class StorageFragment : Fragment() {
} }
} }
binding.dropFiltersButton.setOnClickListener {
filterBy = ""
updateContent()
}
filterBy = arguments?.getString("filterBy")!!
filter = arguments?.getStringArray("filter")!!
return binding.root return binding.root
} }
@@ -56,8 +62,7 @@ class StorageFragment : Fragment() {
} }
private fun fillUpSortBySpinner() { private fun fillUpSortBySpinner() {
val sorts = mutableListOf("Name", "Category") val sorts = resources.getStringArray(R.array.abstract_product_sort_types)
val arrayAdapter = ArrayAdapter(requireContext(), androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, sorts) val arrayAdapter = ArrayAdapter(requireContext(), androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, sorts)
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item) arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
binding.spinner.adapter = arrayAdapter binding.spinner.adapter = arrayAdapter
@@ -81,7 +86,7 @@ class StorageFragment : Fragment() {
if (!deleted) { if (!deleted) {
activity!!.runOnUiThread { activity!!.runOnUiThread {
Toast.makeText(requireContext(), "Nothing to delete", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), getString(R.string.nothing_to_delete), Toast.LENGTH_SHORT).show()
} }
} }
updateContent() updateContent()
@@ -96,6 +101,7 @@ class StorageFragment : Fragment() {
val addProductIntent = Intent(requireContext(), AddAbstractProductActivity::class.java) val addProductIntent = Intent(requireContext(), AddAbstractProductActivity::class.java)
val extras = Bundle() val extras = Bundle()
extras.putParcelable("abstractProduct", view.abstractProduct) extras.putParcelable("abstractProduct", view.abstractProduct)
extras.putString("action", "update")
addProductIntent.putExtras(extras) addProductIntent.putExtras(extras)
ContextCompat.startActivity(requireContext(), addProductIntent, extras) ContextCompat.startActivity(requireContext(), addProductIntent, extras)
} }
@@ -121,17 +127,31 @@ class StorageFragment : Fragment() {
) )
var orderBy: String = "" var orderBy: String = ""
Log.d("QWERTYUIOP", binding.spinner.selectedItem.toString()) when(binding.spinner.selectedItemPosition) {
when(binding.spinner.selectedItem) { 0 -> {
"Name" -> {
orderBy = "${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} ASC" orderBy = "${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} ASC"
} }
"Category" -> { 1 -> {
orderBy = "${AbstractProductContract.AbstractProductEntry.CATEGORY} ASC" orderBy = "${AbstractProductContract.AbstractProductEntry.CATEGORY} ASC"
} }
} }
val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, null, null, null, null, orderBy) var selection = ""
var selectionArgs: Array<String>? = null
when (filterBy) {
"category" -> {
selection = "${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?"
selectionArgs = filter
}
"barcodeless" -> {
selection = "${AbstractProductContract.AbstractProductEntry.BARCODE} = '' "
selectionArgs = null
}
}
val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy)
with (cursor) { with (cursor) {
while(moveToNext()) { while(moveToNext()) {
@@ -153,6 +173,16 @@ class StorageFragment : Fragment() {
product product
) )
if (filterBy == "barcodeless") {
abstractProduct.setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProduct.abstractProduct)
}
abstractProduct.findViewById<TextView>(R.id.productNameView).setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProduct.abstractProduct)
}
}
activity!!.runOnUiThread{ activity!!.runOnUiThread{
grv.addView(abstractProduct) grv.addView(abstractProduct)
} }
@@ -166,4 +196,22 @@ class StorageFragment : Fragment() {
updateContent() updateContent()
} }
fun filterByCategory(id: Int) {
filterBy = "category"
filter = arrayOf("$id")
updateContent()
}
companion object {
fun newInstance(filterBy: String, filter: Array<String>):StorageFragment = StorageFragment().apply {
val fragment = StorageFragment()
val args = Bundle()
args.putString("filterBy", filterBy)
args.putStringArray("filter", filter)
fragment.setArguments(args)
return fragment
}
}
} }

View File

@@ -9,6 +9,7 @@ import android.view.LayoutInflater
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
@@ -71,6 +72,10 @@ class AbstractProductView: LinearLayout {
this.background = ContextCompat.getDrawable(context, if (isProductSelected) R.drawable.outline_selected else R.drawable.outline) this.background = ContextCompat.getDrawable(context, if (isProductSelected) R.drawable.outline_selected else R.drawable.outline)
true true
} }
productNameField!!.setOnClickListener {
Toast.makeText(activity, productNameField!!.text, Toast.LENGTH_SHORT).show()
}
} }
fun update() { fun update() {
@@ -80,7 +85,7 @@ class AbstractProductView: LinearLayout {
thumbnailsDir.mkdirs() thumbnailsDir.mkdirs()
val imageUri = getImageUri(activity, File(thumbnailsDir, "${abstractProduct.imageHash}.webp")) val imageUri = getImageUri(activity, File(thumbnailsDir, "${abstractProduct.imageHash}.webp"))
productPicture!!.setImageURI(imageUri) productPicture!!.setImageURI(imageUri)
productPicture!!.rotation = 90f // productPicture!!.rotation = 90f
productNameField!!.text = abstractProduct.name productNameField!!.text = abstractProduct.name
netWeightField!!.text = abstractProduct.netWeight.toString() netWeightField!!.text = abstractProduct.netWeight.toString()

View File

@@ -6,9 +6,10 @@ import android.view.LayoutInflater
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.DBStorageController import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
class CategoryView : LinearLayout { class CategoryView : LinearLayout {
var category: Category var category: Category
@@ -35,5 +36,8 @@ class CategoryView : LinearLayout {
this.background = ContextCompat.getDrawable(context, if (isCategorySelected) R.drawable.outline_selected else R.drawable.outline) this.background = ContextCompat.getDrawable(context, if (isCategorySelected) R.drawable.outline_selected else R.drawable.outline)
true true
} }
setOnClickListener {
(activity as MainActivity).filterAbstractProductsByCategory(category.id)
}
} }
} }

View File

@@ -0,0 +1,37 @@
package org.foxarmy.barcodescannerforemployees.views
import android.app.Activity
import android.content.Context
import android.view.LayoutInflater
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.ExpiryCalendarActivity
import java.text.SimpleDateFormat
class ExpiryGroupView : LinearLayout {
private var representingDate: Long? = null
constructor(activity: Activity, context: Context, representingDate: Long) : super(context) {
val inflater: LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.expiry_group_view, this)
this.representingDate = representingDate
this.background = ContextCompat.getDrawable(context,R.drawable.outline)
var amount = DBStorageController(context).findAmountOfProductsWithExpiryDate(DBStorageController(context).readableDatabase, representingDate)
findViewById<TextView>(R.id.representingDateTextView).text = SimpleDateFormat("dd.MM.yyyy").format(representingDate * 1000)
findViewById<TextView>(R.id.amountTextView).text = amount.toString()
setOnClickListener {
(activity as ExpiryCalendarActivity).displayDate(representingDate)
// (activity as MainActivity).filterAbstractProductsByCategory(category.id)
}
}
}

View File

@@ -88,14 +88,18 @@ class ProductView: LinearLayout {
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
fun update () { fun update () {
val linkedAbstractProduct: AbstractProduct = DBStorageController(activity).findAbstractProductById(DBStorageController(activity).readableDatabase, product.abstractProductId)!! val linkedAbstractProduct: AbstractProduct? = DBStorageController(activity).findAbstractProductById(DBStorageController(activity).readableDatabase, product.abstractProductId)
if(linkedAbstractProduct == null) {
return
}
val thumbnailsDir = File(activity.cacheDir, "thumbnails") val thumbnailsDir = File(activity.cacheDir, "thumbnails")
thumbnailsDir.mkdirs() thumbnailsDir.mkdirs()
val pictureFile = File(thumbnailsDir, "${linkedAbstractProduct.imageHash}.webp") val pictureFile = File(thumbnailsDir, "${linkedAbstractProduct.imageHash}.webp")
val imageUri = getImageUri(activity, pictureFile) val imageUri = getImageUri(activity, pictureFile)
productImageView.setImageURI(imageUri) productImageView.setImageURI(imageUri)
productImageView.rotation = 90f // productImageView.rotation = 90f
productNameTextView.text = linkedAbstractProduct.name productNameTextView.text = linkedAbstractProduct.name
productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString() productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString()
@@ -103,8 +107,8 @@ class ProductView: LinearLayout {
productCategoryView.text = DBStorageController(activity).getCategoryNameById(DBStorageController(activity).readableDatabase, linkedAbstractProduct.category) productCategoryView.text = DBStorageController(activity).getCategoryNameById(DBStorageController(activity).readableDatabase, linkedAbstractProduct.category)
productLifeSpan.text = "${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfProduction*1000))}-${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfExpiry*1000))}" productLifeSpan.text = "${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfProduction*1000))}-${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfExpiry*1000))}"
productFreshnessTextView.text = productFreshnessTextView.text =
if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY) { if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY || product.freshness < 0) {
"Expired" context.getString(R.string.expired)
} else { } else {
"${DecimalFormat("#.#").format(product.freshness*100)}%" "${DecimalFormat("#.#").format(product.freshness*100)}%"
} }

View File

@@ -7,7 +7,7 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/productLayout" android:outlineProvider="bounds" android:layout_height="match_parent" android:id="@+id/productLayout" android:outlineProvider="bounds"
android:background="#00FFFEFE" android:clickable="true"> android:background="#00FFFEFE" android:clickable="true">
<ImageView <ImageView
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -6,5 +6,5 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".fragments.AddAbstractProductFragment"> tools:context=".fragments.AddAbstractProductFragment">
<include layout="@layout/content_add_abstract_product" android:id="@+id/include_content"/> <include layout="@layout/fragment_add_abstract_product" android:id="@+id/include_content"/>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View File

@@ -4,19 +4,18 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main" android:id="@+id/main"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
tools:context=".activities.AddCategoryActivity"> tools:context=".activities.AddCategoryActivity" android:layout_gravity="center">
<EditText <EditText
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text" android:inputType="text"
android:text="@string/sample_category"
android:ems="10" android:ems="10"
android:id="@+id/newCategoryName" app:layout_constraintStart_toStartOf="parent" android:id="@+id/newCategoryName" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="20dp"/> android:layout_marginTop="20dp" android:hint="@string/sample_category"/>
<Button <Button
android:text="@string/saveButton" android:text="@string/saveButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".fragments.AddProductFragment"> tools:context=".fragments.AddProductFragment">
<include layout="@layout/content_add_product" android:id="@+id/include_content"/> <include layout="@layout/fragment_add_product" android:id="@+id/include_content"/>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.ExpiryDatesFragment">
<include layout="@layout/fragment_expiry_dates" android:id="@+id/include_content"/>
</androidx.core.widget.NestedScrollView>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.ShelfFragment"
android:id="@+id/content">
</androidx.core.widget.NestedScrollView>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.ShelfFragment"
android:id="@+id/content">
</androidx.core.widget.NestedScrollView>

View File

@@ -50,5 +50,12 @@
android:layout_marginEnd="@dimen/fab_margin" android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_input_add"/> app:srcCompat="@android:drawable/ic_input_add"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true" app:srcCompat="@android:drawable/ic_menu_my_calendar"
android:id="@+id/expiryCalendarFab" android:layout_gravity="bottom|end"
tools:layout_editor_absoluteY="742dp" tools:layout_editor_absoluteX="337dp"
android:layout_marginBottom="84dp" android:layout_marginRight="16dp"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" android:id="@+id/addAbstractProductLayout">
<androidx.fragment.app.FragmentContainerView
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" app:navGraph="@navigation/nav_graph_add_abstract_product"
app:defaultNavHost="true" android:id="@+id/fragmentContainerView"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" android:id="@+id/addProductLayout">
<androidx.fragment.app.FragmentContainerView
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" app:navGraph="@navigation/nav_graph_add_product"
app:defaultNavHost="true" android:id="@+id/fragmentContainerView"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/representingDateTextView" android:layout_weight="1"
android:textSize="45sp"/>
<TextView
android:text="TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/amountTextView" android:layout_weight="1"
android:textSize="45sp" android:textAlignment="textEnd"/>
</LinearLayout>

View File

@@ -9,7 +9,7 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="16dp"> android:padding="16dp" android:layout_gravity="center">
<Button <Button
android:id="@+id/scan_button" android:id="@+id/scan_button"
android:layout_width="100dp" android:layout_width="100dp"
@@ -17,27 +17,27 @@
android:text="@string/scan_label" android:text="@string/scan_label"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/categoryTextView" app:layout_constraintTop_toBottomOf="@+id/categoryTextView"
android:layout_marginTop="15dp"/> android:layout_marginTop="40dp" android:layout_marginStart="5dp"/>
<ImageView <ImageView
android:src="@android:drawable/ic_menu_camera" android:src="@android:drawable/ic_menu_camera"
android:layout_width="356dp" android:layout_width="0dp"
android:layout_height="303dp" android:id="@+id/imageView" android:layout_height="303dp" android:id="@+id/imageView"
android:layout_marginBottom="25dp" android:layout_marginBottom="25dp"
app:layout_constraintBottom_toTopOf="@+id/productName" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toTopOf="@+id/productName" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="15dp"/> android:layout_marginTop="15dp" android:layout_marginStart="5dp" android:layout_marginEnd="5dp"/>
<EditText <EditText
android:layout_width="350dp" android:layout_width="0dp"
android:layout_height="50dp" android:layout_height="50dp"
android:inputType="text" android:inputType="text"
android:ems="10" android:ems="10"
android:id="@+id/productName" android:id="@+id/productName"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:layout_marginStart="5dp" android:layout_marginEnd="5dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/netWeight" app:layout_constraintBottom_toTopOf="@+id/netWeight"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
android:visibility="visible" android:hint="@string/product_name_label" android:textColorHint="#737373" android:visibility="visible" android:hint="@string/productName" android:textColorHint="#737373"
android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/barcodeTextEdit"/> android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/barcodeTextEdit"/>
<EditText <EditText
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -46,7 +46,7 @@
android:ems="10" android:ems="10"
android:id="@+id/netWeight" android:id="@+id/netWeight"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp" android:layout_marginStart="5dp"
android:visibility="visible" android:hint="@string/netWeight" android:textColorHint="#737373" android:visibility="visible" android:hint="@string/netWeight" android:textColorHint="#737373"
app:layout_constraintTop_toBottomOf="@+id/productName" app:layout_constraintTop_toBottomOf="@+id/productName"
/> />
@@ -56,40 +56,48 @@
app:layout_constraintStart_toEndOf="@+id/netWeight" app:layout_constraintStart_toEndOf="@+id/netWeight"
app:layout_constraintTop_toBottomOf="@+id/productName" app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintTop_toBottomOf="@+id/productName" app:layout_constraintEnd_toEndOf="parent"/>
<EditText <EditText
android:layout_width="350dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text" android:inputType="text"
android:ems="10" android:ems="10"
android:id="@+id/barcodeTextEdit" app:layout_constraintTop_toBottomOf="@+id/imageView" android:id="@+id/barcodeTextEdit" app:layout_constraintTop_toBottomOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="8dp" android:hint="Barcode" android:textColorHint="#737373"/> android:layout_marginTop="8dp" android:hint="Barcode" android:textColorHint="#737373"
android:layout_marginStart="5dp"/>
<CheckBox
android:text="@string/no_barcode"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/noBarcodeCheckBox"
app:layout_constraintStart_toEndOf="@+id/barcodeTextEdit"
app:layout_constraintTop_toBottomOf="@+id/imageView" android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView <TextView
android:text="@string/category" android:text="@string/category"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/categoryTextView" android:layout_height="wrap_content" android:id="@+id/categoryTextView"
app:layout_constraintTop_toBottomOf="@+id/netWeight" app:layout_constraintTop_toBottomOf="@+id/netWeight"
android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="30dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"/> android:layout_marginStart="8dp"/>
<Spinner <Spinner
android:layout_width="match_parent" android:layout_width="140dp"
android:layout_height="wrap_content" android:id="@+id/categorySpinner" android:layout_height="50dp" android:id="@+id/categorySpinner"
app:layout_constraintStart_toEndOf="@+id/categoryTextView" app:layout_constraintStart_toEndOf="@+id/categoryTextView"
app:layout_constraintTop_toBottomOf="@+id/netWeight" android:layout_marginStart="8dp" app:layout_constraintTop_toBottomOf="@+id/netWeight" android:layout_marginStart="8dp"
android:layout_marginTop="18dp"/> android:layout_marginTop="18dp" android:outlineProvider="bounds"/>
<Button <Button
android:text="@string/saveButton" android:text="@string/saveButton"
android:layout_width="100dp" android:layout_width="100dp"
android:layout_height="50dp" android:id="@+id/saveButton" android:layout_height="50dp" android:id="@+id/saveButton"
app:layout_constraintTop_toBottomOf="@+id/categoryTextView" app:layout_constraintTop_toBottomOf="@+id/categoryTextView"
android:layout_marginTop="15dp" app:layout_constraintEnd_toEndOf="parent"/> android:layout_marginTop="40dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="5dp"/>
<Button <Button
android:text="@string/takePicture" android:text="@string/takePicture"
android:layout_width="100dp" android:layout_width="100dp"
android:layout_height="55dp" android:id="@+id/takePictureButton" android:layout_height="55dp" android:id="@+id/takePictureButton"
app:layout_constraintTop_toBottomOf="@+id/categoryTextView" app:layout_constraintTop_toBottomOf="@+id/categoryTextView"
android:layout_marginTop="15dp" app:layout_constraintStart_toEndOf="@+id/scan_button" android:layout_marginTop="40dp"
android:layout_marginStart="33dp" app:layout_constraintEnd_toStartOf="@+id/saveButton" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/>
android:layout_marginEnd="6dp" app:layout_constraintHorizontal_bias="0.0"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View File

@@ -21,83 +21,77 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/scanButton" android:layout_weight="1" android:layout_height="wrap_content" android:id="@+id/scanButton" android:layout_weight="1"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/abstractProductView" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/abstractProductView" android:layout_marginTop="16dp"/> android:layout_marginStart="32dp"/>
<Button
android:text="@string/no_barcode_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/noBarcodeButton"
app:layout_constraintTop_toBottomOf="@+id/abstractProductView"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"/>
<TextView <TextView
android:text="Date of production:" android:text="@string/date_of_production"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/dateOfProductionTextView" android:layout_height="wrap_content" android:id="@+id/dateOfProductionTextView"
app:layout_constraintTop_toBottomOf="@+id/scanButton" app:layout_constraintTop_toBottomOf="@+id/scanButton"
android:layout_marginTop="16dp" app:layout_constraintStart_toStartOf="parent"/> android:layout_marginTop="48dp" app:layout_constraintStart_toStartOf="parent"
<Button
android:text="Select"
android:layout_width="wrap_content"
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"/> app:layout_constraintEnd_toEndOf="parent"/>
<RadioGroup <RadioGroup
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/selectDateOfProductionButton" android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp" app:layout_constraintEnd_toEndOf="parent"
android:orientation="horizontal" android:id="@+id/radioGroup"> android:orientation="horizontal" android:id="@+id/radioGroup"
app:layout_constraintTop_toBottomOf="@+id/dateOfProductionDatePicker" android:layout_marginTop="16dp">
<RadioButton <RadioButton
android:text="Expiry date" android:text="@string/expiry_date"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/expiryDateRadio"/> android:layout_height="match_parent" android:id="@+id/expiryDateRadio"/>
<RadioButton <RadioButton
android:text="Shelf life" android:text="@string/shelf_life"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/shelfLifeRadio"/> android:layout_height="wrap_content" android:id="@+id/shelfLifeRadio"/>
</RadioGroup> </RadioGroup>
<TextView <DatePicker
android:text="Expiry date: " android:layout_width="247dp"
android:layout_width="wrap_content" android:layout_height="100dp" android:id="@+id/expiryDatePicker"
android:layout_height="wrap_content" android:id="@+id/expiryDateTextView" android:datePickerMode="spinner"
app:layout_constraintTop_toBottomOf="@+id/radioGroup" app:layout_constraintStart_toStartOf="parent" android:calendarViewShown="false"
android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:visibility="invisible"/> app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
<Button app:layout_constraintHorizontal_bias="0.505" android:layout_marginTop="12dp"
android:text="Select" app:layout_constraintTop_toBottomOf="@+id/radioGroup" android:visibility="gone"/>
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 <EditText
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="numberDecimal" android:inputType="numberDecimal"
android:ems="10" android:ems="10"
android:id="@+id/shelfLifeTextEdit" app:layout_constraintTop_toBottomOf="@+id/radioGroup" android:id="@+id/shelfLifeTextEdit" app:layout_constraintTop_toBottomOf="@+id/radioGroup"
app:layout_constraintStart_toEndOf="@+id/expiryDateTextView" android:layout_marginStart="16dp" android:visibility="gone" app:layout_constraintEnd_toEndOf="parent"
android:visibility="invisible"/> app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="16dp"
<TextView android:hint="@string/shelf_life"/>
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 <EditText
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="numberSigned" android:inputType="numberSigned"
android:ems="10" android:ems="10"
android:id="@+id/amountTextEdit" android:id="@+id/amountTextEdit"
app:layout_constraintTop_toBottomOf="@+id/selectExpiryDateButton" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/amountText"/> android:hint="@string/amount" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/expiryDatePicker" android:layout_marginTop="12dp"/>
<Button <Button
android:text="@string/saveButton" android:text="@string/saveButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/saveProductButton" android:layout_height="wrap_content" android:id="@+id/saveProductButton"
app:layout_constraintTop_toBottomOf="@+id/amountTextEdit" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/amountTextEdit" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp"/> app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp"/>
<DatePicker
android:layout_width="247dp"
android:layout_height="100dp" android:id="@+id/dateOfProductionDatePicker"
android:datePickerMode="spinner"
android:calendarViewShown="false"
app:layout_constraintTop_toBottomOf="@+id/dateOfProductionTextView"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp" app:layout_constraintHorizontal_bias="0.505"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>

View File

@@ -8,6 +8,7 @@
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:id="@+id/categoriesLayout"> android:layout_height="match_parent" android:id="@+id/categoriesLayout"
android:layout_gravity="center" android:layout_margin="2dp">
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent" android:id="@+id/layout">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/scrollView">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/datesLinearLayout">
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.core.widget.NestedScrollView>

View File

@@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="#141218">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<TextView <TextView
android:text="Sort by"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="0dp" android:id="@+id/sortByTextView" android:layout_height="0dp" android:id="@+id/sortByTextView"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/scrollView" android:textAlignment="center" app:layout_constraintBottom_toTopOf="@+id/scrollView" android:textAlignment="center"
/> android:text="@string/sort_by"/>
<Spinner <Spinner
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="32dp" android:id="@+id/spinner" android:layout_height="32dp" android:id="@+id/spinner"

View File

@@ -10,7 +10,7 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<TextView <TextView
android:text="Sort by" android:text="@string/sort_by"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="0dp" android:id="@+id/sortByTextView" android:layout_height="0dp" android:id="@+id/sortByTextView"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@@ -22,12 +22,21 @@
android:layout_height="32dp" android:id="@+id/spinner" android:layout_height="32dp" android:id="@+id/spinner"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/sortByTextView" app:layout_constraintStart_toEndOf="@+id/sortByTextView"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="16dp" android:layout_marginStart="16dp"
app:layout_constraintEnd_toStartOf="@+id/dropFiltersButton" android:layout_marginEnd="10dp"
/> />
<Button
android:text="@string/drop_filters"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/dropFiltersButton"
app:layout_constraintStart_toEndOf="@+id/spinner"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="10dp"
android:layout_marginEnd="10dp" app:layout_constraintTop_toTopOf="parent"/>
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/spinner" android:layout_height="wrap_content"
android:id="@+id/scrollView2"> android:id="@+id/scrollView2"
app:layout_constraintTop_toBottomOf="@+id/dropFiltersButton">
<androidx.gridlayout.widget.GridLayout <androidx.gridlayout.widget.GridLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2" android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2"

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph_add_abstract_product"
app:startDestination="@id/addAbstractProductFragment">
<fragment android:id="@+id/addAbstractProductFragment"
android:name="org.foxarmy.barcodescannerforemployees.fragments.AddAbstractProductFragment"
android:label="activity_add_abstract_product" tools:layout="@layout/fragment_add_abstract_product"/>
</navigation>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph_add_product"
app:startDestination="@id/addProductFragment">
<fragment android:id="@+id/addProductFragment"
android:name="org.foxarmy.barcodescannerforemployees.fragments.AddProductFragment"
android:label="activity_add_product" tools:layout="@layout/fragment_add_product"/>
</navigation>

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">СканнерШтрихкодовДляРаботников</string>
<string name="action_settings">Настройки</string>
<string name="first_fragment_label">Добавить продукт</string>
<string name="second_fragment_label">Продукты</string>
<string name="scan_label">Сканировать</string>
<string name="netWeight">Вес нетто</string>
<string name="saveButton">Сохранить</string>
<string name="takePicture">Сфотографировать</string>
<string name="productName">Имя продукта</string>
<string name="productType">Тип продукта</string>
<string name="imageOfAProduct">Изображение продукта</string>
<string name="sample_product_name">Пример имени продукта</string>
<string name="sample_product_net_weight">100</string>
<string name="sample_unit">г</string>
<string name="sample_category">Пример категории</string>
<string name="title_activity_fullscreen">Полноэкранный режим</string>
<string name="fullscreen_image">Полноэкранное изображение</string>
<string name="delete_menu">Удалить предмет(ы)</string>
<string name="update_menu">Изменить предмет(ы)</string>
<string name="update">Обновить</string>
<string name="delete">Удалить</string>
<string name="category">Категория</string>
<string name="date_of_production">Дата производства</string>
<string name="expiry_date">Годен до</string>
<string name="select">Выбрать</string>
<string name="shelf_life">Срок годности</string>
<string name="amount">Количество</string>
<string name="sort_by">Сортировать по</string>
<string name="kilogram">кг</string>
<string name="gram">г</string>
<string name="liter">л</string>
<string name="milliliter">мл</string>
<string name="pieces">шт</string>
<string name="product_picture_request">Пожалуйста, сфотографируйте продукт!</string>
<string name="product_barcode_request">Пожалуйста, отсканируйте штрихкод на продукте!</string>
<string name="product_name_request">Пожалуйста, напишите название продукта</string>
<string name="product_net_weight_request">Пожалуйста, впишите верный вес нетто продукта</string>
<string name="camera_permission_for_picture_request">Требуется разрешение на доступ к камере для снятия
изображения
</string>
<string name="cancelled">Отменено</string>
<string name="scan_barcode_of_a_product">Простанируйте штрихкод продукта</string>
<string name="shell_life_or_expiry_date_request">Пожалуйста, выберите и заполните срок годности или годен до
</string>
<string name="date_of_production_request">Пожалуйста, выберите дату производства</string>
<string name="expiry_date_request">Пожалуйста, выберите годен до</string>
<string name="product_amount_request">Пожалуйста, укажите количество продукта</string>
<string name="camera_permission_for_scanning_request">Требуется разрешение на доступ к камере для сканирования
штрихкода
</string>
<string name="categories_title">Категории</string>
<string name="storage_title">Склад</string>
<string name="shelf_title">Полка</string>
<string name="deleting_abstract_product_warning">Удаление абстрактного продукта приведёт к удалению ВСЕХ продуктов
на полке, которые к нему принадлежат. Вы хотите продолжить?
</string>
<string name="deleting_category_warning">Удаление категории приведёт к удалению ВСЕХ продуктов, принадлежащих к ней.
Вы хотите продолжить?
</string>
<string name="yes">Да</string>
<string name="no">Нет</string>
<string name="nothing_to_delete">Нечего удалять</string>
<string name="nothing_to_update">Нечего обновлять</string>
<string name="expired">Просрочено</string>
<string name="barcode">Штрихкод</string>
<string-array name="product_sort_types">
<item>Имя</item>
<item>Категория</item>
<item>Свежесть</item>
<item>Дата производства</item>
<item>Годен до</item>
</string-array>
<string-array name="abstract_product_sort_types">
<item>Имя</item>
<item>Категория</item>
</string-array>
<string name="abstract_product_does_not_exist">Абстрактный продукт с таким штрихкодом не существует. Хотите его добавить?. </string>
<string name="drop_filters">Убрать фильтры</string>
<string name="category_name_required">Требуется название категории</string>
<string name="abstract_product_already_exists">Такой абстрактный продукт в ваше базе данных уже есть</string>
<string name="quit">Выйти</string>
<string name="edit_existing">Редактировать существующий</string>
<string name="no_product_in_online_database">Продукт не найден в онлайн базе данных. Попробуйте снова, если это
ошибка сканирования или введите данные вручную
</string>
<string name="abstract_product_request">Пожалуйста, отсканируйте штрихкод, чтобы добавить продукт</string>
<string name="no_barcode">No barcode present</string>
<string name="no_barcode_button">No barcode</string>
</resources>

View File

@@ -3,10 +3,7 @@
<string name="action_settings">Settings</string> <string name="action_settings">Settings</string>
<string name="first_fragment_label">Add new product</string> <string name="first_fragment_label">Add new product</string>
<string name="second_fragment_label">Products</string> <string name="second_fragment_label">Products</string>
<string name="scan_label">Scan</string> <string name="netWeight">Net weight</string>
<string name="previous">Previous</string>
<string name="product_name_label">Product name</string>
<string name="netWeight">Net weight (g)</string>
<string name="saveButton">Save</string> <string name="saveButton">Save</string>
<string name="takePicture">Take picture</string> <string name="takePicture">Take picture</string>
<string name="productName">Product name</string> <string name="productName">Product name</string>
@@ -17,16 +14,18 @@
<string name="sample_unit">g</string> <string name="sample_unit">g</string>
<string name="sample_category">Sample category</string> <string name="sample_category">Sample category</string>
<string name="title_activity_fullscreen">FullscreenActivity</string> <string name="title_activity_fullscreen">FullscreenActivity</string>
<string name="dummy_button">Dummy Button</string>
<string name="dummy_content">DUMMY\nCONTENT</string>
<string name="fullscreen_image">Fullscreen image</string> <string name="fullscreen_image">Fullscreen image</string>
<string name="next">Next</string>
<string name="delete_menu">Delete item(s)…</string> <string name="delete_menu">Delete item(s)…</string>
<string name="update_menu">Update item</string> <string name="update_menu">Update item</string>
<string name="update">update</string> <string name="update">update</string>
<string name="delete">delete</string> <string name="delete">delete</string>
<string name="category">Category</string> <string name="category">Category</string>
<string name="date_of_production">Date of production</string> <string name="date_of_production">Date of production</string>
<string name="expiry_date">Expiry date</string>
<string name="select">Select</string>
<string name="shelf_life">Shelf life</string>
<string name="amount">Amount</string>
<string name="sort_by">Sort by</string>
<!-- Unit names --> <!-- Unit names -->
<string name="kilogram">kg</string> <string name="kilogram">kg</string>
@@ -34,4 +33,58 @@
<string name="liter">l</string> <string name="liter">l</string>
<string name="milliliter">ml</string> <string name="milliliter">ml</string>
<string name="pieces">pc</string> <string name="pieces">pc</string>
<string name="product_picture_request">Please, make a picture of a product!</string>
<string name="product_barcode_request">Please, scan barcode on a product</string>
<string name="product_name_request">Please, write a name of a product!</string>
<string name="product_net_weight_request">"Please, write a valid net weight of a product!"</string>
<string name="camera_permission_for_picture_request">I need permission in order to take a picture</string>
<string name="cancelled">Cancelled</string>
<string name="scan_barcode_of_a_product">Scan barcode of a product</string>
<string name="shell_life_or_expiry_date_request">Please, choose and fill in shelf life or expiry date.</string>
<string name="date_of_production_request">Please, choose date of production.</string>
<string name="expiry_date_request">Please, choose expiry date.</string>
<string name="product_amount_request">Please, specify an amount of product.</string>
<string name="camera_permission_for_scanning_request">I need permission in order to scan a barcode</string>
<string name="categories_title">Categories</string>
<string name="storage_title">Storage</string>
<string name="shelf_title">Shelf</string>
<string name="deleting_abstract_product_warning">Deleting an abstract product will also delete ALL the products, that belong to it. Do you want to proceed?</string>
<string name="deleting_category_warning">Deleting a category will also delete ALL the products, that belong to that category. Do you want to proceed?</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="nothing_to_delete">Nothing to delete</string>
<string name="nothing_to_update">Nothing to update</string>
<string-array name="product_sort_types">
<item>Name</item>
<item>Category</item>
<item>Freshness</item>
<item>Date of production</item>
<item>Expiry date</item>
</string-array>
<string-array name="abstract_product_sort_types">
<item>Name</item>
<item>Category</item>
</string-array>
<string name="expired">Expired</string>
<string name="barcode">Barcode</string>
<string name="scan_label">Scan</string>
<string name="abstract_product_does_not_exist">Abstract product with such barcode does not exist. Do you want to add one?</string>
<string name="drop_filters">Drop filters</string>
<string name="category_name_required">Category name required</string>
<string name="abstract_product_already_exists">You\'ve got an abstract product with such barcode in your database.</string>
<string name="quit">Quit</string>
<string name="edit_existing">Edit existing</string>
<string name="no_product_in_online_database">Product not found. Please, try again if you beleive barcode scanned
wrongly or type manually
</string>
<string name="abstract_product_request">Please, scan a barcode in order to add product</string>
<string name="no_barcode">No barcode present</string>
<string name="no_barcode_button">No barcode</string>
</resources> </resources>

View File

@@ -19,6 +19,7 @@ legacySupportV4 = "1.0.0"
fragment = "1.8.4" fragment = "1.8.4"
playServicesCodeScanner = "16.1.0" playServicesCodeScanner = "16.1.0"
volley = "1.2.1" volley = "1.2.1"
zxingAndroidEmbedded = "4.3.0"
[libraries] [libraries]
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraView" } androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraView" }
@@ -41,6 +42,7 @@ androidx-legacy-support-v4 = { group = "androidx.legacy", name = "legacy-support
androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" } androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
play-services-code-scanner = { module = "com.google.android.gms:play-services-code-scanner", version.ref = "playServicesCodeScanner" } play-services-code-scanner = { module = "com.google.android.gms:play-services-code-scanner", version.ref = "playServicesCodeScanner" }
volley = { module = "com.android.volley:volley", version.ref = "volley" } volley = { module = "com.android.volley:volley", version.ref = "volley" }
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }