Compare commits
38 Commits
b3b4b1025e
...
alpha-0.0.
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a018903f0 | |||
| cba0e8594f | |||
| 244ef95a99 | |||
| 05f95ef30d | |||
| 385a19a6ee | |||
| d89d32a793 | |||
| dfe6fa9c3e | |||
| 0fdcabca48 | |||
| e9ef4c44f9 | |||
| c112eec868 | |||
| a2662bf55b | |||
| 6a49b573ca | |||
| b1b987e5f6 | |||
| 4e11a7b4b9 | |||
| 28da5408a1 | |||
| fd7feb72eb | |||
| 224887422c | |||
| b1030ac8c1 | |||
| e8e2a50cad | |||
| 231419eb77 | |||
| 6c4f11bcb0 | |||
| 8ef72db4e4 | |||
| f50e10e231 | |||
| 5066ded9c7 | |||
| efcd9361b2 | |||
| a6ab2e305f | |||
| 646ea2b91a | |||
| 6dd48d0a8d | |||
| 4cad738f0e | |||
| 3bfad0f6ad | |||
| 926403f9ea | |||
| 332c7cd3fe | |||
| a5a4c5db40 | |||
| 068cd7846b | |||
| fbfe68786e | |||
| cfef250d38 | |||
| 5fb481a0d5 | |||
| a6c21f05f9 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -146,3 +146,5 @@ dist
|
|||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
|
release
|
||||||
|
|||||||
@@ -32,9 +32,11 @@ android {
|
|||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
|
buildConfig = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
var cameraxVersion = "1.0.1"
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
@@ -45,18 +47,23 @@ dependencies {
|
|||||||
implementation(libs.androidx.navigation.ui.ktx)
|
implementation(libs.androidx.navigation.ui.ktx)
|
||||||
implementation(libs.firebase.crashlytics.buildtools)
|
implementation(libs.firebase.crashlytics.buildtools)
|
||||||
implementation(libs.androidx.gridlayout)
|
implementation(libs.androidx.gridlayout)
|
||||||
|
implementation(libs.androidx.activity)
|
||||||
|
implementation(libs.androidx.legacy.support.v4)
|
||||||
|
implementation(libs.androidx.fragment)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
|
implementation(libs.volley)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
|
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
|
|
||||||
|
|
||||||
implementation ("androidx.camera:camera-camera2:$cameraxVersion")
|
// CameraX library
|
||||||
implementation ("androidx.camera:camera-lifecycle:$cameraxVersion")
|
|
||||||
|
implementation (libs.androidx.camera.camera2)
|
||||||
|
implementation (libs.androidx.camera.lifecycle)
|
||||||
implementation (libs.androidx.camera.view)
|
implementation (libs.androidx.camera.view)
|
||||||
implementation ("com.google.android.gms:play-services-code-scanner:16.1.0")
|
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@@ -24,10 +25,17 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.BarcodeScannerForEmployees"
|
android:theme="@style/Theme.BarcodeScannerForEmployees"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
|
<activity
|
||||||
|
android:name=".activities.AddCategoryActivity"
|
||||||
|
android:exported="false"/>
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.AddProductActivity"
|
android:name=".activities.AddProductActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.AddAbstractProductActivity"
|
||||||
|
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"
|
||||||
@@ -35,9 +43,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.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"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
@@ -45,11 +57,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"
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees
|
|
||||||
|
|
||||||
class AbstractProduct(
|
|
||||||
public var id: Int,
|
|
||||||
public var name: String,
|
|
||||||
public var netWeight: Double,
|
|
||||||
public var imageHash: String,
|
|
||||||
public var category: Int
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,26 +1,24 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees
|
package org.foxarmy.barcodescannerforemployees
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.database.DatabaseUtils
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
import android.provider.BaseColumns
|
import android.provider.BaseColumns
|
||||||
import android.util.Log
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object ProductContract {
|
object AbstractProductContract {
|
||||||
object ProductEntry : BaseColumns {
|
object AbstractProductEntry : BaseColumns {
|
||||||
const val TABLE_NAME = "products"
|
const val TABLE_NAME = "abstract_products"
|
||||||
|
const val BARCODE = "barcode"
|
||||||
const val PRODUCT_NAME = "name"
|
const val PRODUCT_NAME = "name"
|
||||||
const val PRODUCT_NET_WEIGHT = "net_weight"
|
const val PRODUCT_NET_WEIGHT = "net_weight"
|
||||||
const val IMAGE_FILENAME = "image_filename"
|
const val IMAGE_FILENAME = "image_filename"
|
||||||
}
|
const val CATEGORY = "category"
|
||||||
}
|
const val UNIT = "unit"
|
||||||
|
|
||||||
object ShelfContract {
|
|
||||||
object ShelfEntry : BaseColumns {
|
|
||||||
const val TABLE_NAME = "shelf"
|
|
||||||
const val PRODUCT_ID = "product_id"
|
|
||||||
const val EXPIRE_DATE = "expire_date"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,71 +29,253 @@ object CategoriesContract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const val SQL_CREATE_PRODUCT_TABLE =
|
object ProductContract {
|
||||||
"CREATE TABLE ${ProductContract.ProductEntry.TABLE_NAME} (" +
|
object ProductEntry : BaseColumns {
|
||||||
"${BaseColumns._ID} INTEGER PRIMARY KEY," +
|
const val TABLE_NAME = "products"
|
||||||
"${ProductContract.ProductEntry.PRODUCT_NAME} TEXT," +
|
const val ABSTRACT_PRODUCT_ID = "abstract_product_id"
|
||||||
"${ProductContract.ProductEntry.PRODUCT_NET_WEIGHT} REAL," +
|
const val AMOUNT = "amount"
|
||||||
"${ProductContract.ProductEntry.IMAGE_FILENAME} TEXT)"
|
const val DATE_OF_PRODUCTION = "date_of_production"
|
||||||
|
const val EXPIRY_DATE = "expiry_date"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const val SQL_CREATE_SHELF_TABLE =
|
const val SQL_CREATE_ABSTRACT_PRODUCTS_TABLE =
|
||||||
"CREATE TABLE ${ShelfContract.ShelfEntry.TABLE_NAME} (" +
|
"CREATE TABLE ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} (" +
|
||||||
"${ShelfContract.ShelfEntry.PRODUCT_ID} INTEGER," +
|
"${BaseColumns._ID} INTEGER PRIMARY KEY," +
|
||||||
"${ShelfContract.ShelfEntry.EXPIRE_DATE} DATE)"
|
"${AbstractProductContract.AbstractProductEntry.BARCODE} TEXT," +
|
||||||
|
"${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} TEXT," +
|
||||||
|
"${AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT} REAL," +
|
||||||
|
"${AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME} TEXT," +
|
||||||
|
"${AbstractProductContract.AbstractProductEntry.CATEGORY} INTEGER," +
|
||||||
|
"${AbstractProductContract.AbstractProductEntry.UNIT} INTEGER)"
|
||||||
|
|
||||||
|
const val SQL_CREATE_PRODUCTS_TABLE =
|
||||||
|
"CREATE TABLE ${ProductContract.ProductEntry.TABLE_NAME} (" +
|
||||||
|
"${BaseColumns._ID} INTEGER PRIMARY KEY," +
|
||||||
|
"${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID} INTEGER," +
|
||||||
|
"${ProductContract.ProductEntry.AMOUNT} INTEGER," +
|
||||||
|
"${ProductContract.ProductEntry.DATE_OF_PRODUCTION} INTEGER," +
|
||||||
|
"${ProductContract.ProductEntry.EXPIRY_DATE} INTGER)"
|
||||||
|
|
||||||
const val SQL_CREATE_CATEGORIES_TABLE =
|
const val SQL_CREATE_CATEGORIES_TABLE =
|
||||||
"CREATE TABLE ${CategoriesContract.CategoryEntry.TABLE_NAME} (" +
|
"CREATE TABLE ${CategoriesContract.CategoryEntry.TABLE_NAME} (" +
|
||||||
|
"${BaseColumns._ID} INTEGER PRIMARY KEY," +
|
||||||
"${CategoriesContract.CategoryEntry.CATEGORY_NAME} TEXT)"
|
"${CategoriesContract.CategoryEntry.CATEGORY_NAME} TEXT)"
|
||||||
|
|
||||||
class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
|
class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
|
||||||
|
|
||||||
override fun onCreate(db: SQLiteDatabase) {
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
db.execSQL(SQL_CREATE_PRODUCT_TABLE)
|
db.execSQL(SQL_CREATE_ABSTRACT_PRODUCTS_TABLE)
|
||||||
db.execSQL(SQL_CREATE_SHELF_TABLE)
|
db.execSQL(SQL_CREATE_PRODUCTS_TABLE)
|
||||||
db.execSQL(SQL_CREATE_CATEGORIES_TABLE)
|
db.execSQL(SQL_CREATE_CATEGORIES_TABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
|
override fun onUpgrade(db: SQLiteDatabase?, oldV: Int, newV: Int) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun eraseAbstractProduct(db: SQLiteDatabase, id: Int, context: Context) {
|
fun eraseCategory (db: SQLiteDatabase, id: Int, context: Context) {
|
||||||
val projection = arrayOf(BaseColumns._ID,
|
val productsInCategory = getAllAbstractProductInCategory(db, id)
|
||||||
ProductContract.ProductEntry.IMAGE_FILENAME
|
|
||||||
)
|
|
||||||
|
|
||||||
// val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, , null, null, null)
|
for (product in productsInCategory.iterator()) {
|
||||||
|
eraseAbstractProduct(db, product.id, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.delete(CategoriesContract.CategoryEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCategoryNameById(db: SQLiteDatabase, id: Int) : String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
val projection = arrayOf(CategoriesContract.CategoryEntry.CATEGORY_NAME)
|
||||||
val selection = "${BaseColumns._ID} = ?"
|
val selection = "${BaseColumns._ID} = ?"
|
||||||
val selectionArgs = arrayOf(id.toString())
|
val selectionArgs = arrayOf(id.toString())
|
||||||
val cursor = db.query(
|
|
||||||
ProductContract.ProductEntry.TABLE_NAME,
|
val cursor = db.query(CategoriesContract.CategoryEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)
|
||||||
projection,
|
|
||||||
selection,
|
with (cursor) {
|
||||||
selectionArgs,
|
while (moveToNext()) {
|
||||||
|
result = getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllAbstractProductInCategory(db: SQLiteDatabase, id: Int) : List<AbstractProduct> {
|
||||||
|
|
||||||
|
var result = mutableListOf<AbstractProduct>()
|
||||||
|
val projection = arrayOf(
|
||||||
|
BaseColumns._ID,
|
||||||
|
AbstractProductContract.AbstractProductEntry.BARCODE,
|
||||||
|
AbstractProductContract.AbstractProductEntry.PRODUCT_NAME,
|
||||||
|
AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME,
|
||||||
|
AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT,
|
||||||
|
AbstractProductContract.AbstractProductEntry.UNIT
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = "${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?"
|
||||||
|
val selectionArgs = arrayOf(id.toString())
|
||||||
|
|
||||||
|
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 abstractProductBarcode = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.BARCODE))
|
||||||
|
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 abstractProductUnit = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.UNIT))
|
||||||
|
|
||||||
|
val abstractProduct = AbstractProduct(abstractProductId, abstractProductBarcode, abstractProductName, abstractProductNetWeight, abstractProductImageHash, category = id, abstractProductUnit)
|
||||||
|
|
||||||
|
result.add(abstractProduct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAmountOfAbstractProductsInCategory(db:SQLiteDatabase, id: Int) : Int {
|
||||||
|
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 projectionForAbstractProduct = arrayOf(
|
||||||
|
AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME
|
||||||
|
)
|
||||||
|
|
||||||
|
val selectionForAbstractProduct = "${BaseColumns._ID} = ?"
|
||||||
|
val selectionArgsForAbstractProduct = arrayOf(id.toString())
|
||||||
|
val cursorForAbstractProduct = db.query(
|
||||||
|
AbstractProductContract.AbstractProductEntry.TABLE_NAME,
|
||||||
|
projectionForAbstractProduct,
|
||||||
|
selectionForAbstractProduct,
|
||||||
|
selectionArgsForAbstractProduct,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
// val cursor = db.rawQuery("SELECT image_filename FROM ${ProductContract.ProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID}=$id", null)
|
|
||||||
var imageHash: String = ""
|
var imageHash: String = ""
|
||||||
|
|
||||||
with (cursor) {
|
with (cursorForAbstractProduct) {
|
||||||
while(moveToNext()) {
|
while(moveToNext()) {
|
||||||
val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
|
val productImageHash = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME))
|
||||||
val productImageHash = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.IMAGE_FILENAME))
|
|
||||||
imageHash = productImageHash
|
imageHash = productImageHash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.d("QWERTYUIOP", "Deleting image with hash: $imageHash and $id")
|
|
||||||
val picturesDir = File(context.filesDir, "pictures")
|
val picturesDir = File(context.filesDir, "pictures")
|
||||||
val thumbnailsDir = File(context.cacheDir, "thumbnails")
|
val thumbnailsDir = File(context.cacheDir, "thumbnails")
|
||||||
|
|
||||||
File(picturesDir, "$imageHash.png").delete()
|
File(picturesDir, "$imageHash.png").delete()
|
||||||
File(thumbnailsDir, "$imageHash.webp").delete()
|
File(thumbnailsDir, "$imageHash.webp").delete()
|
||||||
Log.d("QWERTYUIOP", BaseColumns._ID + "=" + id)
|
|
||||||
|
val projectionForProducts = arrayOf(BaseColumns._ID)
|
||||||
|
val selectionForProducts = "${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID} = ?"
|
||||||
|
val selectionArgsForProducts = arrayOf(id.toString())
|
||||||
|
|
||||||
|
val cursorForProducts = db.query(ProductContract.ProductEntry.TABLE_NAME, projectionForProducts, selectionForProducts, selectionArgsForProducts, null, null, null)
|
||||||
|
|
||||||
|
with (cursorForProducts) {
|
||||||
|
while(moveToNext()) {
|
||||||
|
eraseProduct(db, getInt(getColumnIndexOrThrow(BaseColumns._ID)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.delete(AbstractProductContract.AbstractProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun eraseProduct(db:SQLiteDatabase, id: Int) {
|
||||||
db.delete(ProductContract.ProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
|
db.delete(ProductContract.ProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun insertNewProduct(db: SQLiteDatabase, product: Product) {
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID, product.abstractProductId)
|
||||||
|
put(ProductContract.ProductEntry.AMOUNT, product.amount)
|
||||||
|
put(ProductContract.ProductEntry.DATE_OF_PRODUCTION, product.dateOfProduction)
|
||||||
|
put(ProductContract.ProductEntry.EXPIRY_DATE, product.dateOfExpiry)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.insert(ProductContract.ProductEntry.TABLE_NAME, null, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findAbstractProductByBarcode (db: SQLiteDatabase, barcode: String) : AbstractProduct? {
|
||||||
|
var abstractProduct: AbstractProduct? = null
|
||||||
|
val projection = arrayOf(
|
||||||
|
BaseColumns._ID,
|
||||||
|
AbstractProductContract.AbstractProductEntry.PRODUCT_NAME,
|
||||||
|
AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME,
|
||||||
|
AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT,
|
||||||
|
AbstractProductContract.AbstractProductEntry.CATEGORY,
|
||||||
|
AbstractProductContract.AbstractProductEntry.UNIT
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = "${AbstractProductContract.AbstractProductEntry.BARCODE} = ?"
|
||||||
|
val selectionArgs = arrayOf(barcode)
|
||||||
|
|
||||||
|
val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)
|
||||||
|
|
||||||
|
with(cursor) {
|
||||||
|
while(moveToNext()) {
|
||||||
|
val id = getInt(getColumnIndexOrThrow(BaseColumns._ID))
|
||||||
|
val productName = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME))
|
||||||
|
val imageFilename = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME))
|
||||||
|
val netWeight = getDouble(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT))
|
||||||
|
val category = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.CATEGORY))
|
||||||
|
val unit = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.UNIT))
|
||||||
|
|
||||||
|
abstractProduct = AbstractProduct(id, barcode, productName, netWeight, imageFilename, category, unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return abstractProduct
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findAbstractProductById(db: SQLiteDatabase, id: Int): AbstractProduct? {
|
||||||
|
var abstractProduct: AbstractProduct? = null
|
||||||
|
val projection = arrayOf(
|
||||||
|
BaseColumns._ID,
|
||||||
|
AbstractProductContract.AbstractProductEntry.PRODUCT_NAME,
|
||||||
|
AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT,
|
||||||
|
AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME,
|
||||||
|
AbstractProductContract.AbstractProductEntry.CATEGORY,
|
||||||
|
AbstractProductContract.AbstractProductEntry.BARCODE,
|
||||||
|
AbstractProductContract.AbstractProductEntry.UNIT
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = "${BaseColumns._ID} = ?"
|
||||||
|
val selectionArgs = arrayOf(id.toString())
|
||||||
|
|
||||||
|
val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)
|
||||||
|
|
||||||
|
with (cursor) {
|
||||||
|
while (moveToNext()) {
|
||||||
|
val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
|
||||||
|
val barcode = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.BARCODE))
|
||||||
|
val name = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME))
|
||||||
|
val netWeight = getDouble(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT))
|
||||||
|
val imageHash = getString((getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME)))
|
||||||
|
val category = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.CATEGORY))
|
||||||
|
val unit = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.UNIT))
|
||||||
|
|
||||||
|
abstractProduct = AbstractProduct(productId, barcode, name, netWeight, imageHash, category, unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return abstractProduct
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateProduct(db: SQLiteDatabase, product: Product) {
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID, product.abstractProductId)
|
||||||
|
put(ProductContract.ProductEntry.AMOUNT, product.amount)
|
||||||
|
put(ProductContract.ProductEntry.DATE_OF_PRODUCTION, product.dateOfProduction)
|
||||||
|
put(ProductContract.ProductEntry.EXPIRY_DATE, product.dateOfExpiry)
|
||||||
|
}
|
||||||
|
db.update(ProductContract.ProductEntry.TABLE_NAME, values, "${BaseColumns._ID} = ?", arrayOf(product.id.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DATABASE_VERSION = 1
|
const val DATABASE_VERSION = 1
|
||||||
const val DATABASE_NAME = "database.db"
|
const val DATABASE_NAME = "database.db"
|
||||||
|
|||||||
@@ -1,45 +1,48 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees
|
package org.foxarmy.barcodescannerforemployees
|
||||||
|
|
||||||
class Parser constructor(payloadStartRegex: String, payloadEndRegex: String, payloadRegex: String){
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
val payloadStartRegex: String = payloadStartRegex
|
|
||||||
val payloadEndRegex: String = payloadEndRegex
|
|
||||||
val payloadRegex: String = payloadRegex
|
|
||||||
|
|
||||||
fun parse(text: String): MutableList<AbstractProduct> {
|
class Parser constructor() {
|
||||||
|
|
||||||
val payloadStart = Regex(payloadStartRegex)
|
fun findNetWeightInText(text: String, regexes: List<Regex>): Triple<String, Double, String> {
|
||||||
val payloadEnd = Regex(payloadEndRegex)
|
var text = text
|
||||||
val payload = Regex(payloadRegex)
|
var netWeight = 0.0
|
||||||
|
for (regex in regexes.iterator()) {
|
||||||
|
val foundByRegex = regex.find(text)
|
||||||
|
if (foundByRegex != null) {
|
||||||
|
val found = foundByRegex.groupValues[0]
|
||||||
|
text = text.replace(found, "")
|
||||||
|
netWeight = stripNetWeight(found)
|
||||||
|
// }
|
||||||
|
return Triple(text, netWeight, found)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Triple("", 0.0, "")
|
||||||
|
}
|
||||||
|
|
||||||
val startFound = payloadStart.find(text)?.value.toString()
|
fun parse(text: String): AbstractProduct {
|
||||||
val payloadStartIndex = text.indexOf(startFound)
|
val (name, netWeight, unit) = findNetWeightInText(
|
||||||
var clearText = text.removeRange(
|
text,
|
||||||
0,
|
listOf(
|
||||||
if (payloadStartIndex < 0) 0 else payloadStartIndex
|
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
|
||||||
|
val strippedUnit = unit.lowercase().replace(Regex("\\d"), "").strip()
|
||||||
|
unitNumber = when (strippedUnit) {
|
||||||
|
"кг" -> { 0 }
|
||||||
|
"г" -> { 1 }
|
||||||
|
"л" -> { 2 }
|
||||||
|
"мл" -> { 3 }
|
||||||
|
"шт" -> { 4 }
|
||||||
|
|
||||||
val endFound = payloadEnd.find(clearText)?.value.toString()
|
else -> { -1 }
|
||||||
val payloadEndIndex = clearText.indexOf(endFound)
|
|
||||||
clearText = clearText.removeRange(
|
|
||||||
if (payloadEndIndex < 0) 0 else payloadEndIndex - 1,
|
|
||||||
clearText.length
|
|
||||||
)
|
|
||||||
|
|
||||||
println(clearText)
|
|
||||||
|
|
||||||
var products = payload.findAll(clearText).toMutableList()
|
|
||||||
|
|
||||||
for (product in products) {
|
|
||||||
println(product.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mutableListOf()
|
return AbstractProduct(0, "", name, netWeight, "", 0, unitNumber)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun main () {
|
|
||||||
val p = Parser("""\<table\s*class\=\"randomBarcodes\"\s*>""", """\<\/table\>""", """[ёЁ\u0401\u0451\u0410-\u044f\d\w\s]{16,}""")
|
|
||||||
p.parse(Requester("https://barcode-list.ru", "barcode/RU/Поиск.htm?barcode=4680036915828", ).request("4680036915828"))
|
|
||||||
// println(Requester("https://barcode-list.ru", "barcode/RU/Поиск.htm?barcode=4680036915828", ).request("4680036915828"))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,28 +1,40 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees
|
package org.foxarmy.barcodescannerforemployees
|
||||||
|
|
||||||
import java.net.HttpURLConnection
|
import android.content.Context
|
||||||
import java.net.URL
|
import android.widget.Toast
|
||||||
|
import com.android.volley.toolbox.StringRequest
|
||||||
|
import com.android.volley.toolbox.Volley
|
||||||
|
|
||||||
class Requester constructor(siteName:String, endpoint: String) {
|
class Requester constructor(var siteName: String, var endpoint: String) {
|
||||||
var siteName: String = siteName
|
var response = ""
|
||||||
var endpoint: String = endpoint
|
|
||||||
|
|
||||||
fun request (productId: String): String {
|
fun request(context: Context, barcode: String) {
|
||||||
val url = URL("${siteName}/$endpoint")
|
val url = "${siteName}/${endpoint}"
|
||||||
|
|
||||||
var response: String = ""
|
val volleyQueue = Volley.newRequestQueue(context)
|
||||||
|
val stringRequest = object: StringRequest(
|
||||||
with(url.openConnection() as HttpURLConnection) {
|
Method.POST, url, { resp ->
|
||||||
requestMethod = "GET" // optional default is GET
|
run {
|
||||||
|
response =
|
||||||
println("\nSent 'GET' request to URL : $url; Response Code : $responseCode")
|
if (resp == "") {
|
||||||
|
"Not found 404"
|
||||||
inputStream.bufferedReader().use {
|
} else {
|
||||||
it.lines().forEach { line ->
|
resp
|
||||||
response += line //+ '\n'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Toast.makeText(context, "Cannot make request", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
override fun getHeaders(): Map<String, String> {
|
||||||
|
return mapOf("referer" to "$siteName/")
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun getParams(): MutableMap<String, String> {
|
||||||
|
return mutableMapOf("barcode" to barcode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return response
|
volleyQueue.add(stringRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.foxarmy.barcodescannerforemployees
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -12,7 +13,9 @@ 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
|
||||||
|
import java.net.URLEncoder
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
fun getImageUri(activity: Activity, imageFile: File): Uri? {
|
fun getImageUri(activity: Activity, imageFile: File): Uri? {
|
||||||
return FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + "." + activity.localClassName + ".provider", imageFile)
|
return FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + "." + activity.localClassName + ".provider", imageFile)
|
||||||
@@ -39,4 +42,52 @@ fun String.md5(): String {
|
|||||||
val md = MessageDigest.getInstance("MD5")
|
val md = MessageDigest.getInstance("MD5")
|
||||||
val digest = md.digest(this.toByteArray())
|
val digest = md.digest(this.toByteArray())
|
||||||
return digest.toHexString()
|
return digest.toHexString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun stripNetWeight (netWeight: String): Double {
|
||||||
|
return removeSubstringsFromString(netWeight, arrayOf("Л", "л", "мл", "Мл", "г", "Г", "кг", "Кг", "шт", "Шт", ",", " ", ".")).toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeSubstringsFromString(text: String, toRemove: Array<String>): String {
|
||||||
|
var result = text
|
||||||
|
for (candidate in toRemove.iterator()) {
|
||||||
|
result = result.replace(candidate, "")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.utf8(): String = URLEncoder.encode(this, "UTF-8")
|
||||||
|
|
||||||
|
fun getActivity(context: Context?): Activity? {
|
||||||
|
if (context == null) {
|
||||||
|
return null
|
||||||
|
} else if (context is ContextWrapper) {
|
||||||
|
return if (context is Activity) {
|
||||||
|
context
|
||||||
|
} else {
|
||||||
|
getActivity(context.baseContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun calculateProductFreshness(dateOfProduction: Long, dateOfExpiry: Long): Double {
|
||||||
|
|
||||||
|
val productLifeSpan = dateOfExpiry - dateOfProduction;
|
||||||
|
val lifeSpanLeft: Long = dateOfExpiry - Date().time / 1000
|
||||||
|
|
||||||
|
return lifeSpanLeft / productLifeSpan.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUnitNameById (context: Context, id: Int): String {
|
||||||
|
return when(id) {
|
||||||
|
0 -> { context.getString(R.string.kilogram) }
|
||||||
|
1 -> { context.getString(R.string.gram) }
|
||||||
|
2 -> { context.getString(R.string.liter) }
|
||||||
|
3 -> { context.getString(R.string.milliliter) }
|
||||||
|
4 -> { context.getString(R.string.pieces) }
|
||||||
|
else -> { "" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import androidx.fragment.app.FragmentPagerAdapter
|
|||||||
class ViewPagerAdapter
|
class ViewPagerAdapter
|
||||||
public constructor(supportFragmentManager: FragmentManager) : FragmentPagerAdapter(supportFragmentManager) {
|
public constructor(supportFragmentManager: FragmentManager) : FragmentPagerAdapter(supportFragmentManager) {
|
||||||
|
|
||||||
private final var fragmentList1: ArrayList<Fragment> = ArrayList()
|
private var fragmentList1: ArrayList<Fragment> = ArrayList()
|
||||||
private final var fragmentTitleList1: ArrayList<String> = ArrayList()
|
private var fragmentTitleList1: ArrayList<String> = ArrayList()
|
||||||
|
|
||||||
public override fun getItem(position: Int): Fragment {
|
override fun getItem(position: Int): Fragment {
|
||||||
return fragmentList1.get(position)
|
return fragmentList1.get(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,325 @@
|
|||||||
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
//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
|
||||||
|
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.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
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
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.StandardCopyOption
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
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 barcodeText: EditText
|
||||||
|
private lateinit var productNameText: TextView
|
||||||
|
private lateinit var netWeightText: TextView
|
||||||
|
private lateinit var unitTypeSpinner: Spinner
|
||||||
|
|
||||||
|
private lateinit var categorySpinner: Spinner
|
||||||
|
|
||||||
|
private var abstractProduct: AbstractProduct? = null
|
||||||
|
private lateinit var pictureFile: File
|
||||||
|
private lateinit var picturesPath: File
|
||||||
|
private var barcode: String = ""
|
||||||
|
|
||||||
|
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")
|
||||||
|
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)
|
||||||
|
|
||||||
|
barcodeText = findViewById(R.id.barcodeTextEdit)
|
||||||
|
productNameText = findViewById(R.id.productName)
|
||||||
|
netWeightText = findViewById(R.id.netWeight)
|
||||||
|
unitTypeSpinner = findViewById(R.id.unitTypeSpinner)
|
||||||
|
|
||||||
|
categorySpinner = findViewById(R.id.categorySpinner)
|
||||||
|
|
||||||
|
fillupCategorySpinner()
|
||||||
|
fillupUnitsSpinner()
|
||||||
|
|
||||||
|
barcodeText.addTextChangedListener {
|
||||||
|
this.barcode = barcodeText.text.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abstractProduct?.name == "" && 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()
|
||||||
|
categorySpinner.setSelection(abstractProduct!!.category)
|
||||||
|
unitTypeSpinner.setSelection(abstractProduct!!.unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
saveButton.setOnClickListener {
|
||||||
|
val productName = productNameText.text.toString()
|
||||||
|
val netWeight = netWeightText.text
|
||||||
|
if (abstractProduct == null && (!this::pictureFile.isInitialized || !pictureFile.exists())) {
|
||||||
|
Toast.makeText(this, getString(R.string.product_picture_request), Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
if (barcode == "") {
|
||||||
|
Toast.makeText(this, getString(R.string.product_barcode_request), Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productName == "") {
|
||||||
|
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, getString(R.string.product_net_weight_request), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
val db = DBStorageController(this).writableDatabase
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.BARCODE, barcode)
|
||||||
|
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)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.UNIT, unitTypeSpinner.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(android.Manifest.permission.CAMERA)
|
||||||
|
}
|
||||||
|
|
||||||
|
scanButton.setOnClickListener {
|
||||||
|
scanningBarcode = true
|
||||||
|
requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun performRequest(barcode: String) {
|
||||||
|
barcodeText.setText(this.barcode)
|
||||||
|
val requester = Requester("https://ean-online.ru", "match.php")
|
||||||
|
requester.request(this, barcode)
|
||||||
|
var abstractProduct: AbstractProduct
|
||||||
|
|
||||||
|
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 ->
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
.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
|
||||||
|
)
|
||||||
|
extras.putParcelable("abstractProduct", existingAbstractProduct)
|
||||||
|
addProductIntent.putExtras(extras)
|
||||||
|
ContextCompat.startActivity(this, addProductIntent, extras)
|
||||||
|
finish()
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
thread {
|
||||||
|
// Я сам в ахуях какой это костыль, пока хз как фиксить, потом придумаю :))
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
return@thread
|
||||||
|
}
|
||||||
|
|
||||||
|
abstractProduct = Parser().parse(requester.response)
|
||||||
|
requester.response = ""
|
||||||
|
runOnUiThread {
|
||||||
|
productNameText.text = abstractProduct.name
|
||||||
|
netWeightText.text = abstractProduct.netWeight.toString()
|
||||||
|
unitTypeSpinner.setSelection(abstractProduct.unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillupUnitsSpinner() {
|
||||||
|
val units = listOf(
|
||||||
|
getString(R.string.kilogram),
|
||||||
|
getString(R.string.gram),
|
||||||
|
getString(R.string.liter),
|
||||||
|
getString(R.string.milliliter),
|
||||||
|
getString(R.string.pieces)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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<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
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
if (scanningBarcode) {
|
||||||
|
prepareBarcodeScanner()
|
||||||
|
} else {
|
||||||
|
getPicture()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.os.Bundle
|
||||||
|
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
|
||||||
|
|
||||||
|
class AddCategoryActivity : Activity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_add_category)
|
||||||
|
|
||||||
|
val extras = intent.extras
|
||||||
|
val category = extras!!.get("category") as Category?
|
||||||
|
|
||||||
|
val categoryNameTextEdit: EditText = findViewById(R.id.newCategoryName)
|
||||||
|
|
||||||
|
categoryNameTextEdit.setText(category!!.name)
|
||||||
|
|
||||||
|
findViewById<Button>(R.id.saveButton).setOnClickListener {
|
||||||
|
val db = DBStorageController(this).writableDatabase
|
||||||
|
|
||||||
|
if (category.id == 0) { // Inserting new category
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, categoryNameTextEdit.text.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
db.insert(CategoriesContract.CategoryEntry.TABLE_NAME, null, values)
|
||||||
|
} else { // Updating existing category
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, categoryNameTextEdit.text.toString())
|
||||||
|
}
|
||||||
|
db.update(CategoriesContract.CategoryEntry.TABLE_NAME, values, "${BaseColumns._ID} = ?", arrayOf(category.id.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,143 +1,214 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees.activities
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
import android.Manifest
|
import android.app.DatePickerDialog
|
||||||
import android.content.ContentValues
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.view.View
|
||||||
import android.widget.Button
|
import android.widget.*
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
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 org.foxarmy.barcodescannerforemployees.*
|
import com.journeyapps.barcodescanner.ScanOptions
|
||||||
import org.foxarmy.barcodescannerforemployees.databinding.ActivityAddProductBinding
|
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
||||||
import java.io.File
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import java.nio.file.Files
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
import java.nio.file.StandardCopyOption
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
|
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class AddProductActivity : AppCompatActivity() {
|
class AddProductActivity : AppCompatActivity() {
|
||||||
private lateinit var imageView: ImageView
|
|
||||||
|
|
||||||
private lateinit var saveButton: Button
|
|
||||||
private lateinit var takePictureButton: Button
|
|
||||||
private lateinit var scanButton: Button
|
private lateinit var scanButton: Button
|
||||||
|
private lateinit var abstractProductView: AbstractProductView
|
||||||
|
private lateinit var expiryDateRadioButton: RadioButton
|
||||||
|
private lateinit var shelfLifeRadioButton: RadioButton
|
||||||
|
|
||||||
private lateinit var productNameText: TextView
|
private lateinit var expiryDateTextView: TextView
|
||||||
private lateinit var netWeightText: TextView
|
private lateinit var expiryDateSelectButton: Button
|
||||||
|
|
||||||
private lateinit var pictureFile: File
|
private lateinit var shelfLifeTextView: TextView
|
||||||
private lateinit var picturesPath: File
|
private lateinit var shelfLifeTextEdit: EditText
|
||||||
private lateinit var binding: ActivityAddProductBinding
|
|
||||||
|
private lateinit var amountTextEdit: EditText
|
||||||
|
|
||||||
|
private lateinit var dateOfProductionSelectButton: Button
|
||||||
|
private lateinit var saveProductButton: Button
|
||||||
|
|
||||||
|
private var expiryDateOverShelfLife: Boolean? = null
|
||||||
|
private var updatingExistentProduct = false
|
||||||
|
private var product: Product? = null
|
||||||
|
private var abstractProduct: AbstractProduct? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContentView(R.layout.fragment_add_product)
|
setContentView(R.layout.fragment_add_product)
|
||||||
|
|
||||||
|
scanButton = findViewById(R.id.scanButton)
|
||||||
|
abstractProductView = findViewById(R.id.abstractProductView)
|
||||||
|
expiryDateRadioButton = findViewById(R.id.expiryDateRadio)
|
||||||
|
shelfLifeRadioButton = findViewById(R.id.shelfLifeRadio)
|
||||||
|
|
||||||
picturesPath = File(filesDir, "pictures")
|
expiryDateTextView = findViewById(R.id.expiryDateTextView)
|
||||||
picturesPath.mkdirs()
|
expiryDateSelectButton = findViewById(R.id.selectExpiryDateButton)
|
||||||
// imageView = binding.includeContent.addProductLayout
|
|
||||||
imageView = findViewById(R.id.imageView)
|
|
||||||
|
|
||||||
saveButton = findViewById(R.id.saveButton)
|
shelfLifeTextView = findViewById(R.id.shelfLife)
|
||||||
takePictureButton = findViewById(R.id.takePictureButton)
|
shelfLifeTextEdit = findViewById(R.id.shelfLifeTextEdit)
|
||||||
scanButton = findViewById(R.id.scan_button)
|
|
||||||
|
|
||||||
productNameText = findViewById(R.id.productName)
|
amountTextEdit = findViewById(R.id.amountTextEdit)
|
||||||
netWeightText = findViewById(R.id.netWeight)
|
|
||||||
|
|
||||||
saveButton.setOnClickListener {
|
dateOfProductionSelectButton = findViewById(R.id.selectDateOfProductionButton)
|
||||||
val productName = productNameText.text.toString()
|
saveProductButton = findViewById(R.id.saveProductButton)
|
||||||
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 == "") {
|
val extras = intent.extras
|
||||||
Toast.makeText(this, "Please, write a name of a product!", Toast.LENGTH_SHORT).show()
|
product = extras!!.get("product") as Product?
|
||||||
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
|
if (product != null) {
|
||||||
Log.d("QWERTYUIOP", "Putting ${pictureFile.name}")
|
abstractProduct = DBStorageController(this).findAbstractProductById(DBStorageController(this).readableDatabase, product!!.abstractProductId)
|
||||||
val values = ContentValues().apply {
|
abstractProductView.abstractProduct = abstractProduct!!
|
||||||
put(ProductContract.ProductEntry.PRODUCT_NAME, productName)
|
expiryDateRadioButton.isSelected = true
|
||||||
put(ProductContract.ProductEntry.PRODUCT_NET_WEIGHT, netWeight.toString())
|
shelfLifeRadioButton.isSelected = false
|
||||||
put(ProductContract.ProductEntry.IMAGE_FILENAME, pictureFile.nameWithoutExtension)
|
|
||||||
}
|
|
||||||
|
|
||||||
db.insert(ProductContract.ProductEntry.TABLE_NAME, null, values)
|
expiryDateOverShelfLife = true
|
||||||
|
updatingExistentProduct = true
|
||||||
finish()
|
update()
|
||||||
}
|
} else {
|
||||||
|
product = Product(0, 0, 0, 0, 0)
|
||||||
takePictureButton.setOnClickListener {
|
abstractProduct = AbstractProduct(0, "", "", 0.0, "", 0, 0)
|
||||||
Log.d("QWERTYUIOP", "test")
|
|
||||||
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scanButton.setOnClickListener {
|
scanButton.setOnClickListener {
|
||||||
val options = GmsBarcodeScannerOptions.Builder()
|
requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
|
||||||
.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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// binding = ActivityAddProductBinding.inflate(layoutInflater)
|
expiryDateRadioButton.setOnClickListener {
|
||||||
|
expiryDateOverShelfLife = true
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
// setContentView(binding.root)
|
shelfLifeRadioButton.setOnClickListener {
|
||||||
|
expiryDateOverShelfLife = false
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
dateOfProductionSelectButton.setOnClickListener {
|
||||||
|
val c = Calendar.getInstance()
|
||||||
|
val year = c.get(Calendar.YEAR)
|
||||||
|
val month = c.get(Calendar.MONTH)
|
||||||
|
val day = c.get(Calendar.DAY_OF_MONTH)
|
||||||
|
|
||||||
|
val dpd = DatePickerDialog(this, { _, y, m, d ->
|
||||||
|
product!!.dateOfProduction = SimpleDateFormat("dd.MM.yyyy").parse("$d.${m+1}.$y")!!.time / 1000
|
||||||
|
update()
|
||||||
|
}, year, month, day)
|
||||||
|
|
||||||
|
dpd.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
expiryDateSelectButton.setOnClickListener {
|
||||||
|
val c = Calendar.getInstance()
|
||||||
|
val year = c.get(Calendar.YEAR)
|
||||||
|
val month = c.get(Calendar.MONTH)
|
||||||
|
val day = c.get(Calendar.DAY_OF_MONTH)
|
||||||
|
|
||||||
|
val dpd = DatePickerDialog(this, { _, y, m, d ->
|
||||||
|
product!!.dateOfExpiry = SimpleDateFormat("dd.MM.yyyy").parse("$d.${m+1}.$y")!!.time / 1000
|
||||||
|
update()
|
||||||
|
}, year, month, day)
|
||||||
|
|
||||||
|
dpd.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
amountTextEdit.addTextChangedListener {
|
||||||
|
val text = amountTextEdit.text.toString()
|
||||||
|
if (text == "") return@addTextChangedListener
|
||||||
|
product!!.amount = text.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
saveProductButton.setOnClickListener {
|
||||||
|
if (expiryDateOverShelfLife == null) {
|
||||||
|
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, getString(R.string.date_of_production_request), Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expiryDateOverShelfLife!!) {
|
||||||
|
if (shelfLifeTextEdit.text.toString() != "") {
|
||||||
|
evaluateDateOfExpiry(shelfLifeTextEdit.text.toString().toInt())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (product!!.dateOfExpiry == 0.toLong()) {
|
||||||
|
Toast.makeText(this, getString(R.string.expiry_date_request), Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (product!!.amount == 0) {
|
||||||
|
Toast.makeText(this, getString(R.string.product_amount_request), Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatingExistentProduct) {
|
||||||
|
DBStorageController(this).updateProduct(DBStorageController(this).writableDatabase, product!!)
|
||||||
|
} else {
|
||||||
|
DBStorageController(this).insertNewProduct(DBStorageController(this).writableDatabase, product!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@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")
|
private fun update () {
|
||||||
Files.move(tempfile.toPath(), pictureFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
if (expiryDateOverShelfLife == true) {
|
||||||
tempfile.delete()
|
expiryDateTextView.visibility = View.VISIBLE
|
||||||
generateThumbnailForImage(this, imageHash)
|
expiryDateSelectButton.visibility = View.VISIBLE
|
||||||
|
|
||||||
imageView.setImageURI(getImageUri(this, pictureFile))
|
shelfLifeTextEdit.visibility = View.INVISIBLE
|
||||||
Log.i("QWERTYUIOP", "Picture saved")
|
shelfLifeTextView.visibility = View.INVISIBLE
|
||||||
|
} else if (expiryDateOverShelfLife == false){
|
||||||
|
expiryDateTextView.visibility = View.INVISIBLE
|
||||||
|
expiryDateSelectButton.visibility = View.INVISIBLE
|
||||||
|
|
||||||
|
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 = "${getString(R.string.date_of_production)}: $dateOfProductionParsed"
|
||||||
|
|
||||||
|
val expiryDateParsed = SimpleDateFormat("dd.MM.yyyy").format(Date(product!!.dateOfExpiry * 1000))
|
||||||
|
expiryDateTextView.text = "${getString(R.string.expiry_date)}: $expiryDateParsed"
|
||||||
|
|
||||||
|
if (amountTextEdit.text.toString() == "") {
|
||||||
|
amountTextEdit.setText(product!!.amount.toString())
|
||||||
} else {
|
} else {
|
||||||
Log.e("QWERTYUIOP", "Cannot save a picture")
|
product!!.amount = amountTextEdit.text.toString().toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstractProductView.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.R)
|
private fun evaluateDateOfExpiry(shelfLifeMonths: Int) {
|
||||||
fun getPicture () {
|
if (product!!.dateOfProduction == 0.toLong() ) {
|
||||||
//Saving picture to a temp file for further hash calculation and moving to a proper directory
|
return
|
||||||
val imageFile = File(this.filesDir, "image.png")
|
}
|
||||||
val imageUri = getImageUri(this, imageFile)
|
|
||||||
takePicture.launch(imageUri)
|
val c = Calendar.getInstance()
|
||||||
|
c.timeInMillis = product!!.dateOfProduction * 1000
|
||||||
|
|
||||||
|
c.add(Calendar.MONTH, shelfLifeMonths)
|
||||||
|
product!!.dateOfExpiry = c.timeInMillis / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun displayAbstractProduct(abstractProduct: AbstractProduct) {
|
||||||
|
abstractProductView.abstractProduct = abstractProduct
|
||||||
|
abstractProductView.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.R)
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
@@ -146,9 +217,34 @@ class AddProductActivity : AppCompatActivity() {
|
|||||||
ActivityResultContracts.RequestPermission()
|
ActivityResultContracts.RequestPermission()
|
||||||
) { isGranted: Boolean ->
|
) { isGranted: Boolean ->
|
||||||
if (isGranted) {
|
if (isGranted) {
|
||||||
getPicture()
|
prepareBarcodeScanner()
|
||||||
} 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_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!!)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,14 +28,12 @@ class FullscreenActivity : Activity() {
|
|||||||
fullscreenImageView = findViewById(R.id.fullscreenImageView)
|
fullscreenImageView = findViewById(R.id.fullscreenImageView)
|
||||||
|
|
||||||
val extras = intent.extras
|
val extras = intent.extras
|
||||||
val imageHash = extras!!.get("imagehash") as String? //extras!!.getParcelable<Parcelable>("imagehash") as String?
|
val imageHash = extras!!.get("imagehash") as String?
|
||||||
|
|
||||||
|
|
||||||
val picturesDir = File(filesDir, "pictures")
|
val picturesDir = File(filesDir, "pictures")
|
||||||
picturesDir.mkdirs()
|
picturesDir.mkdirs()
|
||||||
Log.d("QWERTYUIOP", imageHash.toString())
|
|
||||||
val fullscreenImageFile = File(picturesDir, "$imageHash.png")
|
val fullscreenImageFile = File(picturesDir, "$imageHash.png")
|
||||||
// crash. change activity
|
|
||||||
fullscreenImageView.setImageURI(getImageUri(this, fullscreenImageFile))
|
fullscreenImageView.setImageURI(getImageUri(this, fullscreenImageFile))
|
||||||
fullscreenImageView.setOnClickListener {
|
fullscreenImageView.setOnClickListener {
|
||||||
this.finish()
|
this.finish()
|
||||||
|
|||||||
@@ -1,29 +1,26 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees.activities
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.ImageView
|
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 androidx.core.view.children
|
|
||||||
import androidx.gridlayout.widget.GridLayout
|
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import org.foxarmy.barcodescannerforemployees.ViewPagerAdapter
|
import org.foxarmy.barcodescannerforemployees.ViewPagerAdapter
|
||||||
import org.foxarmy.barcodescannerforemployees.databinding.ActivityMainBinding
|
import org.foxarmy.barcodescannerforemployees.databinding.ActivityMainBinding
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
||||||
import org.foxarmy.barcodescannerforemployees.fragments.CategoriesFragment
|
import org.foxarmy.barcodescannerforemployees.fragments.CategoriesFragment
|
||||||
|
import org.foxarmy.barcodescannerforemployees.fragments.ShelfFragment
|
||||||
import org.foxarmy.barcodescannerforemployees.fragments.StorageFragment
|
import org.foxarmy.barcodescannerforemployees.fragments.StorageFragment
|
||||||
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
// private lateinit var appBarConfiguration: AppBarConfiguration
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
public lateinit var adapter: ViewPagerAdapter
|
lateinit var adapter: ViewPagerAdapter
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -35,23 +32,47 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setupViewPager(binding.tabViewpager)
|
setupViewPager(binding.tabViewpager)
|
||||||
binding.tabTablayout.setupWithViewPager(binding.tabViewpager)
|
binding.tabTablayout.setupWithViewPager(binding.tabViewpager)
|
||||||
|
|
||||||
// val navController = findNavController(R.id.nav_host_fragment_content_storage)
|
binding.newElementFab.setOnClickListener { view ->
|
||||||
// appBarConfiguration = AppBarConfiguration(navController.graph)
|
val currentPosition = binding.tabTablayout.selectedTabPosition
|
||||||
// setupActionBarWithNavController(navController, appBarConfiguration)
|
val fragment = adapter.getItem(currentPosition)
|
||||||
|
|
||||||
binding.addProductFab.setOnClickListener { view ->
|
when (fragment::class.simpleName.toString()) {
|
||||||
val addProductIntent = Intent(this, AddProductActivity::class.java)
|
"StorageFragment" -> {
|
||||||
val extras = Bundle()
|
val addAbstractProductIntent = Intent(this, AddAbstractProductActivity::class.java)
|
||||||
ContextCompat.startActivity(this, addProductIntent, extras)
|
val extras = Bundle()
|
||||||
|
// 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)
|
||||||
|
addAbstractProductIntent.putExtras(extras)
|
||||||
|
ContextCompat.startActivity(this, addAbstractProductIntent, extras)
|
||||||
|
}
|
||||||
|
|
||||||
|
"CategoriesFragment" -> {
|
||||||
|
val addCategoryIntent = Intent(this, AddCategoryActivity::class.java)
|
||||||
|
val extras = Bundle()
|
||||||
|
extras.putParcelable("category", Category(0, "New category"))
|
||||||
|
addCategoryIntent.putExtras(extras)
|
||||||
|
ContextCompat.startActivity(this, addCategoryIntent, extras)
|
||||||
|
}
|
||||||
|
|
||||||
|
"ShelfFragment" -> {
|
||||||
|
val addProductIntent = Intent(this, AddProductActivity::class.java)
|
||||||
|
val extras = Bundle()
|
||||||
|
extras.putParcelable("product", null)
|
||||||
|
addProductIntent.putExtras(extras)
|
||||||
|
ContextCompat.startActivity(this, addProductIntent, extras)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupViewPager(viewpager: ViewPager) {
|
private fun setupViewPager(viewpager: ViewPager) {
|
||||||
adapter = ViewPagerAdapter(supportFragmentManager)
|
adapter = ViewPagerAdapter(supportFragmentManager)
|
||||||
|
|
||||||
adapter.addFragment(CategoriesFragment(), "Categories")
|
adapter.addFragment(CategoriesFragment(), getString(R.string.categories_title))
|
||||||
adapter.addFragment(StorageFragment(), "Storage")
|
adapter.addFragment(StorageFragment(), getString(R.string.storage_title))
|
||||||
//TODO: shelf and settings fragments
|
adapter.addFragment(ShelfFragment(), getString(R.string.shelf_title))
|
||||||
|
//TODO: settings fragments
|
||||||
|
|
||||||
// setting adapter to view pager.
|
// setting adapter to view pager.
|
||||||
viewpager.setAdapter(adapter)
|
viewpager.setAdapter(adapter)
|
||||||
@@ -63,43 +84,71 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
val currentPosition = binding.tabTablayout.selectedTabPosition
|
||||||
|
val fragment = adapter.getItem(currentPosition)
|
||||||
|
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
|
|
||||||
|
|
||||||
R.id.action_settings -> {
|
R.id.action_settings -> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_delete -> {
|
R.id.action_delete -> {
|
||||||
// if there's no selected items, write a toast about it, otherwise delete those items.
|
|
||||||
val currentPosition = binding.tabTablayout.selectedTabPosition
|
|
||||||
val fragment = adapter.getItem(currentPosition)
|
|
||||||
|
|
||||||
when (fragment::class.simpleName.toString()) {
|
when (fragment::class.simpleName.toString()) {
|
||||||
"StorageFragment" -> {
|
"StorageFragment" -> {
|
||||||
val storageFragment = fragment as StorageFragment
|
AlertDialog.Builder(this)
|
||||||
storageFragment.removeSelected()
|
.setMessage(getString(R.string.deleting_abstract_product_warning))
|
||||||
// storageFragment.updateContent()
|
.setPositiveButton(getString(R.string.yes)) { _: DialogInterface, _: Int ->
|
||||||
|
val storageFragment = fragment as StorageFragment
|
||||||
|
storageFragment.removeSelected()
|
||||||
|
}
|
||||||
|
.setNegativeButton(getString(R.string.no)) { _: DialogInterface, _: Int ->
|
||||||
|
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
"CategoriesFragment" -> {
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setMessage(getString(R.string.deleting_category_warning))
|
||||||
|
.setPositiveButton(getString(R.string.yes)) { _: DialogInterface, _: Int ->
|
||||||
|
val categoriesFragment = fragment as CategoriesFragment
|
||||||
|
categoriesFragment.removeSelected()
|
||||||
|
}
|
||||||
|
.setNegativeButton(getString(R.string.no)) { _: DialogInterface, _: Int ->
|
||||||
|
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
"ShelfFragment" -> {
|
||||||
|
val shelfFragment = fragment as ShelfFragment
|
||||||
|
shelfFragment.removeSelected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// val storageFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_content_storage)?.childFragmentManager!!.fragments[0] as StorageFragment
|
|
||||||
// val amountOfViews = storageFragment.view?.findViewById<GridLayout>(R.id.contentGridLayout)?.childCount
|
|
||||||
//
|
|
||||||
// for (view: AbstractProductView in storageFragment.view?.findViewById<GridLayout>(R.id.contentGridLayout)?.children!!.iterator() as Iterator<AbstractProductView>) {
|
|
||||||
// val db = DBStorageController(this)
|
|
||||||
// if (view.isProductSelected) {
|
|
||||||
// db.eraseAbstractProduct(db.writableDatabase , view.product.id, this)
|
|
||||||
// Log.d("QWERTYUIOP", "Removing ${view.product.id}")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// storageFragment.updateContent()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R.id.action_update -> {
|
||||||
|
when (fragment::class.simpleName.toString()) {
|
||||||
|
"StorageFragment" -> {
|
||||||
|
val storageFragment = fragment as StorageFragment
|
||||||
|
storageFragment.updateSelected()
|
||||||
|
}
|
||||||
|
|
||||||
|
"CategoriesFragment" -> {
|
||||||
|
val categoriesFragment = fragment as CategoriesFragment
|
||||||
|
categoriesFragment.updateSelected()
|
||||||
|
}
|
||||||
|
|
||||||
|
"ShelfFragment" -> {
|
||||||
|
val shelfFragment = fragment as ShelfFragment
|
||||||
|
shelfFragment.updateSelected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// override fun onSupportNavigateUp(): Boolean {
|
|
||||||
// val navController = findNavController(R.id.nav_host_fragment_content_storage)
|
|
||||||
// return navController.navigateUp(appBarConfiguration)
|
|
||||||
// || super.onSupportNavigateUp()
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package org.foxarmy.barcodescannerforemployees.dataclasses
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
|
||||||
|
class AbstractProduct() : Parcelable {
|
||||||
|
var id: Int = 0
|
||||||
|
var barcode: String = ""
|
||||||
|
var name: String = ""
|
||||||
|
var netWeight: Double = 0.0
|
||||||
|
var imageHash: String = ""
|
||||||
|
var category: Int = 0
|
||||||
|
var unit: Int = 0
|
||||||
|
|
||||||
|
constructor(id: Int, barcode: String, name: String, netWeight: Double, imageHash: String, category: Int, unit: Int) : this() {
|
||||||
|
this.id = id
|
||||||
|
this.barcode = barcode
|
||||||
|
this.name = name
|
||||||
|
this.netWeight = netWeight
|
||||||
|
this.imageHash = imageHash
|
||||||
|
this.category = category
|
||||||
|
this.unit = unit
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(parcel: Parcel) : this() {
|
||||||
|
id = parcel.readInt()
|
||||||
|
barcode = parcel.readString()!!
|
||||||
|
name = parcel.readString()!!
|
||||||
|
netWeight = parcel.readDouble()
|
||||||
|
imageHash = parcel.readString()!!
|
||||||
|
category = parcel.readInt()
|
||||||
|
unit = parcel.readInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
parcel.writeInt(id)
|
||||||
|
parcel.writeString(barcode)
|
||||||
|
parcel.writeString(name)
|
||||||
|
parcel.writeDouble(netWeight)
|
||||||
|
parcel.writeString(imageHash)
|
||||||
|
parcel.writeInt(category)
|
||||||
|
parcel.writeInt(unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Parcelable.Creator<AbstractProduct> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): AbstractProduct {
|
||||||
|
return AbstractProduct(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<AbstractProduct?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package org.foxarmy.barcodescannerforemployees.dataclasses
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
|
||||||
|
class Category() : Parcelable {
|
||||||
|
var id = 0
|
||||||
|
var name = ""
|
||||||
|
|
||||||
|
|
||||||
|
constructor(id: Int, name: String) : this() {
|
||||||
|
this.id = id
|
||||||
|
this.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(parcel: Parcel) : this() {
|
||||||
|
id = parcel.readInt()
|
||||||
|
name = parcel.readString()!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
parcel.writeInt(id)
|
||||||
|
parcel.writeString(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Parcelable.Creator<Category> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): Category {
|
||||||
|
return Category(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<Category?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package org.foxarmy.barcodescannerforemployees.dataclasses
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness
|
||||||
|
|
||||||
|
class Product() : Parcelable {
|
||||||
|
var id = 0
|
||||||
|
var abstractProductId = 0
|
||||||
|
var amount = 0
|
||||||
|
var dateOfProduction: Long = 0
|
||||||
|
var dateOfExpiry: Long = 0
|
||||||
|
var freshness: Double = 0.0
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
constructor(id: Int, abstractProductId: Int, amount: Int, dateOfProduction: Long, dateOfExpiry: Long) : this() {
|
||||||
|
this.id = id
|
||||||
|
this.abstractProductId = abstractProductId
|
||||||
|
this.amount = amount
|
||||||
|
this.dateOfProduction = dateOfProduction
|
||||||
|
this.dateOfExpiry = dateOfExpiry
|
||||||
|
this.freshness = calculateProductFreshness(dateOfProduction, dateOfExpiry)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(parcel: Parcel) : this() {
|
||||||
|
id = parcel.readInt()
|
||||||
|
abstractProductId = parcel.readInt()
|
||||||
|
amount = parcel.readInt()
|
||||||
|
dateOfProduction = parcel.readLong()
|
||||||
|
dateOfExpiry = parcel.readLong()
|
||||||
|
freshness = parcel.readDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
parcel.writeInt(id)
|
||||||
|
parcel.writeInt(abstractProductId)
|
||||||
|
parcel.writeInt(amount)
|
||||||
|
parcel.writeLong(dateOfProduction)
|
||||||
|
parcel.writeLong(dateOfExpiry)
|
||||||
|
parcel.writeDouble(freshness)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Parcelable.Creator<Product> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): Product {
|
||||||
|
return Product(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<Product?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.databinding.FragmentAddAbstractProductBinding
|
||||||
|
|
||||||
|
class AddAbstractProductFragment : Fragment() {
|
||||||
|
private lateinit var binding: FragmentAddAbstractProductBinding
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
binding = FragmentAddAbstractProductBinding.inflate(layoutInflater)
|
||||||
|
return inflater.inflate(R.layout.fragment_add_abstract_product, container, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +1,20 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees.fragments
|
package org.foxarmy.barcodescannerforemployees.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
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 androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import org.foxarmy.barcodescannerforemployees.databinding.FragmentAddProductBinding
|
import org.foxarmy.barcodescannerforemployees.databinding.FragmentAddProductBinding
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
|
|
||||||
class AddProductFragment : Fragment() {
|
class AddProductFragment : Fragment() {
|
||||||
|
|
||||||
|
|
||||||
private lateinit var binding: FragmentAddProductBinding
|
private lateinit var binding: FragmentAddProductBinding
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Log.d("QWERTYUIOP", "хуета1")
|
|
||||||
binding = FragmentAddProductBinding.inflate(layoutInflater)
|
binding = FragmentAddProductBinding.inflate(layoutInflater)
|
||||||
return inflater.inflate(R.layout.fragment_add_product, container, false)
|
return inflater.inflate(R.layout.fragment_add_product, container, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +1,97 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees.fragments
|
package org.foxarmy.barcodescannerforemployees.fragments
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.BaseColumns
|
||||||
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.LinearLayout
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.children
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import org.foxarmy.barcodescannerforemployees.CategoriesContract
|
||||||
|
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.activities.AddCategoryActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
||||||
|
import org.foxarmy.barcodescannerforemployees.views.CategoryView
|
||||||
|
|
||||||
class CategoriesFragment : Fragment() {
|
class CategoriesFragment : Fragment() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
// Inflate the layout for this fragment
|
|
||||||
return inflater.inflate(R.layout.fragment_categories, container, false)
|
return inflater.inflate(R.layout.fragment_categories, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
// updateContent()
|
updateContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
// public fun updateContent() {
|
fun removeSelected() {
|
||||||
//
|
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
|
||||||
// val grv = getView()?.findViewById<GridLayout>(R.id.contentGridLayout)
|
|
||||||
// grv?.removeAllViews()
|
val db = DBStorageController(requireContext())
|
||||||
//
|
var deleted = false
|
||||||
// val db = DBStorageController(requireContext()).readableDatabase
|
for (view: CategoryView in layout?.children!!.iterator() as Iterator<CategoryView>) {
|
||||||
// val projection = arrayOf(
|
if (view.isCategorySelected) {
|
||||||
// BaseColumns._ID,
|
db.eraseCategory(db.writableDatabase, view.category.id, requireContext())
|
||||||
// ProductContract.ProductEntry.PRODUCT_NAME,
|
deleted = true
|
||||||
// ProductContract.ProductEntry.PRODUCT_NET_WEIGHT,
|
}
|
||||||
// ProductContract.ProductEntry.IMAGE_FILENAME
|
}
|
||||||
// )
|
|
||||||
//
|
if (!deleted) {
|
||||||
// val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, null, null, null, null)
|
Toast.makeText(requireContext(), getString(R.string.nothing_to_delete), Toast.LENGTH_SHORT).show()
|
||||||
//
|
}
|
||||||
// with (cursor) {
|
updateContent()
|
||||||
// while(moveToNext()) {
|
}
|
||||||
// val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
|
|
||||||
// val productName = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.PRODUCT_NAME))
|
fun updateSelected() {
|
||||||
// val netWeight = getDouble(getColumnIndexOrThrow(ProductContract.ProductEntry.PRODUCT_NET_WEIGHT))
|
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
|
||||||
// val productImageHash = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.IMAGE_FILENAME))
|
for (view: CategoryView in layout?.children!!.iterator() as Iterator<CategoryView>) {
|
||||||
//
|
if (view.isCategorySelected) {
|
||||||
// val product = AbstractProduct(productId, productName, netWeight, productImageHash, 1)
|
val addCategoryIntent = Intent(context, AddCategoryActivity::class.java)
|
||||||
//
|
val extras = Bundle()
|
||||||
// generateThumbnailForImage(context!!, productImageHash)
|
extras.putParcelable("category", view.category)
|
||||||
//
|
addCategoryIntent.putExtras(extras)
|
||||||
// val abstractProduct = AbstractProductView(
|
ContextCompat.startActivity(context!!, addCategoryIntent, extras)
|
||||||
// requireActivity(),
|
}
|
||||||
// requireContext(),
|
}
|
||||||
// product
|
}
|
||||||
// )
|
|
||||||
// grv?.addView(abstractProduct)
|
fun updateContent() {
|
||||||
// }
|
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
|
||||||
// }
|
layout?.removeAllViews()
|
||||||
// }
|
|
||||||
|
val db = DBStorageController(requireContext()).readableDatabase
|
||||||
|
|
||||||
|
val projection = arrayOf(
|
||||||
|
BaseColumns._ID,
|
||||||
|
CategoriesContract.CategoryEntry.CATEGORY_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
val cursor = db.query(CategoriesContract.CategoryEntry.TABLE_NAME, projection, null, null, null, null, null)
|
||||||
|
|
||||||
|
with (cursor) {
|
||||||
|
while(moveToNext()) {
|
||||||
|
val categoryId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
|
||||||
|
val categoryName = getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME))
|
||||||
|
|
||||||
|
val category = Category(categoryId, categoryName)
|
||||||
|
|
||||||
|
val categoryView = CategoryView(requireActivity(), requireContext(), category)
|
||||||
|
layout?.addView(categoryView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
// updateContent()
|
updateContent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
package org.foxarmy.barcodescannerforemployees.fragments
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.BaseColumns
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.AdapterView
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.children
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.gridlayout.widget.GridLayout
|
||||||
|
import org.foxarmy.barcodescannerforemployees.AbstractProductContract
|
||||||
|
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
||||||
|
import org.foxarmy.barcodescannerforemployees.ProductContract
|
||||||
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.activities.AddProductActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.databinding.FragmentShelfBinding
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
|
import org.foxarmy.barcodescannerforemployees.views.ProductView
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
class ShelfFragment : Fragment() {
|
||||||
|
|
||||||
|
private lateinit var binding: FragmentShelfBinding
|
||||||
|
private var updateInProgress = false
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
binding = FragmentShelfBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
fillUpSortBySpinner()
|
||||||
|
|
||||||
|
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
|
updateContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
|
updateContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContent()
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSelected() {
|
||||||
|
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
|
||||||
|
|
||||||
|
var updated = false
|
||||||
|
for (view: ProductView in grv?.children!!.iterator() as Iterator<ProductView>) {
|
||||||
|
if (view.isProductSelected) {
|
||||||
|
val addProductIntent = Intent(requireContext(), AddProductActivity::class.java)
|
||||||
|
val extras = Bundle()
|
||||||
|
extras.putParcelable("product", view.product)
|
||||||
|
addProductIntent.putExtras(extras)
|
||||||
|
ContextCompat.startActivity(requireContext(), addProductIntent, extras)
|
||||||
|
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!updated) {
|
||||||
|
Toast.makeText(requireContext(), getString(R.string.nothing_to_update), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
updateContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillUpSortBySpinner() {
|
||||||
|
val sorts = resources.getStringArray(R.array.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
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
|
||||||
|
updateContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateContent() {
|
||||||
|
thread {
|
||||||
|
if (updateInProgress) return@thread
|
||||||
|
updateInProgress = true
|
||||||
|
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
|
||||||
|
activity!!.runOnUiThread {
|
||||||
|
grv?.removeAllViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
val db = DBStorageController(requireContext()).readableDatabase
|
||||||
|
val projection = arrayOf(
|
||||||
|
BaseColumns._ID,
|
||||||
|
ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID,
|
||||||
|
ProductContract.ProductEntry.AMOUNT,
|
||||||
|
ProductContract.ProductEntry.DATE_OF_PRODUCTION,
|
||||||
|
ProductContract.ProductEntry.EXPIRY_DATE,
|
||||||
|
)
|
||||||
|
|
||||||
|
var orderBy: String = ""
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> {
|
||||||
|
orderBy =
|
||||||
|
"(SELECT ${AbstractProductContract.AbstractProductEntry.CATEGORY} FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID} = ${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID}) ASC"
|
||||||
|
}
|
||||||
|
// Wow, I wrote this on first try but unfortunately SQLite can't do that :(
|
||||||
|
// I'll leave it here as a memory.
|
||||||
|
// "Freshness" -> {
|
||||||
|
// orderBy =
|
||||||
|
// "(SELECT ( (julianday(${ProductContract.ProductEntry.EXPIRY_DATE}) - julianday('now', 'localtime')) / (julianday(${ProductContract.ProductEntry.EXPIRY_DATE}) - julianday(${ProductContract.ProductEntry.DATE_OF_PRODUCTION})) )) ASC"
|
||||||
|
// }
|
||||||
|
|
||||||
|
3 -> {
|
||||||
|
orderBy = "${ProductContract.ProductEntry.DATE_OF_PRODUCTION} ASC"
|
||||||
|
}
|
||||||
|
|
||||||
|
4 -> {
|
||||||
|
orderBy = "${ProductContract.ProductEntry.EXPIRY_DATE} ASC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, null, null, null, orderBy)
|
||||||
|
|
||||||
|
val products = mutableListOf<Product>()
|
||||||
|
|
||||||
|
with(cursor) {
|
||||||
|
while (moveToNext()) {
|
||||||
|
val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
|
||||||
|
val abstractProductId =
|
||||||
|
getInt(getColumnIndexOrThrow(ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID))
|
||||||
|
val amount = getInt(getColumnIndexOrThrow(ProductContract.ProductEntry.AMOUNT))
|
||||||
|
val dateOfProduction =
|
||||||
|
getLong(getColumnIndexOrThrow(ProductContract.ProductEntry.DATE_OF_PRODUCTION))
|
||||||
|
val dateOfExpiry = getLong(getColumnIndexOrThrow(ProductContract.ProductEntry.EXPIRY_DATE))
|
||||||
|
|
||||||
|
val product = Product(productId, abstractProductId, amount, dateOfProduction, dateOfExpiry)
|
||||||
|
|
||||||
|
if (binding.spinner.selectedItemPosition == 2) { //freshness
|
||||||
|
products.add(product)
|
||||||
|
} else {
|
||||||
|
val productView = ProductView(
|
||||||
|
requireActivity(),
|
||||||
|
requireContext(),
|
||||||
|
product
|
||||||
|
)
|
||||||
|
activity!!.runOnUiThread {
|
||||||
|
grv?.addView(productView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
products.sortWith(compareByDescending { it.freshness })
|
||||||
|
|
||||||
|
for (product in products.iterator()) {
|
||||||
|
val productView = ProductView(
|
||||||
|
requireActivity(),
|
||||||
|
requireContext(),
|
||||||
|
product
|
||||||
|
)
|
||||||
|
activity!!.runOnUiThread {
|
||||||
|
grv?.addView(productView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateInProgress = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeSelected() {
|
||||||
|
thread {
|
||||||
|
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
|
||||||
|
|
||||||
|
val db = DBStorageController(requireContext())
|
||||||
|
var deleted = false
|
||||||
|
for (view: ProductView in grv?.children!!.iterator() as Iterator<ProductView>) {
|
||||||
|
activity!!.runOnUiThread {
|
||||||
|
view.findViewById<ImageView>(R.id.productPicture).setImageURI(null)
|
||||||
|
}
|
||||||
|
if (view.isProductSelected) {
|
||||||
|
db.eraseProduct(db.writableDatabase, view.product.id)
|
||||||
|
deleted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deleted) {
|
||||||
|
activity!!.runOnUiThread {
|
||||||
|
Toast.makeText(requireContext(), getString(R.string.nothing_to_delete), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,37 +1,51 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees.fragments
|
package org.foxarmy.barcodescannerforemployees.fragments
|
||||||
|
|
||||||
|
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.ArrayAdapter
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.Toast
|
||||||
|
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
|
||||||
import androidx.gridlayout.widget.GridLayout
|
import org.foxarmy.barcodescannerforemployees.AbstractProductContract
|
||||||
import org.foxarmy.barcodescannerforemployees.*
|
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
||||||
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.activities.AddAbstractProductActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.databinding.FragmentStorageBinding
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
|
import org.foxarmy.barcodescannerforemployees.generateThumbnailForImage
|
||||||
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
|
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple [Fragment] subclass.
|
|
||||||
* Use the [StorageFragment.newInstance] factory method to
|
|
||||||
* create an instance of this fragment.
|
|
||||||
*/
|
|
||||||
class StorageFragment : Fragment() {
|
class StorageFragment : Fragment() {
|
||||||
|
|
||||||
public var test_value = 1
|
private lateinit var binding: FragmentStorageBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
// Inflate the layout for this fragment
|
binding = FragmentStorageBinding.inflate(inflater)
|
||||||
return inflater.inflate(R.layout.fragment_storage, container, false)
|
|
||||||
|
fillUpSortBySpinner()
|
||||||
|
|
||||||
|
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
|
updateContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
|
updateContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
@@ -40,56 +54,108 @@ class StorageFragment : Fragment() {
|
|||||||
updateContent()
|
updateContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fillUpSortBySpinner() {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
fun removeSelected() {
|
fun removeSelected() {
|
||||||
val grv = getView()?.findViewById<GridLayout>(R.id.contentGridLayout)
|
thread {
|
||||||
|
val grv = binding.contentGridLayout
|
||||||
|
|
||||||
val db = DBStorageController(requireContext())
|
val db = DBStorageController(requireContext())
|
||||||
|
var deleted = false
|
||||||
|
for (view: AbstractProductView in grv.children.iterator() as Iterator<AbstractProductView>) {
|
||||||
|
activity!!.runOnUiThread {
|
||||||
|
view.findViewById<ImageView>(R.id.productPicture).setImageURI(null)
|
||||||
|
}
|
||||||
|
if (view.isProductSelected) {
|
||||||
|
db.eraseAbstractProduct(db.writableDatabase, view.abstractProduct.id, requireContext())
|
||||||
|
deleted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (view: AbstractProductView in grv?.children!!.iterator() as Iterator<AbstractProductView>) {
|
if (!deleted) {
|
||||||
view.findViewById<ImageView>(R.id.productPicture).setImageURI(null)
|
activity!!.runOnUiThread {
|
||||||
|
Toast.makeText(requireContext(), getString(R.string.nothing_to_delete), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSelected() {
|
||||||
|
val grv = binding.contentGridLayout
|
||||||
|
|
||||||
|
for (view: AbstractProductView in grv.children.iterator() as Iterator<AbstractProductView>) {
|
||||||
if (view.isProductSelected) {
|
if (view.isProductSelected) {
|
||||||
db.eraseAbstractProduct(db.writableDatabase, view.product.id, requireContext())
|
val addProductIntent = Intent(requireContext(), AddAbstractProductActivity::class.java)
|
||||||
Log.d("QWERTYUIOP", "Removing ${view.product.id}")
|
val extras = Bundle()
|
||||||
} else {
|
extras.putParcelable("abstractProduct", view.abstractProduct)
|
||||||
Log.d("QWERTYUIOP", "Not ${view.product.id}")
|
addProductIntent.putExtras(extras)
|
||||||
|
ContextCompat.startActivity(requireContext(), addProductIntent, extras)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateContent()
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun updateContent() {
|
fun updateContent() {
|
||||||
|
thread {
|
||||||
val grv = getView()?.findViewById<GridLayout>(R.id.contentGridLayout)
|
val grv = binding.contentGridLayout
|
||||||
grv?.removeAllViews()
|
activity!!.runOnUiThread{
|
||||||
|
grv.removeAllViews()
|
||||||
val db = DBStorageController(requireContext()).readableDatabase
|
|
||||||
val projection = arrayOf(BaseColumns._ID,
|
|
||||||
ProductContract.ProductEntry.PRODUCT_NAME,
|
|
||||||
ProductContract.ProductEntry.PRODUCT_NET_WEIGHT,
|
|
||||||
ProductContract.ProductEntry.IMAGE_FILENAME
|
|
||||||
)
|
|
||||||
|
|
||||||
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, null, null, null, null)
|
|
||||||
|
|
||||||
with (cursor) {
|
|
||||||
while(moveToNext()) {
|
|
||||||
val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
|
|
||||||
val productName = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.PRODUCT_NAME))
|
|
||||||
val netWeight = getDouble(getColumnIndexOrThrow(ProductContract.ProductEntry.PRODUCT_NET_WEIGHT))
|
|
||||||
val productImageHash = getString(getColumnIndexOrThrow(ProductContract.ProductEntry.IMAGE_FILENAME))
|
|
||||||
|
|
||||||
val product = AbstractProduct(productId, productName, netWeight, productImageHash, 1)
|
|
||||||
|
|
||||||
generateThumbnailForImage(context!!, productImageHash)
|
|
||||||
|
|
||||||
val abstractProduct = AbstractProductView(
|
|
||||||
requireActivity(),
|
|
||||||
requireContext(),
|
|
||||||
product
|
|
||||||
)
|
|
||||||
grv?.addView(abstractProduct)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
val db = DBStorageController(requireContext()).readableDatabase
|
||||||
|
val projection = arrayOf(
|
||||||
|
BaseColumns._ID,
|
||||||
|
AbstractProductContract.AbstractProductEntry.BARCODE,
|
||||||
|
AbstractProductContract.AbstractProductEntry.PRODUCT_NAME,
|
||||||
|
AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT,
|
||||||
|
AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME,
|
||||||
|
AbstractProductContract.AbstractProductEntry.CATEGORY,
|
||||||
|
AbstractProductContract.AbstractProductEntry.UNIT
|
||||||
|
)
|
||||||
|
|
||||||
|
var orderBy: String = ""
|
||||||
|
when(binding.spinner.selectedItemPosition) {
|
||||||
|
0 -> {
|
||||||
|
orderBy = "${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} ASC"
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
orderBy = "${AbstractProductContract.AbstractProductEntry.CATEGORY} ASC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, null, null, null, null, orderBy)
|
||||||
|
|
||||||
|
with (cursor) {
|
||||||
|
while(moveToNext()) {
|
||||||
|
val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
|
||||||
|
val barcode = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.BARCODE))
|
||||||
|
val productName = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME))
|
||||||
|
val netWeight = getDouble(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT))
|
||||||
|
val productImageHash = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME))
|
||||||
|
val category = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.CATEGORY))
|
||||||
|
val unit = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.UNIT))
|
||||||
|
|
||||||
|
val product = AbstractProduct(productId, barcode, productName, netWeight, productImageHash, category, unit)
|
||||||
|
|
||||||
|
generateThumbnailForImage(context!!, productImageHash)
|
||||||
|
|
||||||
|
val abstractProduct = AbstractProductView(
|
||||||
|
requireActivity(),
|
||||||
|
requireContext(),
|
||||||
|
product
|
||||||
|
)
|
||||||
|
|
||||||
|
activity!!.runOnUiThread{
|
||||||
|
grv.addView(abstractProduct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
package org.foxarmy.barcodescannerforemployees
|
|
||||||
|
|
||||||
import android.provider.BaseColumns
|
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
@@ -12,29 +12,38 @@ import android.widget.TextView
|
|||||||
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
|
||||||
import org.foxarmy.barcodescannerforemployees.AbstractProduct
|
import org.foxarmy.barcodescannerforemployees.*
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
|
||||||
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
|
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
|
||||||
import org.foxarmy.barcodescannerforemployees.getImageUri
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class AbstractProductView: LinearLayout {
|
class AbstractProductView: LinearLayout {
|
||||||
private var productLayout: ConstraintLayout
|
private var productLayout: ConstraintLayout? = null
|
||||||
private var productPicture: ImageView
|
private var productPicture: ImageView? = null
|
||||||
private var productNameField: TextView
|
private var productNameField: TextView? = null
|
||||||
private var netWeightField: TextView
|
private var netWeightField: TextView? = null
|
||||||
private var categoryField: TextView
|
private var categoryField: TextView? = null
|
||||||
private var unitField: TextView
|
private var unitField: TextView? = null
|
||||||
public var product: AbstractProduct
|
var abstractProduct: AbstractProduct = AbstractProduct()
|
||||||
var isProductSelected = false
|
var isProductSelected = false
|
||||||
|
private lateinit var activity: Activity
|
||||||
|
|
||||||
constructor(activity: Activity, context: Context, product: AbstractProduct) : super(context) {
|
constructor(context: Context, a: AttributeSet) : super(context, a) {
|
||||||
|
activity = getActivity(context)!!
|
||||||
this.product = product
|
val inflater:LayoutInflater = activity.layoutInflater
|
||||||
|
inflater.inflate(R.layout.abstract_product_view, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(activity: Activity, context: Context, abstractProduct: AbstractProduct) : super(context) {
|
||||||
|
this.abstractProduct = abstractProduct
|
||||||
|
this.activity = activity
|
||||||
val inflater:LayoutInflater = activity.layoutInflater
|
val inflater:LayoutInflater = activity.layoutInflater
|
||||||
inflater.inflate(R.layout.abstract_product_view, this)
|
inflater.inflate(R.layout.abstract_product_view, this)
|
||||||
|
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init () {
|
||||||
productLayout = findViewById(R.id.productLayout)
|
productLayout = findViewById(R.id.productLayout)
|
||||||
productPicture = findViewById(R.id.productPicture)
|
productPicture = findViewById(R.id.productPicture)
|
||||||
productNameField = findViewById(R.id.productNameView)
|
productNameField = findViewById(R.id.productNameView)
|
||||||
@@ -42,34 +51,39 @@ class AbstractProductView: LinearLayout {
|
|||||||
categoryField = findViewById(R.id.categoryView)
|
categoryField = findViewById(R.id.categoryView)
|
||||||
unitField = findViewById(R.id.unitView)
|
unitField = findViewById(R.id.unitView)
|
||||||
|
|
||||||
val thumbnailsDir = File(context.cacheDir, "thumbnails")
|
|
||||||
thumbnailsDir.mkdirs()
|
productPicture!!.setOnClickListener {
|
||||||
val imageUri = getImageUri(activity, File(thumbnailsDir, "${product.imageHash}.webp"))
|
|
||||||
productPicture.setImageURI(imageUri)
|
|
||||||
productPicture.rotation = 90f
|
|
||||||
productPicture.setOnClickListener {
|
|
||||||
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
|
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
|
||||||
val extras = Bundle()
|
val extras = Bundle()
|
||||||
extras.putString("imagehash", product.imageHash)
|
extras.putString("imagehash", abstractProduct.imageHash)
|
||||||
fullscreenIntent.putExtras(extras)
|
fullscreenIntent.putExtras(extras)
|
||||||
startActivity(context, fullscreenIntent, extras)
|
startActivity(context, fullscreenIntent, extras)
|
||||||
}
|
}
|
||||||
|
|
||||||
productLayout.setOnClickListener {
|
productLayout!!.setOnClickListener {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
productNameField.text = product.name
|
unitField?.text = getUnitNameById(context, abstractProduct.unit)
|
||||||
netWeightField.text = product.netWeight.toString()
|
|
||||||
|
|
||||||
//TODO: category and units
|
productLayout!!.setOnLongClickListener {
|
||||||
|
|
||||||
productLayout.setOnLongClickListener {
|
|
||||||
isProductSelected = !isProductSelected
|
isProductSelected = !isProductSelected
|
||||||
Log.d("QWERTYUIOP", "Changed to value $isProductSelected")
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun update() {
|
||||||
|
init()
|
||||||
|
|
||||||
|
val thumbnailsDir = File(context.cacheDir, "thumbnails")
|
||||||
|
thumbnailsDir.mkdirs()
|
||||||
|
val imageUri = getImageUri(activity, File(thumbnailsDir, "${abstractProduct.imageHash}.webp"))
|
||||||
|
productPicture!!.setImageURI(imageUri)
|
||||||
|
productPicture!!.rotation = 90f
|
||||||
|
|
||||||
|
productNameField!!.text = abstractProduct.name
|
||||||
|
netWeightField!!.text = abstractProduct.netWeight.toString()
|
||||||
|
categoryField!!.text = DBStorageController(context).getCategoryNameById(DBStorageController(context).readableDatabase, abstractProduct.category)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
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.dataclasses.Category
|
||||||
|
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
||||||
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
|
||||||
|
class CategoryView : LinearLayout {
|
||||||
|
var category: Category
|
||||||
|
val categoryName: TextView
|
||||||
|
val amountOfProducts: TextView
|
||||||
|
var isCategorySelected = false
|
||||||
|
|
||||||
|
constructor(activity: Activity, context: Context, category: Category) : super(context) {
|
||||||
|
this.category = category
|
||||||
|
|
||||||
|
val inflater: LayoutInflater = activity.layoutInflater
|
||||||
|
inflater.inflate(R.layout.category_view, this)
|
||||||
|
|
||||||
|
this.background = ContextCompat.getDrawable(context, if (isCategorySelected) R.drawable.outline_selected else R.drawable.outline)
|
||||||
|
|
||||||
|
categoryName = findViewById(R.id.categoryNameTextView)
|
||||||
|
amountOfProducts = findViewById(R.id.amountOfProducts)
|
||||||
|
|
||||||
|
categoryName.text = category.name
|
||||||
|
amountOfProducts.text = DBStorageController(context).getAmountOfAbstractProductsInCategory(DBStorageController(context).readableDatabase, category.id).toString()
|
||||||
|
|
||||||
|
setOnLongClickListener {
|
||||||
|
isCategorySelected = !isCategorySelected
|
||||||
|
this.background = ContextCompat.getDrawable(context, if (isCategorySelected) R.drawable.outline_selected else R.drawable.outline)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
package org.foxarmy.barcodescannerforemployees.views
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.content.ContextCompat.startActivity
|
||||||
|
import androidx.core.graphics.blue
|
||||||
|
import androidx.core.graphics.green
|
||||||
|
import androidx.core.graphics.red
|
||||||
|
import androidx.core.math.MathUtils.clamp
|
||||||
|
import org.foxarmy.barcodescannerforemployees.*
|
||||||
|
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
|
import java.io.File
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
class ProductView: LinearLayout {
|
||||||
|
var product: Product = Product()
|
||||||
|
var isProductSelected = false
|
||||||
|
private lateinit var activity: Activity
|
||||||
|
|
||||||
|
private lateinit var productImageView: ImageView
|
||||||
|
private lateinit var productNameTextView: TextView
|
||||||
|
private lateinit var productNetWeightTextView: TextView
|
||||||
|
private lateinit var productAmountTextView: TextView
|
||||||
|
private lateinit var productCategoryView: TextView
|
||||||
|
private lateinit var productLifeSpan: TextView
|
||||||
|
private lateinit var productFreshnessTextView: TextView
|
||||||
|
|
||||||
|
private var backgroundColor: Int = 0xffffff
|
||||||
|
private var strokeColor: Int = 0x000000
|
||||||
|
private lateinit var outline: GradientDrawable
|
||||||
|
|
||||||
|
constructor(context: Context, a: AttributeSet) : super(context, a) {
|
||||||
|
activity = getActivity(context)!!
|
||||||
|
val inflater: LayoutInflater = activity.layoutInflater
|
||||||
|
inflater.inflate(R.layout.product_view, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
constructor(activity: Activity, context: Context, product: Product) : super(context) {
|
||||||
|
this.product = product
|
||||||
|
this.activity = activity
|
||||||
|
val inflater: LayoutInflater = activity.layoutInflater
|
||||||
|
inflater.inflate(R.layout.product_view, this)
|
||||||
|
|
||||||
|
productImageView = findViewById(R.id.productPicture)
|
||||||
|
productNameTextView = findViewById(R.id.productNameView)
|
||||||
|
productNetWeightTextView = findViewById(R.id.productNetWeightView)
|
||||||
|
productAmountTextView = findViewById(R.id.amountView)
|
||||||
|
productCategoryView = findViewById(R.id.categoryView)
|
||||||
|
productLifeSpan = findViewById(R.id.dateSpan)
|
||||||
|
productFreshnessTextView = findViewById(R.id.freshnessPercentTextView)
|
||||||
|
|
||||||
|
findViewById<ConstraintLayout>(R.id.productLayout).setOnLongClickListener {
|
||||||
|
isProductSelected = !isProductSelected
|
||||||
|
updateStroke()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
productImageView.setOnClickListener {
|
||||||
|
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
|
||||||
|
val extras = Bundle()
|
||||||
|
val abstractProduct = DBStorageController(context).findAbstractProductById(DBStorageController(context).readableDatabase, product.abstractProductId)
|
||||||
|
extras.putString("imagehash", abstractProduct!!.imageHash)
|
||||||
|
fullscreenIntent.putExtras(extras)
|
||||||
|
startActivity(context, fullscreenIntent, extras)
|
||||||
|
}
|
||||||
|
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun update () {
|
||||||
|
val linkedAbstractProduct: AbstractProduct = DBStorageController(activity).findAbstractProductById(DBStorageController(activity).readableDatabase, product.abstractProductId)!!
|
||||||
|
|
||||||
|
val thumbnailsDir = File(activity.cacheDir, "thumbnails")
|
||||||
|
thumbnailsDir.mkdirs()
|
||||||
|
val pictureFile = File(thumbnailsDir, "${linkedAbstractProduct.imageHash}.webp")
|
||||||
|
val imageUri = getImageUri(activity, pictureFile)
|
||||||
|
productImageView.setImageURI(imageUri)
|
||||||
|
productImageView.rotation = 90f
|
||||||
|
|
||||||
|
productNameTextView.text = linkedAbstractProduct.name
|
||||||
|
productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString()
|
||||||
|
productAmountTextView.text = product.amount.toString()
|
||||||
|
productCategoryView.text = DBStorageController(activity).getCategoryNameById(DBStorageController(activity).readableDatabase, linkedAbstractProduct.category)
|
||||||
|
productLifeSpan.text = "${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 || product.freshness < 0) {
|
||||||
|
context.getString(R.string.expired)
|
||||||
|
} else {
|
||||||
|
"${DecimalFormat("#.#").format(product.freshness*100)}%"
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun updateStroke() {
|
||||||
|
if (isProductSelected) {
|
||||||
|
this.background = ContextCompat.getDrawable(context, R.drawable.outline_selected)
|
||||||
|
} else {
|
||||||
|
if (product.id != 0) {
|
||||||
|
thread {
|
||||||
|
this.outline = GradientDrawable()
|
||||||
|
backgroundColor = evaluateColor()
|
||||||
|
strokeColor = darkenColor(backgroundColor, 0.25) // (backgroundColor and 0xfefefe ) shr 1
|
||||||
|
this.outline.setColor(backgroundColor)
|
||||||
|
this.outline.setStroke(4, strokeColor)
|
||||||
|
this.outline.alpha = 84
|
||||||
|
this.background = outline
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.background = ContextCompat.getDrawable(context, R.drawable.outline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun evaluateColor(): Int {
|
||||||
|
val freshnessPercentage = calculateProductFreshness(product.dateOfProduction, product.dateOfExpiry)
|
||||||
|
return calculateFreshnessGradient(freshnessPercentage)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateFreshnessGradient(percentage: Double): Int {
|
||||||
|
val startColor = ContextCompat.getColor(context, if (percentage > 0.5) R.color.full_freshness else R.color.half_freshness)
|
||||||
|
val endColor = ContextCompat.getColor(context, if (percentage > 0.5) R.color.half_freshness else R.color.expired_freshness)
|
||||||
|
|
||||||
|
val gradientPosition = 1 - if (percentage > 0.5) (percentage - 0.5) * 2 else percentage * 2
|
||||||
|
|
||||||
|
val red = clamp((startColor.red + gradientPosition * (endColor.red - startColor.red )).toInt(), 0, 255)
|
||||||
|
val green = clamp((startColor.green + gradientPosition * (endColor.green - startColor.green)).toInt(), 0, 255)
|
||||||
|
val blue = clamp((startColor.blue + gradientPosition * (endColor.blue - startColor.blue)).toInt(), 0, 255)
|
||||||
|
|
||||||
|
var redHex = Integer.toHexString(red)
|
||||||
|
if (redHex.length == 1) redHex = "0$redHex"
|
||||||
|
|
||||||
|
var greenHex = Integer.toHexString(green)
|
||||||
|
if (greenHex.length == 1) greenHex = "0$greenHex"
|
||||||
|
|
||||||
|
var blueHex = Integer.toHexString(blue)
|
||||||
|
if (blueHex.length == 1 ) blueHex = "0$blueHex"
|
||||||
|
|
||||||
|
|
||||||
|
val colorString = "#$redHex$greenHex$blueHex"
|
||||||
|
return Color.parseColor(colorString)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun darkenColor(color: Int, darkPercent: Double) : Int {
|
||||||
|
val c = Color.valueOf(color)
|
||||||
|
|
||||||
|
val red = c.red() * (1 - darkPercent)
|
||||||
|
val green = c.green() * (1 - darkPercent)
|
||||||
|
val blue = c.blue() * (1 - darkPercent)
|
||||||
|
|
||||||
|
return Color.rgb(red.toFloat(), green.toFloat(), blue.toFloat())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
|
||||||
<solid android:color="@android:color/transparent" />
|
<solid android:color="@android:color/transparent" />
|
||||||
<stroke android:width="1dip" android:color="#28283f"/>
|
<stroke android:width="3dip" android:color="#28283f" android:id="@+id/test"/>
|
||||||
<padding android:bottom="1dp" android:top="1dp" android:left="1dp" android:right="1dp" />
|
<padding android:bottom="1dp" android:top="1dp" android:left="1dp" android:right="1dp" />
|
||||||
</shape>
|
</shape>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
|
||||||
<solid android:color="@android:color/transparent" />
|
<solid android:color="@android:color/transparent" />
|
||||||
<stroke android:width="1dip" android:color="#840d15"/>
|
<stroke android:width="3dip" android:color="#840d15"/>
|
||||||
<padding android:bottom="1dp" android:top="1dp" android:left="1dp" android:right="1dp" />
|
<padding android:bottom="1dp" android:top="1dp" android:left="1dp" android:right="1dp" />
|
||||||
</shape>
|
</shape>
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
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:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools" android:layout_width="200dp"
|
xmlns:tools="http://schemas.android.com/tools" android:layout_width="190dp"
|
||||||
android:layout_height="wrap_content" app:barrierMargin="1dp" android:clickable="false"
|
android:layout_height="330dp" app:barrierMargin="1dp" android:clickable="false"
|
||||||
android:outlineProvider="background" android:background="@drawable/outline">
|
android:outlineProvider="background" android:background="@drawable/outline">
|
||||||
|
|
||||||
<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"
|
||||||
android:layout_height="200dp" app:srcCompat="@android:drawable/ic_btn_speak_now"
|
android:layout_height="200dp" app:srcCompat="@android:drawable/ic_menu_gallery"
|
||||||
android:id="@+id/productPicture"
|
android:id="@+id/productPicture"
|
||||||
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"/>
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
@@ -45,7 +45,8 @@
|
|||||||
android:layout_height="wrap_content" android:id="@+id/categoryView"
|
android:layout_height="wrap_content" android:id="@+id/categoryView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:layout_marginEnd="25dp" android:fontFamily="monospace" android:textSize="10sp"
|
android:layout_marginEnd="25dp" android:fontFamily="monospace" android:textSize="10sp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="20dp"/>
|
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="20dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/productNameView" android:layout_marginTop="10dp"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
10
app/src/main/res/layout/activity_add_abstract_product.xml
Normal file
10
app/src/main/res/layout/activity_add_abstract_product.xml
Normal 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:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".fragments.AddAbstractProductFragment">
|
||||||
|
<include layout="@layout/content_add_abstract_product" android:id="@+id/include_content"/>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
26
app/src/main/res/layout/activity_add_category.xml
Normal file
26
app/src/main/res/layout/activity_add_category.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
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">
|
||||||
|
|
||||||
|
<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"/>
|
||||||
|
<Button
|
||||||
|
android:text="@string/saveButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/saveButton"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/newCategoryName" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="40dp"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/add_product_fab"
|
android:id="@+id/new_element_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|end"
|
android:layout_gravity="bottom|end"
|
||||||
|
|||||||
17
app/src/main/res/layout/category_view.xml
Normal file
17
app/src/main/res/layout/category_view.xml
Normal 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="@string/sample_category"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/categoryNameTextView" android:layout_weight="1"
|
||||||
|
android:textSize="25sp"/>
|
||||||
|
<TextView
|
||||||
|
android:text="0"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/amountOfProducts" android:layout_weight="1"
|
||||||
|
android:textAlignment="viewEnd" android:textSize="25sp"/>
|
||||||
|
</LinearLayout>
|
||||||
14
app/src/main/res/layout/content_add_abstract_product.xml
Normal file
14
app/src/main/res/layout/content_add_abstract_product.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?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>
|
||||||
@@ -6,9 +6,9 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" android:id="@+id/addProductLayout">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" android:id="@+id/addProductLayout">
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" app:navGraph="@navigation/nav_graph_add_product"
|
android:layout_height="match_parent" app:navGraph="@navigation/nav_graph_add_product"
|
||||||
app:defaultNavHost="true" android:id="@+id/fragmentContainerView"/>
|
app:defaultNavHost="true" android:id="@+id/fragmentContainerView"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
96
app/src/main/res/layout/fragment_add_abstract_product.xml
Normal file
96
app/src/main/res/layout/fragment_add_abstract_product.xml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".fragments.AddAbstractProductFragment">
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="16dp" android:layout_gravity="center">
|
||||||
|
<Button
|
||||||
|
android:id="@+id/scan_button"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:text="@string/scan_label"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/categoryTextView"
|
||||||
|
android:layout_marginTop="15dp" android:layout_marginStart="5dp"/>
|
||||||
|
<ImageView
|
||||||
|
android:src="@android:drawable/ic_menu_camera"
|
||||||
|
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_marginStart="5dp" android:layout_marginEnd="5dp"/>
|
||||||
|
<EditText
|
||||||
|
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="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/productName" android:textColorHint="#737373"
|
||||||
|
android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/barcodeTextEdit"/>
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:inputType="text"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/netWeight"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:visibility="visible" android:hint="@string/netWeight" android:textColorHint="#737373"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/productName"
|
||||||
|
/>
|
||||||
|
<Spinner
|
||||||
|
android:layout_width="130dp"
|
||||||
|
android:layout_height="50dp" android:id="@+id/unitTypeSpinner"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/netWeight"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/productName" app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
|
<EditText
|
||||||
|
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_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_marginStart="8dp"/>
|
||||||
|
<Spinner
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/categorySpinner"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/categoryTextView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/netWeight" android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="18dp"/>
|
||||||
|
<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_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_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
@@ -9,62 +9,95 @@
|
|||||||
<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_horizontal" android:id="@+id/addProductConstraintLayout">
|
||||||
|
<org.foxarmy.barcodescannerforemployees.views.AbstractProductView
|
||||||
|
android:layout_width="300dp"
|
||||||
|
android:layout_height="300dp" android:id="@+id/abstractProductView"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_marginTop="32dp" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/scan_button"
|
|
||||||
android:layout_width="100dp"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:text="@string/scan_label"
|
android:text="@string/scan_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
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_constraintTop_toBottomOf="@+id/netWeight"
|
|
||||||
android:layout_marginTop="25dp"/>
|
|
||||||
<ImageView
|
|
||||||
android:src="@android:drawable/ic_menu_camera"
|
|
||||||
android:layout_width="356dp"
|
|
||||||
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"/>
|
|
||||||
<EditText
|
|
||||||
android:layout_width="350dp"
|
|
||||||
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"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/netWeight"
|
app:layout_constraintTop_toBottomOf="@+id/abstractProductView" android:layout_marginTop="16dp"/>
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
<TextView
|
||||||
android:visibility="visible" android:hint="@string/product_name_label" android:textColorHint="#737373"
|
android:text="@string/date_of_production"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/imageView"/>
|
android:layout_width="wrap_content"
|
||||||
<EditText
|
android:layout_height="wrap_content" android:id="@+id/dateOfProductionTextView"
|
||||||
android:layout_width="350dp"
|
app:layout_constraintTop_toBottomOf="@+id/scanButton"
|
||||||
android:layout_height="50dp"
|
android:layout_marginTop="16dp" app:layout_constraintStart_toStartOf="parent"/>
|
||||||
android:inputType="text"
|
<Button
|
||||||
android:ems="10"
|
android:text="@string/select"
|
||||||
android:id="@+id/netWeight"
|
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"/>
|
||||||
|
<RadioGroup
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/selectDateOfProductionButton"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:layout_marginStart="8dp"
|
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:orientation="horizontal" android:id="@+id/radioGroup">
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
<RadioButton
|
||||||
android:visibility="visible" android:hint="@string/netWeight" android:textColorHint="#737373"
|
android:text="@string/expiry_date"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/productName"/>
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/expiryDateRadio"/>
|
||||||
|
<RadioButton
|
||||||
|
android:text="@string/shelf_life"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/shelfLifeRadio"/>
|
||||||
|
</RadioGroup>
|
||||||
|
<TextView
|
||||||
|
android:text="Expiry date: "
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/expiryDateTextView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/radioGroup" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:visibility="invisible"/>
|
||||||
|
<Button
|
||||||
|
android:text="Select"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/selectExpiryDateButton"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/expiryDateTextView" android:layout_marginStart="16dp"
|
||||||
|
android:visibility="invisible"/>
|
||||||
|
<TextView
|
||||||
|
android:text="Shelf life:"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/shelfLife"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
|
||||||
|
android:layout_marginTop="16dp" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:layout_marginStart="16dp" android:visibility="invisible"/>
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/shelfLifeTextEdit" app:layout_constraintTop_toBottomOf="@+id/radioGroup"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/expiryDateTextView" android:layout_marginStart="16dp"
|
||||||
|
android:visibility="invisible"/>
|
||||||
|
<TextView
|
||||||
|
android: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"
|
||||||
|
android:text="@string/amount"/>
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="numberSigned"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/amountTextEdit"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/selectExpiryDateButton"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/amountText"/>
|
||||||
<Button
|
<Button
|
||||||
android:text="@string/saveButton"
|
android:text="@string/saveButton"
|
||||||
android:layout_width="100dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="50dp" android:id="@+id/saveButton"
|
android:layout_height="wrap_content" android:id="@+id/saveProductButton"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/netWeight"
|
app:layout_constraintTop_toBottomOf="@+id/amountTextEdit" app:layout_constraintStart_toStartOf="parent"
|
||||||
android:layout_marginTop="25dp" app:layout_constraintEnd_toEndOf="parent"/>
|
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp"/>
|
||||||
<Button
|
|
||||||
android:text="@string/takePicture"
|
|
||||||
android:layout_width="100dp"
|
|
||||||
android:layout_height="55dp" android:id="@+id/takePictureButton"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/netWeight"
|
|
||||||
android:layout_marginTop="24dp" 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"/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
@@ -5,9 +5,10 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/fragment_storage"
|
android:id="@+id/fragment_storage"
|
||||||
tools:context=".fragments.CategoriesFragment">
|
tools:context=".fragments.CategoriesFragment">
|
||||||
<ScrollView
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent" android:id="@+id/categoriesLayout"
|
||||||
android:id="@+id/categoriesLayout">
|
android:layout_gravity="center" android:layout_margin="2dp">
|
||||||
</ScrollView>
|
</LinearLayout>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
37
app/src/main/res/layout/fragment_shelf.xml
Normal file
37
app/src/main/res/layout/fragment_shelf.xml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout 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">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
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"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/sortByTextView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="16dp"
|
||||||
|
/>
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" app:layout_constraintTop_toBottomOf="@+id/spinner"
|
||||||
|
android:layout_marginTop="32dp" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" android:id="@+id/scrollView">
|
||||||
|
<androidx.gridlayout.widget.GridLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2"
|
||||||
|
app:rowCount="100" android:nestedScrollingEnabled="true">
|
||||||
|
|
||||||
|
</androidx.gridlayout.widget.GridLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</FrameLayout>
|
||||||
@@ -5,19 +5,35 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/fragment_storage"
|
android:id="@+id/fragment_storage"
|
||||||
tools:context=".fragments.StorageFragment">
|
tools:context=".fragments.StorageFragment">
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
<androidx.gridlayout.widget.GridLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2"
|
|
||||||
app:rowCount="100" android:nestedScrollingEnabled="true">
|
|
||||||
|
|
||||||
</androidx.gridlayout.widget.GridLayout>
|
|
||||||
</ScrollView>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" android:id="@+id/storageLayout">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:text="@string/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"
|
||||||
|
android:textAlignment="center"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/scrollView2"/>
|
||||||
|
<Spinner
|
||||||
|
android:layout_width="0dp"
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/spinner"
|
||||||
|
android:id="@+id/scrollView2">
|
||||||
|
<androidx.gridlayout.widget.GridLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2"
|
||||||
|
app:rowCount="100" android:nestedScrollingEnabled="true">
|
||||||
|
|
||||||
|
</androidx.gridlayout.widget.GridLayout>
|
||||||
|
</ScrollView>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
68
app/src/main/res/layout/product_view.xml
Normal file
68
app/src/main/res/layout/product_view.xml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical"
|
||||||
|
android:layout_width="190dp"
|
||||||
|
android:layout_height="345dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/productLayout" android:outlineProvider="bounds"
|
||||||
|
android:background="#00FFFEFE" android:clickable="true">
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp" app:srcCompat="@android:drawable/ic_menu_gallery"
|
||||||
|
android:id="@+id/productPicture"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
|
<TextView
|
||||||
|
android:text="0%"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/freshnessPercentTextView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="10dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="5dp"/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/sample_product_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/productNameView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/productPicture"
|
||||||
|
android:layout_marginTop="15dp" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:fontFamily="monospace" android:textSize="20sp"
|
||||||
|
android:maxLines="2" android:layout_marginStart="10dp"/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/sample_product_net_weight"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/productNetWeightView" android:layout_weight="1"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/productNameView"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="25dp"
|
||||||
|
android:layout_marginTop="10dp" android:textSize="12sp" android:fontFamily="monospace"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/unitView" android:layout_marginEnd="2dp"
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/sample_unit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/unitView"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/productNetWeightView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/productNameView" android:layout_marginTop="10dp"
|
||||||
|
android:fontFamily="monospace" android:textSize="12sp" android:layout_marginStart="8dp"/>
|
||||||
|
<TextView
|
||||||
|
android:text="10"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/amountView"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/unitView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/productNameView" android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginStart="16dp"/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/sample_category"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/categoryView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginEnd="25dp" android:fontFamily="monospace" android:textSize="10sp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/amountView" android:layout_marginTop="5dp"/>
|
||||||
|
<TextView
|
||||||
|
android:text="01.01.1970 - 01.01.1971"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/categoryView"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:layout_marginTop="8dp" app:layout_constraintEnd_toEndOf="parent" android:id="@+id/dateSpan"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</LinearLayout>
|
||||||
@@ -7,4 +7,6 @@
|
|||||||
android:orderInCategory="100"
|
android:orderInCategory="100"
|
||||||
app:showAsAction="never"/>
|
app:showAsAction="never"/>
|
||||||
<item android:id="@+id/action_delete" android:title="@string/delete_menu"/>
|
<item android:id="@+id/action_delete" android:title="@string/delete_menu"/>
|
||||||
|
<item android:id="@+id/action_update" android:title="@string/update_menu"/>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?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>
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
android:id="@+id/nav_graph_add_product"
|
android:id="@+id/nav_graph_add_product"
|
||||||
app:startDestination="@id/addProductFragment">
|
app:startDestination="@id/addProductFragment">
|
||||||
|
|
||||||
<fragment android:id="@+id/addProductFragment" android:name="org.foxarmy.barcodescannerforemployees.fragments.AddProductFragment"
|
<fragment android:id="@+id/addProductFragment"
|
||||||
android:label="add_product_fragment" tools:layout="@layout/fragment_add_product"/>
|
android:name="org.foxarmy.barcodescannerforemployees.fragments.AddProductFragment"
|
||||||
|
android:label="activity_add_product" tools:layout="@layout/fragment_add_product"/>
|
||||||
</navigation>
|
</navigation>
|
||||||
80
app/src/main/res/values-ru/strings.xml
Normal file
80
app/src/main/res/values-ru/strings.xml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?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>
|
||||||
|
</resources>
|
||||||
@@ -10,4 +10,8 @@
|
|||||||
<color name="light_blue_A200">#FF40C4FF</color>
|
<color name="light_blue_A200">#FF40C4FF</color>
|
||||||
<color name="light_blue_A400">#FF00B0FF</color>
|
<color name="light_blue_A400">#FF00B0FF</color>
|
||||||
<color name="black_overlay">#66000000</color>
|
<color name="black_overlay">#66000000</color>
|
||||||
|
|
||||||
|
<color name="full_freshness">#5AFF30</color>
|
||||||
|
<color name="half_freshness">#FFF200</color>
|
||||||
|
<color name="expired_freshness">#ff0000</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,57 +1,78 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">BarcodeScannerForEmployees</string>
|
<string name="app_name">BarcodeScannerForEmployees</string>
|
||||||
<string name="action_settings">Settings</string>
|
<string name="action_settings">Settings</string>
|
||||||
<!-- Strings used for fragments for navigation -->
|
|
||||||
<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>
|
||||||
<string name="productType">Product type</string>
|
<string name="productType">Product type</string>
|
||||||
<string name="imageOfAProduct">Image of a product</string>
|
<string name="imageOfAProduct">Image of a product</string>
|
||||||
<string name="sample_product_name">Уззкое название товара</string>
|
<string name="sample_product_name">Sample product name</string>
|
||||||
<string name="sample_product_net_weight">1998</string>
|
<string name="sample_product_net_weight">100</string>
|
||||||
<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="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>
|
||||||
|
|
||||||
<string name="lorem_ipsum">
|
<!-- Unit names -->
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris volutpat, dolor id
|
<string name="kilogram">kg</string>
|
||||||
interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus dui nec risus. Maecenas non sodales
|
<string name="gram">g</string>
|
||||||
nisi, vel dictum dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
|
<string name="liter">l</string>
|
||||||
himenaeos. Suspendisse blandit eleifend diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet,
|
<string name="milliliter">ml</string>
|
||||||
imperdiet nisl a, ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n
|
<string name="pieces">pc</string>
|
||||||
Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus egestas, est a
|
|
||||||
condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed neque. Morbi tellus erat, dapibus ut
|
<string name="product_picture_request">Please, make a picture of a product!</string>
|
||||||
sem a, iaculis tincidunt dui. Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur et eros
|
<string name="product_barcode_request">Please, scan barcode on a product</string>
|
||||||
porttitor, ultricies urna vitae, molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl
|
<string name="product_name_request">Please, write a name of a product!</string>
|
||||||
nec dolor bibendum, vel congue leo egestas.\n\n
|
<string name="product_net_weight_request">"Please, write a valid net weight of a product!"</string>
|
||||||
Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit amet auctor at,
|
<string name="camera_permission_for_picture_request">I need permission in order to take a picture</string>
|
||||||
mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel, molestie quam. Fusce blandit tincidunt
|
<string name="cancelled">Cancelled</string>
|
||||||
nulla, quis sollicitudin libero facilisis et. Integer interdum nunc ligula, et fermentum metus hendrerit id.
|
<string name="scan_barcode_of_a_product">Scan barcode of a product</string>
|
||||||
Vestibulum lectus felis, dictum at lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse
|
|
||||||
sodales nunc ligula, in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque
|
<string name="shell_life_or_expiry_date_request">Please, choose and fill in shelf life or expiry date.</string>
|
||||||
est.\n\n
|
<string name="date_of_production_request">Please, choose date of production.</string>
|
||||||
Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. Morbi laoreet, tortor
|
<string name="expiry_date_request">Please, choose expiry date.</string>
|
||||||
sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui non lorem. Nam mollis ipsum quis auctor
|
<string name="product_amount_request">Please, specify an amount of product.</string>
|
||||||
varius. Quisque elementum eu libero sed commodo. In eros nisl, imperdiet vel imperdiet et, scelerisque a mauris.
|
<string name="camera_permission_for_scanning_request">I need permission in order to scan a barcode</string>
|
||||||
Pellentesque varius ex nunc, quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non
|
|
||||||
viverra ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a placerat
|
<string name="categories_title">Categories</string>
|
||||||
diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus convallis.\n\n
|
<string name="storage_title">Storage</string>
|
||||||
Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis
|
<string name="shelf_title">Shelf</string>
|
||||||
egestas. In volutpat arcu ut felis sagittis, in finibus massa gravida. Pellentesque id tellus orci. Integer
|
|
||||||
dictum, lorem sed efficitur ullamcorper, libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec
|
<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>
|
||||||
maximus ullamcorper sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus
|
<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>
|
||||||
libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus vestibulum. Fusce
|
<string name="yes">Yes</string>
|
||||||
dictum libero quis erat maximus, vitae volutpat diam dignissim.
|
<string name="no">No</string>
|
||||||
</string>
|
|
||||||
<string name="delete_menu">Delete item(s)...</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>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -14,8 +14,16 @@ navigationFragmentKtx = "2.8.0"
|
|||||||
navigationUiKtx = "2.8.0"
|
navigationUiKtx = "2.8.0"
|
||||||
firebaseCrashlyticsBuildtools = "3.0.2"
|
firebaseCrashlyticsBuildtools = "3.0.2"
|
||||||
gridlayout = "1.0.0"
|
gridlayout = "1.0.0"
|
||||||
|
activity = "1.9.2"
|
||||||
|
legacySupportV4 = "1.0.0"
|
||||||
|
fragment = "1.8.4"
|
||||||
|
playServicesCodeScanner = "16.1.0"
|
||||||
|
volley = "1.2.1"
|
||||||
|
zxingAndroidEmbedded = "4.3.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraView" }
|
||||||
|
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "cameraView" }
|
||||||
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "cameraView" }
|
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "cameraView" }
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "barcodeScanning" }
|
barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "barcodeScanning" }
|
||||||
@@ -29,6 +37,12 @@ androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navi
|
|||||||
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
|
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
|
||||||
firebase-crashlytics-buildtools = { group = "com.google.firebase", name = "firebase-crashlytics-buildtools", version.ref = "firebaseCrashlyticsBuildtools" }
|
firebase-crashlytics-buildtools = { group = "com.google.firebase", name = "firebase-crashlytics-buildtools", version.ref = "firebaseCrashlyticsBuildtools" }
|
||||||
androidx-gridlayout = { group = "androidx.gridlayout", name = "gridlayout", version.ref = "gridlayout" }
|
androidx-gridlayout = { group = "androidx.gridlayout", name = "gridlayout", version.ref = "gridlayout" }
|
||||||
|
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
||||||
|
androidx-legacy-support-v4 = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacySupportV4" }
|
||||||
|
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]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Reference in New Issue
Block a user