10 Commits

Author SHA1 Message Date
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
25 changed files with 459 additions and 177 deletions

2
.gitignore vendored
View File

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

View File

@@ -32,7 +32,10 @@ android {
}
buildFeatures {
viewBinding = true
buildConfig = true
}
}
dependencies {
@@ -50,14 +53,14 @@ dependencies {
testImplementation(libs.junit)
implementation(libs.volley)
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)
// implementation("com.google.android.material:1.2.0")
// Barcode scanning API
implementation (libs.barcode.scanning)
// CameraX library
// CameraX library
implementation (libs.androidx.camera.camera2)
implementation (libs.androidx.camera.lifecycle)

View File

@@ -43,6 +43,10 @@
android:label="@string/title_activity_fullscreen"
android:theme="@style/Theme.BarcodeScannerForEmployees.Fullscreen"/>
<activity android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="fullSensor"
tools:replace="android:screenOrientation"/>
<provider
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"
@@ -53,11 +57,6 @@
android:resource="@xml/file_path"/>
</provider>
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="barcode_ui">
</meta-data>
<activity
android:name=".activities.MainActivity"
android:exported="true"

View File

@@ -1,6 +1,5 @@
package org.foxarmy.barcodescannerforemployees
import android.util.Log
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
class Parser constructor() {
@@ -14,9 +13,6 @@ class Parser constructor() {
val found = foundByRegex.groupValues[0]
text = text.replace(found, "")
netWeight = stripNetWeight(found)
// when (found.lowercase().strip()) {
// "кг" -> netWeight *= 1000
// "мл" -> netWeight /= 1000
// }
return Triple(text, netWeight, found)
}
@@ -28,11 +24,11 @@ class Parser constructor() {
val (name, netWeight, unit) = findNetWeightInText(
text,
listOf(
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*[.,]?\\d+\\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
@@ -46,7 +42,6 @@ class Parser constructor() {
else -> { -1 }
}
Log.d("QWERTYUIOP", "Unit: ${strippedUnit}, number: ${unitNumber}")
return AbstractProduct(0, "", name, netWeight, "", 0, unitNumber)
}

View File

@@ -5,11 +5,11 @@ import android.content.Context
import android.content.ContextWrapper
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.content.FileProvider
import androidx.core.graphics.scale
import com.google.firebase.components.BuildConfig
import java.io.File
import java.io.FileOutputStream
@@ -33,8 +33,12 @@ fun generateThumbnailForImage(context: Context, imageHash: String) {
val imageFile = File(picturesDir, "$imageHash.png")
val imageContent = imageFile.inputStream().readBytes()
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)

View File

@@ -1,6 +1,8 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.Manifest
//import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
//import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import android.content.ContentValues
import android.content.DialogInterface
import android.content.Intent
@@ -14,9 +16,10 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import androidx.core.widget.addTextChangedListener
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions
import org.foxarmy.barcodescannerforemployees.*
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import java.io.File
@@ -42,19 +45,15 @@ class AddAbstractProductActivity : AppCompatActivity() {
private lateinit var pictureFile: File
private lateinit var picturesPath: File
private var barcode: String = ""
private var action: String = "new"
private var scanningBarcode = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
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")
val thumbnailsDir = File(cacheDir, "thumbnails")
@@ -76,15 +75,27 @@ class AddAbstractProductActivity : AppCompatActivity() {
fillupCategorySpinner()
fillupUnitsSpinner()
if (abstractProduct?.name == "" && abstractProduct?.barcode != "") {
performRequest(abstractProduct?.barcode!!)
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) {
val imageThumbnailUri = getImageUri(this, File(thumbnailsDir, "${abstractProduct!!.imageHash}.webp"))
pictureFile = File(picturesPath, "${abstractProduct!!.imageHash}.png]")
imageView.setImageURI(imageThumbnailUri)
imageView.rotation = 90f
barcodeText.setText(abstractProduct!!.barcode)
productNameText.text = abstractProduct!!.name
netWeightText.text = abstractProduct!!.netWeight.toString()
@@ -96,21 +107,21 @@ class AddAbstractProductActivity : AppCompatActivity() {
val productName = productNameText.text.toString()
val netWeight = netWeightText.text
if (abstractProduct == null && (!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
}
if (barcode == "") {
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
}
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
}
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
@@ -123,39 +134,27 @@ class AddAbstractProductActivity : AppCompatActivity() {
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)
} else {
db.update(AbstractProductContract.AbstractProductEntry.TABLE_NAME, values, "${BaseColumns._ID} = ?", arrayOf(abstractProduct!!.id.toString()))
}
finish()
}
takePictureButton.setOnClickListener {
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
}
scanButton.setOnClickListener {
val options = GmsBarcodeScannerOptions.Builder()
.setBarcodeFormats(
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()
}
scanningBarcode = true
requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
}
}
@@ -165,7 +164,11 @@ class AddAbstractProductActivity : AppCompatActivity() {
requester.request(this, barcode)
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)
.setMessage("You've got an abstract product with such barcode in your database")
.setPositiveButton("Quit") { _: DialogInterface, _: Int ->
@@ -174,7 +177,10 @@ class AddAbstractProductActivity : AppCompatActivity() {
.setNegativeButton("Edit existing") { _: DialogInterface, _: Int ->
val addProductIntent = Intent(this, AddAbstractProductActivity::class.java)
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)
addProductIntent.putExtras(extras)
ContextCompat.startActivity(this, addProductIntent, extras)
@@ -184,10 +190,12 @@ class AddAbstractProductActivity : AppCompatActivity() {
thread {
// Я сам в ахуях какой это костыль, пока хз как фиксить, потом придумаю :))
while (requester.response == "") { }
while (requester.response == "") {
}
if (requester.response == "Not found 404") {
runOnUiThread {
Toast.makeText(this, "Product not found. Please, try again or type manually", Toast.LENGTH_LONG).show()
Toast.makeText(this, "Product not found. Please, try again or type manually", Toast.LENGTH_LONG)
.show()
}
return@thread
}
@@ -212,7 +220,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)
unitTypeSpinner.adapter = arrayAdapter
}
@@ -226,15 +235,24 @@ class AddAbstractProductActivity : AppCompatActivity() {
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()) {
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)
categorySpinner.adapter = arrayAdapter
}
@@ -259,7 +277,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
}
@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
val imageFile = File(this.filesDir, "image.png")
val imageUri = getImageUri(this, imageFile)
@@ -274,9 +292,38 @@ class AddAbstractProductActivity : AppCompatActivity() {
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
getPicture()
if (scanningBarcode) {
prepareBarcodeScanner()
} else {
getPicture()
}
} 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 {
scanningBarcode = false
val scannedBarcode = result.contents
barcodeText.setText(scannedBarcode)
performRequest(scannedBarcode)
}
}
}
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.widget.Button
import android.widget.EditText
import android.widget.Toast
import org.foxarmy.barcodescannerforemployees.CategoriesContract
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
class AddCategoryActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -27,6 +28,11 @@ class AddCategoryActivity : Activity() {
findViewById<Button>(R.id.saveButton).setOnClickListener {
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
val values = ContentValues().apply {
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, categoryNameTextEdit.text.toString())

View File

@@ -1,12 +1,20 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.app.DatePickerDialog
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.View
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.core.content.ContextCompat
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.R
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
@@ -55,6 +63,7 @@ class AddProductActivity : AppCompatActivity() {
shelfLifeTextEdit = findViewById(R.id.shelfLifeTextEdit)
amountTextEdit = findViewById(R.id.amountTextEdit)
amountTextEdit.setText("1")
dateOfProductionSelectButton = findViewById(R.id.selectDateOfProductionButton)
saveProductButton = findViewById(R.id.saveProductButton)
@@ -77,17 +86,7 @@ class AddProductActivity : AppCompatActivity() {
}
scanButton.setOnClickListener {
val scanner = GmsBarcodeScanning.getClient(this)
scanner.startScan()
.addOnSuccessListener { bc ->
abstractProduct = DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, bc.rawValue.toString())
product!!.abstractProductId = abstractProduct!!.id
displayAbstractProduct(abstractProduct!!)
}
.addOnFailureListener {
Toast.makeText(this, "Cannot scan barcode", Toast.LENGTH_SHORT).show()
}
requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
}
expiryDateRadioButton.setOnClickListener {
@@ -136,11 +135,11 @@ class AddProductActivity : AppCompatActivity() {
saveProductButton.setOnClickListener {
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
}
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
}
@@ -150,13 +149,13 @@ class AddProductActivity : AppCompatActivity() {
}
} else {
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
}
}
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
}
@@ -185,10 +184,10 @@ class AddProductActivity : AppCompatActivity() {
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"
findViewById<TextView>(R.id.dateOfProductionTextView).text = "${getString(R.string.date_of_production)}: $dateOfProductionParsed"
val expiryDateParsed = SimpleDateFormat("dd.MM.yyyy").format(Date(product!!.dateOfExpiry * 1000))
expiryDateTextView.text = "Expiry date: $expiryDateParsed"
expiryDateTextView.text = "${getString(R.string.expiry_date)}: $expiryDateParsed"
if (amountTextEdit.text.toString() == "") {
amountTextEdit.setText(product!!.amount.toString())
@@ -211,8 +210,64 @@ class AddProductActivity : AppCompatActivity() {
product!!.dateOfExpiry = c.timeInMillis / 1000
}
private fun displayAbstractProduct(abstractProduct: AbstractProduct) {
abstractProductView.abstractProduct = abstractProduct
abstractProductView.update()
private fun displayAbstractProduct(abstractProduct: AbstractProduct?, scannedBarcode: String) {
if (abstractProduct == null) {
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

@@ -43,6 +43,7 @@ class MainActivity : AppCompatActivity() {
// I reuse the same stuff for editing and adding new product.
// if abstractProduct == null, it means that we need to create new object
extras.putParcelable("abstractProduct", null)
extras.putString("action", "new")
addAbstractProductIntent.putExtras(extras)
ContextCompat.startActivity(this, addAbstractProductIntent, extras)
}
@@ -50,7 +51,7 @@ class MainActivity : AppCompatActivity() {
"CategoriesFragment" -> {
val addCategoryIntent = Intent(this, AddCategoryActivity::class.java)
val extras = Bundle()
extras.putParcelable("category", Category(0, "New category"))
extras.putParcelable("category", Category(0, ""))
addCategoryIntent.putExtras(extras)
ContextCompat.startActivity(this, addCategoryIntent, extras)
}
@@ -69,13 +70,14 @@ class MainActivity : AppCompatActivity() {
private fun setupViewPager(viewpager: ViewPager) {
adapter = ViewPagerAdapter(supportFragmentManager)
adapter.addFragment(CategoriesFragment(), "Categories")
adapter.addFragment(StorageFragment(), "Storage")
adapter.addFragment(ShelfFragment(), "Shelf")
adapter.addFragment(StorageFragment(), getString(R.string.storage_title))
adapter.addFragment(ShelfFragment(), getString(R.string.shelf_title))
adapter.addFragment(CategoriesFragment(), getString(R.string.categories_title))
//TODO: settings fragments
// setting adapter to view pager.
viewpager.setAdapter(adapter)
viewpager.adapter = adapter
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -99,24 +101,24 @@ class MainActivity : AppCompatActivity() {
when (fragment::class.simpleName.toString()) {
"StorageFragment" -> {
AlertDialog.Builder(this)
.setMessage("Deleting an abstract product will also delete ALL the products, that belong to it. Do you want to proceed?")
.setPositiveButton("Yes") { _: DialogInterface, _: Int ->
.setMessage(getString(R.string.deleting_abstract_product_warning))
.setPositiveButton(getString(R.string.yes)) { _: DialogInterface, _: Int ->
val storageFragment = fragment as StorageFragment
storageFragment.removeSelected()
}
.setNegativeButton("No") { _: DialogInterface, _: Int ->
.setNegativeButton(getString(R.string.no)) { _: DialogInterface, _: Int ->
}.show()
}
"CategoriesFragment" -> {
AlertDialog.Builder(this)
.setMessage("Deleting a category will also delete ALL the products, that belong to that category. Do you want to proceed?")
.setPositiveButton("Yes") { _: DialogInterface, _: Int ->
.setMessage(getString(R.string.deleting_category_warning))
.setPositiveButton(getString(R.string.yes)) { _: DialogInterface, _: Int ->
val categoriesFragment = fragment as CategoriesFragment
categoriesFragment.removeSelected()
}
.setNegativeButton("No") { _: DialogInterface, _: Int ->
.setNegativeButton(getString(R.string.no)) { _: DialogInterface, _: Int ->
}.show()
}
@@ -151,4 +153,14 @@ class MainActivity : AppCompatActivity() {
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

@@ -45,7 +45,7 @@ class CategoriesFragment : Fragment() {
}
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()
}

View File

@@ -69,13 +69,13 @@ class ShelfFragment : Fragment() {
}
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()
}
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 =
ArrayAdapter(requireContext(), androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, sorts)
@@ -109,13 +109,13 @@ class ShelfFragment : Fragment() {
var orderBy: String = ""
when (binding.spinner.selectedItem) {
"Name" -> {
when (binding.spinner.selectedItemPosition) {
0 -> {
orderBy =
"(SELECT ${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID} = ${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID}) ASC"
}
"Category" -> {
1 -> {
orderBy =
"(SELECT ${AbstractProductContract.AbstractProductEntry.CATEGORY} FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID} = ${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID}) ASC"
}
@@ -126,11 +126,11 @@ class ShelfFragment : Fragment() {
// "(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"
}
"Date of expiry" -> {
4 -> {
orderBy = "${ProductContract.ProductEntry.EXPIRY_DATE} ASC"
}
}
@@ -151,7 +151,7 @@ class ShelfFragment : Fragment() {
val product = Product(productId, abstractProductId, amount, dateOfProduction, dateOfExpiry)
if (binding.spinner.selectedItem == "Freshness") {
if (binding.spinner.selectedItemPosition == 2) { //freshness
products.add(product)
} else {
val productView = ProductView(
@@ -200,7 +200,7 @@ class ShelfFragment : Fragment() {
if (!deleted) {
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()

View File

@@ -3,7 +3,6 @@ package org.foxarmy.barcodescannerforemployees.fragments
import android.content.Intent
import android.os.Bundle
import android.provider.BaseColumns
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -27,7 +26,7 @@ import kotlin.concurrent.thread
class StorageFragment : Fragment() {
private lateinit var binding: FragmentStorageBinding
private var filterByCategory = ""
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@@ -46,6 +45,11 @@ class StorageFragment : Fragment() {
}
}
binding.dropFiltersButton.setOnClickListener {
filterByCategory = ""
updateContent()
}
return binding.root
}
@@ -56,8 +60,7 @@ class StorageFragment : Fragment() {
}
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)
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
binding.spinner.adapter = arrayAdapter
@@ -81,7 +84,7 @@ class StorageFragment : Fragment() {
if (!deleted) {
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()
@@ -96,6 +99,7 @@ class StorageFragment : Fragment() {
val addProductIntent = Intent(requireContext(), AddAbstractProductActivity::class.java)
val extras = Bundle()
extras.putParcelable("abstractProduct", view.abstractProduct)
extras.putString("action", "update")
addProductIntent.putExtras(extras)
ContextCompat.startActivity(requireContext(), addProductIntent, extras)
}
@@ -121,17 +125,24 @@ class StorageFragment : Fragment() {
)
var orderBy: String = ""
Log.d("QWERTYUIOP", binding.spinner.selectedItem.toString())
when(binding.spinner.selectedItem) {
"Name" -> {
when(binding.spinner.selectedItemPosition) {
0 -> {
orderBy = "${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} ASC"
}
"Category" -> {
1 -> {
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
if (filterByCategory != "") {
selection = "${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?"
selectionArgs = arrayOf(filterByCategory)
}
val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy)
with (cursor) {
while(moveToNext()) {
@@ -166,4 +177,10 @@ class StorageFragment : Fragment() {
updateContent()
}
fun filterByCategory(id: Int) {
// filterByCategory = DBStorageController(context!!).getCategoryNameById(DBStorageController(context!!).readableDatabase, id)
filterByCategory = "$id"
updateContent()
}
}

View File

@@ -80,7 +80,7 @@ class AbstractProductView: LinearLayout {
thumbnailsDir.mkdirs()
val imageUri = getImageUri(activity, File(thumbnailsDir, "${abstractProduct.imageHash}.webp"))
productPicture!!.setImageURI(imageUri)
productPicture!!.rotation = 90f
// productPicture!!.rotation = 90f
productNameField!!.text = abstractProduct.name
netWeightField!!.text = abstractProduct.netWeight.toString()

View File

@@ -6,9 +6,10 @@ import android.view.LayoutInflater
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
class CategoryView : LinearLayout {
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)
true
}
setOnClickListener {
(activity as MainActivity).filterAbstractProductsByCategory(category.id)
}
}
}

View File

@@ -95,7 +95,7 @@ class ProductView: LinearLayout {
val pictureFile = File(thumbnailsDir, "${linkedAbstractProduct.imageHash}.webp")
val imageUri = getImageUri(activity, pictureFile)
productImageView.setImageURI(imageUri)
productImageView.rotation = 90f
// productImageView.rotation = 90f
productNameTextView.text = linkedAbstractProduct.name
productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString()
@@ -103,8 +103,8 @@ class ProductView: LinearLayout {
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))}"
productFreshnessTextView.text =
if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY) {
"Expired"
if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY || product.freshness < 0) {
context.getString(R.string.expired)
} else {
"${DecimalFormat("#.#").format(product.freshness*100)}%"
}

View File

@@ -7,7 +7,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
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">
<ImageView
android:layout_width="match_parent"

View File

@@ -4,19 +4,18 @@
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.AddCategoryActivity">
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context=".activities.AddCategoryActivity" android:layout_gravity="center">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="text"
android:text="@string/sample_category"
android:ems="10"
android:id="@+id/newCategoryName" app:layout_constraintStart_toStartOf="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
android:text="@string/saveButton"
android:layout_width="wrap_content"

View File

@@ -9,7 +9,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp">
android:padding="16dp" android:layout_gravity="center">
<Button
android:id="@+id/scan_button"
android:layout_width="100dp"
@@ -17,27 +17,27 @@
android:text="@string/scan_label"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/categoryTextView"
android:layout_marginTop="15dp"/>
android:layout_marginTop="40dp" android:layout_marginStart="5dp"/>
<ImageView
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_marginBottom="25dp"
app:layout_constraintBottom_toTopOf="@+id/productName" app:layout_constraintStart_toStartOf="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
android:layout_width="350dp"
android:layout_width="0dp"
android:layout_height="50dp"
android:inputType="text"
android:ems="10"
android:id="@+id/productName"
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_constraintBottom_toTopOf="@+id/netWeight"
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"/>
<EditText
android:layout_width="match_parent"
@@ -46,7 +46,7 @@
android:ems="10"
android:id="@+id/netWeight"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
android:layout_marginStart="5dp"
android:visibility="visible" android:hint="@string/netWeight" android:textColorHint="#737373"
app:layout_constraintTop_toBottomOf="@+id/productName"
/>
@@ -56,40 +56,41 @@
app:layout_constraintStart_toEndOf="@+id/netWeight"
app:layout_constraintTop_toBottomOf="@+id/productName" app:layout_constraintEnd_toEndOf="parent"/>
<EditText
android:layout_width="350dp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="text"
android:ems="10"
android:id="@+id/barcodeTextEdit" app:layout_constraintTop_toBottomOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="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" android:layout_marginEnd="5dp"/>
<TextView
android:text="@string/category"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/categoryTextView"
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"/>
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/categorySpinner"
android:layout_width="140dp"
android:layout_height="50dp" android:id="@+id/categorySpinner"
app:layout_constraintStart_toEndOf="@+id/categoryTextView"
app:layout_constraintTop_toBottomOf="@+id/netWeight" android:layout_marginStart="8dp"
android:layout_marginTop="18dp"/>
android:layout_marginTop="18dp" android:outlineProvider="bounds"/>
<Button
android:text="@string/saveButton"
android:layout_width="100dp"
android:layout_height="50dp" android:id="@+id/saveButton"
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
android:text="@string/takePicture"
android:layout_width="100dp"
android:layout_height="55dp" android:id="@+id/takePictureButton"
app:layout_constraintTop_toBottomOf="@+id/categoryTextView"
android:layout_marginTop="15dp" app:layout_constraintStart_toEndOf="@+id/scan_button"
android:layout_marginStart="33dp" app:layout_constraintEnd_toStartOf="@+id/saveButton"
android:layout_marginEnd="6dp" app:layout_constraintHorizontal_bias="0.0"/>
android:layout_marginTop="40dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -24,13 +24,13 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/abstractProductView" android:layout_marginTop="16dp"/>
<TextView
android:text="Date of production:"
android:text="@string/date_of_production"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/dateOfProductionTextView"
app:layout_constraintTop_toBottomOf="@+id/scanButton"
android:layout_marginTop="16dp" app:layout_constraintStart_toStartOf="parent"/>
<Button
android:text="Select"
android:text="@string/select"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/selectDateOfProductionButton"
app:layout_constraintTop_toBottomOf="@+id/scanButton"
@@ -43,11 +43,11 @@
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp"
android:orientation="horizontal" android:id="@+id/radioGroup">
<RadioButton
android:text="Expiry date"
android:text="@string/expiry_date"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/expiryDateRadio"/>
<RadioButton
android:text="Shelf life"
android:text="@string/shelf_life"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/shelfLifeRadio"/>
</RadioGroup>
@@ -80,11 +80,11 @@
app:layout_constraintStart_toEndOf="@+id/expiryDateTextView" android:layout_marginStart="16dp"
android:visibility="invisible"/>
<TextView
android:text="Amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/amountText"
app:layout_constraintTop_toBottomOf="@+id/shelfLifeTextEdit"
app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="16dp"/>
app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="16dp"
android:text="@string/amount"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@@ -8,6 +8,7 @@
<LinearLayout
android:orientation="vertical"
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>
</FrameLayout>

View File

@@ -8,13 +8,12 @@
android:layout_height="match_parent">
<TextView
android:text="Sort by"
android:layout_width="wrap_content"
android:layout_height="0dp" android:id="@+id/sortByTextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/scrollView" android:textAlignment="center"
/>
android:text="@string/sort_by"/>
<Spinner
android:layout_width="0dp"
android:layout_height="32dp" android:id="@+id/spinner"

View File

@@ -10,7 +10,7 @@
android:layout_height="match_parent">
<TextView
android:text="Sort by"
android:text="@string/sort_by"
android:layout_width="wrap_content"
android:layout_height="0dp" android:id="@+id/sortByTextView"
app:layout_constraintStart_toStartOf="parent"
@@ -22,12 +22,21 @@
android:layout_height="32dp" android:id="@+id/spinner"
app:layout_constraintTop_toTopOf="parent"
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
android:layout_width="match_parent"
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/spinner"
android:id="@+id/scrollView2">
android:layout_height="wrap_content"
android:id="@+id/scrollView2"
app:layout_constraintTop_toBottomOf="@+id/dropFiltersButton">
<androidx.gridlayout.widget.GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2"

View File

@@ -0,0 +1,83 @@
<?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>
</resources>

View File

@@ -3,10 +3,7 @@
<string name="action_settings">Settings</string>
<string name="first_fragment_label">Add new product</string>
<string name="second_fragment_label">Products</string>
<string name="scan_label">Scan</string>
<string name="previous">Previous</string>
<string name="product_name_label">Product name</string>
<string name="netWeight">Net weight (g)</string>
<string name="netWeight">Net weight</string>
<string name="saveButton">Save</string>
<string name="takePicture">Take picture</string>
<string name="productName">Product name</string>
@@ -17,16 +14,18 @@
<string name="sample_unit">g</string>
<string name="sample_category">Sample category</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="next">Next</string>
<string name="delete_menu">Delete item(s)…</string>
<string name="update_menu">Update item</string>
<string name="update">update</string>
<string name="delete">delete</string>
<string name="category">Category</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 -->
<string name="kilogram">kg</string>
@@ -34,4 +33,49 @@
<string name="liter">l</string>
<string name="milliliter">ml</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>
</resources>

View File

@@ -19,6 +19,7 @@ legacySupportV4 = "1.0.0"
fragment = "1.8.4"
playServicesCodeScanner = "16.1.0"
volley = "1.2.1"
zxingAndroidEmbedded = "4.3.0"
[libraries]
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" }
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" }
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }