From 3bfad0f6ad16c56aeb5aded849b925e901b429ee Mon Sep 17 00:00:00 2001 From: leca Date: Fri, 11 Oct 2024 04:58:31 +0300 Subject: [PATCH] some refactor, renaming, started working with product on shelf --- app/build.gradle.kts | 2 + app/src/main/AndroidManifest.xml | 6 +- .../barcodescannerforemployees/Category.kt | 8 - .../DBStorageController.kt | 79 +++---- .../barcodescannerforemployees/Parser.kt | 2 + .../activities/AddAbstractProductActivity.kt | 194 ++++++++++++++++++ .../activities/AddCategoryActivity.kt | 10 +- .../activities/AddProductActivity.kt | 190 +---------------- .../activities/MainActivity.kt | 30 ++- .../{ => dataclasses}/AbstractProduct.kt | 2 +- .../dataclasses/Category.kt | 39 ++++ .../dataclasses/Product.kt | 40 ++++ .../fragments/AddAbstractProductFragment.kt | 21 ++ .../fragments/CategoriesFragment.kt | 5 +- .../fragments/ShelfFragment.kt | 17 ++ .../fragments/StorageFragment.kt | 32 +-- .../views/AbstractProductView.kt | 18 +- .../views/CategoryView.kt | 2 +- .../layout/activity_add_abstract_product.xml | 10 + .../layout/content_add_abstract_product.xml | 14 ++ .../main/res/layout/content_add_product.xml | 10 +- .../layout/fragment_add_abstract_product.xml | 83 ++++++++ .../main/res/layout/fragment_add_product.xml | 73 +------ app/src/main/res/layout/fragment_shelf.xml | 6 + app/src/main/res/layout/product_view.xml | 7 + .../nav_graph_add_abstract_product.xml | 11 + .../res/navigation/nav_graph_add_product.xml | 5 +- gradle/libs.versions.toml | 4 + 28 files changed, 571 insertions(+), 349 deletions(-) delete mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/Category.kt create mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddAbstractProductActivity.kt rename app/src/main/java/org/foxarmy/barcodescannerforemployees/{ => dataclasses}/AbstractProduct.kt (95%) create mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Category.kt create mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Product.kt create mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/fragments/AddAbstractProductFragment.kt create mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/fragments/ShelfFragment.kt create mode 100644 app/src/main/res/layout/activity_add_abstract_product.xml create mode 100644 app/src/main/res/layout/content_add_abstract_product.xml create mode 100644 app/src/main/res/layout/fragment_add_abstract_product.xml create mode 100644 app/src/main/res/layout/fragment_shelf.xml create mode 100644 app/src/main/res/layout/product_view.xml create mode 100644 app/src/main/res/navigation/nav_graph_add_abstract_product.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6a97330..764c87e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -46,6 +46,8 @@ dependencies { implementation(libs.firebase.crashlytics.buildtools) implementation(libs.androidx.gridlayout) implementation(libs.androidx.activity) + implementation(libs.androidx.legacy.support.v4) + implementation(libs.androidx.fragment) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e8b3101..1dc263f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,6 +31,10 @@ android:name=".activities.AddProductActivity" android:exported="false" android:theme="@style/Theme.BarcodeScannerForEmployees"/> + () val projection = arrayOf( BaseColumns._ID, - ProductContract.ProductEntry.PRODUCT_NAME, - ProductContract.ProductEntry.IMAGE_FILENAME, - ProductContract.ProductEntry.PRODUCT_NET_WEIGHT, + AbstractProductContract.AbstractProductEntry.PRODUCT_NAME, + AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME, + AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT, ) - val selection = "${ProductContract.ProductEntry.CATEGORY} = ?" + val selection = "${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?" val selectionArgs = arrayOf(id.toString()) - val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null) + val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null) with(cursor) { while (moveToNext()) { val abstractProductId = getInt(getColumnIndexOrThrow(BaseColumns._ID)) - val abstractProductName = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.PRODUCT_NAME)) - val abstractProductNetWeight = getDouble(getColumnIndexOrThrow(ProductContract.ProductEntry.PRODUCT_NET_WEIGHT)) - val abstractProductImageHash = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.IMAGE_FILENAME)) + val abstractProductName = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME)) + val abstractProductNetWeight = getDouble(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT)) + val abstractProductImageHash = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME)) val abstractProduct = AbstractProduct(abstractProductId, abstractProductName, abstractProductNetWeight, abstractProductImageHash, category = id) @@ -123,17 +128,17 @@ class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE } fun getAmountOfAbstractProductsInCategory(db:SQLiteDatabase, id: Int) : Int { - return DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM ${ProductContract.ProductEntry.TABLE_NAME} WHERE ${ProductContract.ProductEntry.CATEGORY} = ?", arrayOf(id.toString())).toInt() + return DatabaseUtils.longForQuery(db, "SELECT COUNT(*) FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?", arrayOf(id.toString())).toInt() } fun eraseAbstractProduct(db: SQLiteDatabase, id: Int, context: Context) { val projection = arrayOf( - ProductContract.ProductEntry.IMAGE_FILENAME + AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME ) val selection = "${BaseColumns._ID} = ?" val selectionArgs = arrayOf(id.toString()) val cursor = db.query( - ProductContract.ProductEntry.TABLE_NAME, + AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, @@ -145,7 +150,7 @@ class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE with (cursor) { while(moveToNext()) { - val productImageHash = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.IMAGE_FILENAME)) + val productImageHash = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME)) imageHash = productImageHash } } @@ -154,7 +159,7 @@ class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE File(picturesDir, "$imageHash.png").delete() File(thumbnailsDir, "$imageHash.webp").delete() - db.delete(ProductContract.ProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null) + db.delete(AbstractProductContract.AbstractProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null) } companion object { diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/Parser.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/Parser.kt index 1d7958b..741d48c 100644 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/Parser.kt +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/Parser.kt @@ -1,5 +1,7 @@ package org.foxarmy.barcodescannerforemployees +import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct + class Parser constructor(payloadStartRegex: String, payloadEndRegex: String, payloadRegex: String){ val payloadStartRegex: String = payloadStartRegex val payloadEndRegex: String = payloadEndRegex diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddAbstractProductActivity.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddAbstractProductActivity.kt new file mode 100644 index 0000000..9b2dacb --- /dev/null +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddAbstractProductActivity.kt @@ -0,0 +1,194 @@ +package org.foxarmy.barcodescannerforemployees.activities + +import android.Manifest +import android.content.ContentValues +import android.os.Build +import android.os.Bundle +import android.provider.BaseColumns +import android.util.Log +import android.widget.* +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.appcompat.app.AppCompatActivity +import com.google.mlkit.vision.barcode.common.Barcode +import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions +import com.google.mlkit.vision.codescanner.GmsBarcodeScanning +import org.foxarmy.barcodescannerforemployees.* +import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct +import java.io.File +import java.nio.file.Files +import java.nio.file.StandardCopyOption + +class AddAbstractProductActivity : AppCompatActivity() { + private lateinit var imageView: ImageView + + private lateinit var saveButton: Button + private lateinit var takePictureButton: Button + private lateinit var scanButton: Button + + private lateinit var productNameText: TextView + private lateinit var netWeightText: TextView + + private lateinit var categorySpinner: Spinner + + private var abstractProduct: AbstractProduct? = null + private lateinit var pictureFile: File + private lateinit var picturesPath: File + + 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? + + picturesPath = File(filesDir, "pictures") + val thumbnailsDir = File(cacheDir, "thumbnails") + thumbnailsDir.mkdirs() + picturesPath.mkdirs() + imageView = findViewById(R.id.imageView) + + saveButton = findViewById(R.id.saveButton) + takePictureButton = findViewById(R.id.takePictureButton) + scanButton = findViewById(R.id.scan_button) + + productNameText = findViewById(R.id.productName) + netWeightText = findViewById(R.id.netWeight) + + categorySpinner = findViewById(R.id.categorySpinner) + + fillupCategorySpinner() + + if (abstractProduct != null) { + val imageThumbnailUri = getImageUri(this, File(thumbnailsDir, "${abstractProduct!!.imageHash}.webp")) + pictureFile = File(picturesPath, "${abstractProduct!!.imageHash}.png]") + imageView.setImageURI(imageThumbnailUri) + imageView.rotation = 90f + productNameText.text = abstractProduct!!.name + netWeightText.text = abstractProduct!!.netWeight.toString() + categorySpinner.setSelection(abstractProduct!!.category) + } + + saveButton.setOnClickListener { + val productName = productNameText.text.toString() + val netWeight = netWeightText.text + if (!this::pictureFile.isInitialized && !pictureFile.exists()) { + + Toast.makeText(this, "Please, make a picture of a product!", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + + if (productName == "") { + Toast.makeText(this, "Please, write a name of a product!", 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() + } + + val db = DBStorageController(this).writableDatabase + val values = ContentValues().apply { + put(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME, productName) + put(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT, netWeight.toString()) + put(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME, pictureFile.nameWithoutExtension) + put(AbstractProductContract.AbstractProductEntry.CATEGORY, categorySpinner.selectedItemPosition) + } + + if (abstractProduct == null) { + 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) + } + + scanButton.setOnClickListener { + val options = GmsBarcodeScannerOptions.Builder() + .setBarcodeFormats( + Barcode.FORMAT_EAN_13 + ) + .build() + val scanner = GmsBarcodeScanning.getClient(this) + scanner.startScan() + .addOnSuccessListener { barcode -> + productNameText.setText(barcode.rawValue) + } + .addOnFailureListener { e -> + Toast.makeText( + this, + "Failed to scan barcode. Please, try again or enter data manually", + Toast.LENGTH_LONG + ).show() + } + } + } + + fun fillupCategorySpinner() { + val db = DBStorageController(this).readableDatabase + + val categories = mutableListOf("") + + val projection = arrayOf( + CategoriesContract.CategoryEntry.CATEGORY_NAME + ) + + val cursor = db.query(CategoriesContract.CategoryEntry.TABLE_NAME, projection, null, null, null, null, BaseColumns._ID+" ASC") + + with (cursor) { + while (moveToNext()) { + categories.add(getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME))) + } + } + + val arrayAdapter = ArrayAdapter(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 + } + + @RequiresApi(Build.VERSION_CODES.R) + val takePicture = registerForActivityResult(ActivityResultContracts.TakePicture()) { success: Boolean -> + if (success) { + //Move picture to a proper directory according to its calculated hash + val tempfile = File(filesDir, "image.png") + val imageContent = tempfile.inputStream().readBytes() + val imageHash = imageContent.toString(Charsets.UTF_8).md5() + + pictureFile = File(picturesPath, "$imageHash.png") + Files.move(tempfile.toPath(), pictureFile.toPath(), StandardCopyOption.REPLACE_EXISTING) + tempfile.delete() + generateThumbnailForImage(this, imageHash) + + imageView.setImageURI(getImageUri(this, pictureFile)) + } else { + Log.e("QWERTYUIOP", "Cannot save a picture") + } + } + + @RequiresApi(Build.VERSION_CODES.R) + 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) + if (imageUri != null) { + takePicture.launch(imageUri) + } + } + + @RequiresApi(Build.VERSION_CODES.R) + val requestPermissionLauncher = + registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + getPicture() + } else { + Toast.makeText(this, "I need permission in order to take a picture", Toast.LENGTH_LONG).show() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddCategoryActivity.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddCategoryActivity.kt index 6f0827b..9c178d8 100644 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddCategoryActivity.kt +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddCategoryActivity.kt @@ -7,6 +7,7 @@ import android.provider.BaseColumns import android.widget.Button import android.widget.EditText import org.foxarmy.barcodescannerforemployees.CategoriesContract +import org.foxarmy.barcodescannerforemployees.dataclasses.Category import org.foxarmy.barcodescannerforemployees.DBStorageController import org.foxarmy.barcodescannerforemployees.R @@ -17,17 +18,16 @@ class AddCategoryActivity : Activity() { setContentView(R.layout.activity_add_category) val extras = intent.extras - val categoryId = extras!!.get("categoryid") as Int? - val categoryName = extras.get("categoryname") as String? + val category = extras!!.get("category") as Category? val categoryNameTextEdit: EditText = findViewById(R.id.newCategoryName) - categoryNameTextEdit.setText(categoryName) + categoryNameTextEdit.setText(category!!.name) findViewById