Compare commits
24 Commits
alpha-0.0.
...
master
Author | SHA1 | Date |
---|---|---|
leca | 58a7ea7357 | |
leca | 2ebcfff51a | |
leca | 2676d8083e | |
leca | e5772bcad3 | |
leca | 988272070d | |
leca | fbf630090c | |
leca | 8c3845a07e | |
leca | d08a79e981 | |
leca | ec7062602d | |
leca | c6def13eb4 | |
leca | 767e1ec818 | |
leca | 3a296e7a26 | |
leca | 617dcf2e7d | |
leca | 7cc0467967 | |
leca | cd299477d4 | |
leca | 94d309c491 | |
leca | 3e5150754a | |
leca | 8adbe65aa0 | |
leca | 4041e63a4b | |
leca | f636511983 | |
leca | 7c33fe526c | |
leca | 2feb2c1b5e | |
leca | a0d96da9e4 | |
leca | 3d4f86085e |
|
@ -50,13 +50,16 @@ dependencies {
|
||||||
implementation(libs.androidx.activity)
|
implementation(libs.androidx.activity)
|
||||||
implementation(libs.androidx.legacy.support.v4)
|
implementation(libs.androidx.legacy.support.v4)
|
||||||
implementation(libs.androidx.fragment)
|
implementation(libs.androidx.fragment)
|
||||||
|
implementation(libs.androidx.material3.android)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
implementation(libs.volley)
|
implementation(libs.volley)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
implementation(libs.zxing.android.embedded)
|
implementation(libs.zxing.android.embedded)
|
||||||
implementation("com.google.zxing:core:3.4.1")
|
implementation("com.google.zxing:core:3.4.1")
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
implementation("com.google.android.material:material:1.3.0-alpha03")
|
||||||
|
implementation(libs.androidx.security.crypto)
|
||||||
|
implementation(libs.okhttp)
|
||||||
// Barcode scanning API
|
// Barcode scanning API
|
||||||
implementation (libs.barcode.scanning)
|
implementation (libs.barcode.scanning)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<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"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -48,6 +49,34 @@
|
||||||
android:name=".activities.FindBarcodelessAbstractProduct"
|
android:name=".activities.FindBarcodelessAbstractProduct"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.MainActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.LoginActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.AccountSettingsActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.GroupActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.ManageGroupActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.MyGroupsActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.SettingsActivity"
|
||||||
|
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"
|
||||||
|
@ -61,7 +90,7 @@
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="com.google.firebase.components.activities.MainActivity.provider;com.google.firebase.components.activities.FullscreenActivity.provider;com.google.firebase.components.activities.AddAbstractProductActivity.provider;com.google.firebase.components.activities.AddProductActivity.provider;com.google.firebase.components.activities.ExpiryCalendarActivity.provider;com.google.firebase.components.activities.FindBarcodelessAbstractProduct.provider;com.google.firebase.components.activities.ExpiryCalendarGroupActivity.provider"
|
android:authorities="com.google.firebase.components.activities.MainActivity.provider;com.google.firebase.components.activities.FullscreenActivity.provider;com.google.firebase.components.activities.AddAbstractProductActivity.provider;com.google.firebase.components.activities.AddProductActivity.provider;com.google.firebase.components.activities.ExpiryCalendarActivity.provider;com.google.firebase.components.activities.FindBarcodelessAbstractProduct.provider;com.google.firebase.components.activities.ExpiryCalendarGroupActivity.provider;com.google.firebase.components.activities.AccountSettingsActivity.provider;com.google.firebase.components.activities.GroupActivity.provider;com.google.firebase.components.activities.SettingsActivity.provider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
|
@ -70,7 +99,7 @@
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.MainActivity"
|
android:name=".activities.NavigatorActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.BarcodeScannerForEmployees">
|
android:theme="@style/Theme.BarcodeScannerForEmployees">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|
|
@ -1,326 +0,0 @@
|
||||||
package org.foxarmy.barcodescannerforemployees
|
|
||||||
|
|
||||||
import android.content.ContentValues
|
|
||||||
import android.content.Context
|
|
||||||
import android.database.DatabaseUtils
|
|
||||||
import android.database.sqlite.SQLiteDatabase
|
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
|
||||||
import android.provider.BaseColumns
|
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
object AbstractProductContract {
|
|
||||||
object AbstractProductEntry : BaseColumns {
|
|
||||||
const val TABLE_NAME = "abstract_products"
|
|
||||||
const val BARCODE = "barcode"
|
|
||||||
const val PRODUCT_NAME = "name"
|
|
||||||
const val PRODUCT_NET_WEIGHT = "net_weight"
|
|
||||||
const val IMAGE_FILENAME = "image_filename"
|
|
||||||
const val CATEGORY = "category"
|
|
||||||
const val UNIT = "unit"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object CategoriesContract {
|
|
||||||
object CategoryEntry : BaseColumns {
|
|
||||||
const val TABLE_NAME = "categories"
|
|
||||||
const val CATEGORY_NAME = "category_name"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object ProductContract {
|
|
||||||
object ProductEntry : BaseColumns {
|
|
||||||
const val TABLE_NAME = "products"
|
|
||||||
const val ABSTRACT_PRODUCT_ID = "abstract_product_id"
|
|
||||||
const val AMOUNT = "amount"
|
|
||||||
const val DATE_OF_PRODUCTION = "date_of_production"
|
|
||||||
const val EXPIRY_DATE = "expiry_date"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const val SQL_CREATE_ABSTRACT_PRODUCTS_TABLE =
|
|
||||||
"CREATE TABLE ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} (" +
|
|
||||||
"${BaseColumns._ID} INTEGER PRIMARY KEY," +
|
|
||||||
"${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 =
|
|
||||||
"CREATE TABLE ${CategoriesContract.CategoryEntry.TABLE_NAME} (" +
|
|
||||||
"${BaseColumns._ID} INTEGER PRIMARY KEY," +
|
|
||||||
"${CategoriesContract.CategoryEntry.CATEGORY_NAME} TEXT)"
|
|
||||||
|
|
||||||
class DBStorageController(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
|
|
||||||
|
|
||||||
override fun onCreate(db: SQLiteDatabase) {
|
|
||||||
db.execSQL(SQL_CREATE_ABSTRACT_PRODUCTS_TABLE)
|
|
||||||
db.execSQL(SQL_CREATE_PRODUCTS_TABLE)
|
|
||||||
db.execSQL(SQL_CREATE_CATEGORIES_TABLE)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUpgrade(db: SQLiteDatabase?, oldV: Int, newV: Int) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun eraseCategory (db: SQLiteDatabase, id: Int, context: Context) {
|
|
||||||
val productsInCategory = getAllAbstractProductInCategory(db, id)
|
|
||||||
|
|
||||||
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 selectionArgs = arrayOf(id.toString())
|
|
||||||
|
|
||||||
val cursor = db.query(CategoriesContract.CategoryEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)
|
|
||||||
|
|
||||||
with (cursor) {
|
|
||||||
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
|
|
||||||
)
|
|
||||||
var imageHash: String = ""
|
|
||||||
|
|
||||||
with (cursorForAbstractProduct) {
|
|
||||||
while(moveToNext()) {
|
|
||||||
val productImageHash = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME))
|
|
||||||
imageHash = productImageHash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val picturesDir = File(context.filesDir, "pictures")
|
|
||||||
val thumbnailsDir = File(context.cacheDir, "thumbnails")
|
|
||||||
|
|
||||||
File(picturesDir, "$imageHash.png").delete()
|
|
||||||
File(thumbnailsDir, "$imageHash.webp").delete()
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 findAmountOfProductsWithExpiryDate(db: SQLiteDatabase, date: Long): Int {
|
|
||||||
var amount = 0
|
|
||||||
|
|
||||||
val projection = arrayOf(
|
|
||||||
ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID,
|
|
||||||
ProductContract.ProductEntry.AMOUNT,
|
|
||||||
ProductContract.ProductEntry.DATE_OF_PRODUCTION,
|
|
||||||
)
|
|
||||||
|
|
||||||
val selection = "${ProductContract.ProductEntry.EXPIRY_DATE} = ?"
|
|
||||||
val selectionArgs = arrayOf(date.toString())
|
|
||||||
|
|
||||||
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)
|
|
||||||
|
|
||||||
with(cursor) {
|
|
||||||
while (moveToNext()) {
|
|
||||||
amount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return amount
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findAllExpiryDates(db:SQLiteDatabase): Set<Long> {
|
|
||||||
val dates: MutableSet<Long> = mutableSetOf<Long>()
|
|
||||||
|
|
||||||
val projection = arrayOf(
|
|
||||||
ProductContract.ProductEntry.EXPIRY_DATE
|
|
||||||
)
|
|
||||||
|
|
||||||
val orderBy = "${ProductContract.ProductEntry.EXPIRY_DATE} ASC"
|
|
||||||
|
|
||||||
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, null, null, null, orderBy)
|
|
||||||
|
|
||||||
with(cursor) {
|
|
||||||
while (moveToNext()) {
|
|
||||||
dates.add(getLong(getColumnIndexOrThrow(ProductContract.ProductEntry.EXPIRY_DATE)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dates
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findAbstractProductByBarcode (db: SQLiteDatabase, barcode: String) : AbstractProduct? {
|
|
||||||
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 {
|
|
||||||
const val DATABASE_VERSION = 1
|
|
||||||
const val DATABASE_NAME = "database.db"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,656 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import okhttp3.*
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
class Net {
|
||||||
|
var language = "en-US"
|
||||||
|
var server = "bsfe.foxarmy.org"
|
||||||
|
var token = ""
|
||||||
|
|
||||||
|
fun serverIsAvailable(context: Context): Boolean {
|
||||||
|
if (!isInternetConnectionAvailable(context)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var flag = false
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient();
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/status")
|
||||||
|
.get()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
val response = client.newCall(request).execute();
|
||||||
|
flag = response.code == 200;
|
||||||
|
} catch (e: Exception) {
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
return flag
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestProductFromOnlineDB(barcode: String): String {
|
||||||
|
var response = ""
|
||||||
|
thread {
|
||||||
|
val url = "https://ean-online.ru/match.php"
|
||||||
|
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("barcode", barcode)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(body)
|
||||||
|
.addHeader("referer", "https://ean-online.ru")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute().body!!.string()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return if (response == "") {
|
||||||
|
"Not found 404"
|
||||||
|
} else {
|
||||||
|
response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerAccount(username: String, password: String): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("username", username)
|
||||||
|
.add("password", password)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/user/register")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("content-type", "application/x-www-form-urlencoded")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun login(username: String, password: String): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("username", username)
|
||||||
|
.add("password", password)
|
||||||
|
.build()
|
||||||
|
val requestLogin = Request.Builder()
|
||||||
|
.url("https://$server/api/user/login")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("content-type", "application/x-www-form-urlencoded")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
response = client.newCall(requestLogin).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun uploadAbstractProduct(groupId: Int, abstractProduct: AbstractProduct, imageFile: File): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = MultipartBody.Builder()
|
||||||
|
.setType("multipart/form-data".toMediaType())
|
||||||
|
.addFormDataPart("file", imageFile.name, imageFile.asRequestBody("image/png".toMediaTypeOrNull()))
|
||||||
|
.addFormDataPart("groupId", groupId.toString())
|
||||||
|
.addFormDataPart("localId", abstractProduct.id.toString())
|
||||||
|
.addFormDataPart("barcode", abstractProduct.barcode)
|
||||||
|
.addFormDataPart("name", abstractProduct.name)
|
||||||
|
.addFormDataPart("net_weight", abstractProduct.netWeight.toString())
|
||||||
|
.addFormDataPart("category", abstractProduct.category.toString())
|
||||||
|
.addFormDataPart("unit", abstractProduct.unit.toString())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/abstractproduct/create")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeUsername(newUsername: String): Response {
|
||||||
|
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
body.add("username", newUsername)
|
||||||
|
|
||||||
|
val requestBody = body.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/user/changeUsername")
|
||||||
|
.post(requestBody)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changePassword(newPassword: String): Response {
|
||||||
|
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
body.add("password", newPassword)
|
||||||
|
|
||||||
|
val requestBody = body.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/user/changePassword")
|
||||||
|
.post(requestBody)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createGroup(name: String, password: String): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
|
||||||
|
val requestBody = body.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/group/create/$name")
|
||||||
|
.post(requestBody)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
|
||||||
|
changeGroupPassword(name, password)
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun joinGroup(id: Int, password: String): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("password", password)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/group/join/$id")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeGroupPassword(name: String, password: String): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
|
||||||
|
val groupId = getGroupId(name);
|
||||||
|
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
body.add("password", password)
|
||||||
|
|
||||||
|
val requestBody = body.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/group/password/$groupId")
|
||||||
|
.post(requestBody)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGroupId(name: String): String {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/group/byName/$name")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
val responseText = response.body!!.string()
|
||||||
|
|
||||||
|
return responseText
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGroupName(id: Int): String {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/group/byId/$id")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
val responseText = response.body!!.string()
|
||||||
|
|
||||||
|
return responseText
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUsersInGroup(groupId: Int): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/group/getUsers/$groupId")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMyGroups(): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/user/myGroups")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUsernameById(userId: Int): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/user/byId/$userId")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGroupAdminId(groupId: Int): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/group/adminId/$groupId")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateAbstractProduct(groupId: Int, abstractProduct: AbstractProduct, imageFile: File): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = MultipartBody.Builder()
|
||||||
|
.setType("multipart/form-data".toMediaType())
|
||||||
|
.addFormDataPart("file", imageFile.name, imageFile.asRequestBody("image/png".toMediaTypeOrNull()))
|
||||||
|
.addFormDataPart("groupId", groupId.toString())
|
||||||
|
.addFormDataPart("localId", abstractProduct.id.toString())
|
||||||
|
.addFormDataPart("barcode", abstractProduct.barcode)
|
||||||
|
.addFormDataPart("name", abstractProduct.name)
|
||||||
|
.addFormDataPart("net_weight", abstractProduct.netWeight.toString())
|
||||||
|
.addFormDataPart("category", abstractProduct.category.toString())
|
||||||
|
.addFormDataPart("unit", abstractProduct.unit.toString())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/abstractproduct/update")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun uploadCategory(groupId: Int, category: Category): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("localId", category.id.toString())
|
||||||
|
.add("categoryName", category.name)
|
||||||
|
.add("groupId", groupId.toString())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/category/create")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateCategory(groupId: Int, category: Category): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("localId", category.id.toString())
|
||||||
|
.add("categoryName", category.name)
|
||||||
|
.add("groupId", groupId.toString())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/category/update")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun uploadProduct(groupId: Int, product: Product): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("localId", product.id.toString())
|
||||||
|
.add("groupId", groupId.toString())
|
||||||
|
.add("abstract_product_id", product.abstractProductId.toString())
|
||||||
|
.add("amount", product.amount.toString())
|
||||||
|
.add("date_of_production", product.dateOfProduction.toString())
|
||||||
|
.add("expiry_date", product.dateOfExpiry.toString())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/product/create")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateProduct(groupId: Int, product: Product): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("localId", product.id.toString())
|
||||||
|
.add("groupId", groupId.toString())
|
||||||
|
.add("abstract_product_id", product.abstractProductId.toString())
|
||||||
|
.add("amount", product.amount.toString())
|
||||||
|
.add("date_of_production", product.dateOfProduction.toString())
|
||||||
|
.add("expiry_date", product.dateOfExpiry.toString())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/product/update")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun synchronize(groupId: Int): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/user/synchronize/$groupId")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProduct(groupId: Int, localId: Int): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/product/$groupId/$localId")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAbstractProduct(groupId: Int, localId: Int): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/abstractproduct/$groupId/$localId")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun downloadImage(url: String, file: File) {
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
client.newCall(request).execute().use { response ->
|
||||||
|
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||||
|
|
||||||
|
val fos = FileOutputStream(file)
|
||||||
|
|
||||||
|
response.body?.byteStream()?.use { inputStream ->
|
||||||
|
fos.use {
|
||||||
|
inputStream.copyTo(fos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteCategory(groupId: Int, localId: Int): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/category/$groupId/$localId")
|
||||||
|
.delete()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteAbstractProduct(groupId: Int, localId: Int): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/abstractproduct/$groupId/$localId")
|
||||||
|
.delete()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteProduct(groupId: Int, localId: Int): Response {
|
||||||
|
lateinit var response: Response
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("https://$server/api/product/$groupId/$localId")
|
||||||
|
.delete()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.addHeader("accept-language", language)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
response = client.newCall(request).execute()
|
||||||
|
}.join()
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,6 @@ class Parser constructor() {
|
||||||
text = text.replace(found, "")
|
text = text.replace(found, "")
|
||||||
netWeight = stripNetWeight(found)
|
netWeight = stripNetWeight(found)
|
||||||
return Triple(text, netWeight, found)
|
return Triple(text, netWeight, found)
|
||||||
} else {
|
|
||||||
return Triple(text, 0.0, "")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Triple("", 0.0, "")
|
return Triple("", 0.0, "")
|
||||||
|
@ -48,4 +46,4 @@ class Parser constructor() {
|
||||||
|
|
||||||
return AbstractProduct(0, "", name, netWeight, "", 0, unitNumber)
|
return AbstractProduct(0, "", name, netWeight, "", 0, unitNumber)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
package org.foxarmy.barcodescannerforemployees
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.widget.Toast
|
|
||||||
import com.android.volley.toolbox.StringRequest
|
|
||||||
import com.android.volley.toolbox.Volley
|
|
||||||
|
|
||||||
class Requester constructor(var siteName: String, var endpoint: String) {
|
|
||||||
var response = ""
|
|
||||||
|
|
||||||
fun request(context: Context, barcode: String) {
|
|
||||||
val url = "${siteName}/${endpoint}"
|
|
||||||
|
|
||||||
val volleyQueue = Volley.newRequestQueue(context)
|
|
||||||
val stringRequest = object: StringRequest(
|
|
||||||
Method.POST, url, { resp ->
|
|
||||||
run {
|
|
||||||
response =
|
|
||||||
if (resp == "") {
|
|
||||||
"Not found 404"
|
|
||||||
} else {
|
|
||||||
resp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
volleyQueue.add(stringRequest)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,19 +6,38 @@ import android.content.ContextWrapper
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Matrix
|
import android.graphics.Matrix
|
||||||
|
import android.media.ExifInterface
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
import com.google.firebase.components.BuildConfig
|
import com.google.firebase.components.BuildConfig
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.net.URLEncoder
|
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
fun convertToUnixEpochTimestamp(dateString: String): Long {
|
||||||
|
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||||
|
format.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
val date = format.parse(dateString)
|
||||||
|
return date!!.time / 1000
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.R)
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
@ -34,11 +53,18 @@ fun generateThumbnailForImage(context: Context, imageHash: String) {
|
||||||
val imageContent = imageFile.inputStream().readBytes()
|
val imageContent = imageFile.inputStream().readBytes()
|
||||||
var img = BitmapFactory.decodeByteArray(imageContent, 0, imageContent.size)
|
var img = BitmapFactory.decodeByteArray(imageContent, 0, imageContent.size)
|
||||||
|
|
||||||
val matrix = Matrix();
|
val exif = ExifInterface(imageFile.absoluteFile.toString())
|
||||||
matrix.postRotate(90f)
|
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||||
val scaled = Bitmap.createScaledBitmap(img, img.width/4, img.height/4, true)
|
val matrix = Matrix()
|
||||||
val rotated = Bitmap.createBitmap(scaled, 0, 0, scaled.width, scaled.height, matrix, true)
|
|
||||||
rotated.compress(Bitmap.CompressFormat.WEBP_LOSSY, 25, FileOutputStream(thumbnailFile))
|
when (orientation) {
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
|
||||||
|
}
|
||||||
|
val rotated = Bitmap.createBitmap(img, 0, 0, img.width, img.height, matrix, true)
|
||||||
|
val scaled = Bitmap.createScaledBitmap(rotated, rotated.width / 4, rotated.height / 4, true)
|
||||||
|
scaled.compress(Bitmap.CompressFormat.WEBP_LOSSY, 25, FileOutputStream(thumbnailFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
@ -48,8 +74,11 @@ fun String.md5(): String {
|
||||||
return digest.toHexString()
|
return digest.toHexString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stripNetWeight (netWeight: String): Double {
|
fun stripNetWeight(netWeight: String): Double {
|
||||||
return removeSubstringsFromString(netWeight, arrayOf("Л", "л", "мл", "Мл", "г", "Г", "кг", "Кг", "шт", "Шт", ",", " ", ".")).toDouble()
|
return removeSubstringsFromString(
|
||||||
|
netWeight,
|
||||||
|
arrayOf("Л", "л", "мл", "Мл", "г", "Г", "кг", "Кг", "шт", "Шт", ",", " ", ".")
|
||||||
|
).toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeSubstringsFromString(text: String, toRemove: Array<String>): String {
|
fun removeSubstringsFromString(text: String, toRemove: Array<String>): String {
|
||||||
|
@ -60,8 +89,6 @@ fun removeSubstringsFromString(text: String, toRemove: Array<String>): String {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.utf8(): String = URLEncoder.encode(this, "UTF-8")
|
|
||||||
|
|
||||||
fun getActivity(context: Context?): Activity? {
|
fun getActivity(context: Context?): Activity? {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
return null
|
return null
|
||||||
|
@ -85,13 +112,104 @@ fun calculateProductFreshness(dateOfProduction: Long, dateOfExpiry: Long): Doubl
|
||||||
return lifeSpanLeft / productLifeSpan.toDouble()
|
return lifeSpanLeft / productLifeSpan.toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUnitNameById (context: Context, id: Int): String {
|
fun getUnitNameById(context: Context, id: Int): String {
|
||||||
return when(id) {
|
return when (id) {
|
||||||
0 -> { context.getString(R.string.kilogram) }
|
0 -> {
|
||||||
1 -> { context.getString(R.string.gram) }
|
context.getString(R.string.kilogram)
|
||||||
2 -> { context.getString(R.string.liter) }
|
}
|
||||||
3 -> { context.getString(R.string.milliliter) }
|
|
||||||
4 -> { context.getString(R.string.pieces) }
|
1 -> {
|
||||||
else -> { "" }
|
context.getString(R.string.gram)
|
||||||
|
}
|
||||||
|
|
||||||
|
2 -> {
|
||||||
|
context.getString(R.string.liter)
|
||||||
|
}
|
||||||
|
|
||||||
|
3 -> {
|
||||||
|
context.getString(R.string.milliliter)
|
||||||
|
}
|
||||||
|
|
||||||
|
4 -> {
|
||||||
|
context.getString(R.string.pieces)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun parseArray(input: String): IntArray {
|
||||||
|
return input.trim('[', ']').split(",").map { it.trim().toInt() }.toIntArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateMd5Hash(file: File): String {
|
||||||
|
val digest = MessageDigest.getInstance("MD5")
|
||||||
|
val fis = FileInputStream(file)
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
var bytesRead = fis.read(buffer)
|
||||||
|
while (bytesRead != -1) {
|
||||||
|
digest.update(buffer, 0, bytesRead)
|
||||||
|
bytesRead = fis.read(buffer)
|
||||||
|
}
|
||||||
|
fis.close()
|
||||||
|
return bytesToHex(digest.digest())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bytesToHex(bytes: ByteArray): String {
|
||||||
|
val hexString = StringBuilder()
|
||||||
|
for (byte in bytes) {
|
||||||
|
val hex = Integer.toHexString(0xff and byte.toInt())
|
||||||
|
if (hex.length == 1) {
|
||||||
|
hexString.append('0')
|
||||||
|
}
|
||||||
|
hexString.append(hex)
|
||||||
|
}
|
||||||
|
return hexString.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isInternetConnectionAvailable(context: Context): Boolean {
|
||||||
|
if (context.getSystemService(Context.CONNECTIVITY_SERVICE) == null) return false
|
||||||
|
val connectivityManager =
|
||||||
|
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
if (connectivityManager != null) {
|
||||||
|
val capabilities =
|
||||||
|
connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
|
||||||
|
if (capabilities != null) {
|
||||||
|
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||||
|
return true
|
||||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||||
|
return true
|
||||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun noInternetConnectionAvailableNotification(context: Context) {
|
||||||
|
(context as Activity).runOnUiThread {
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setMessage(context.getString(R.string.no_internet_connection))
|
||||||
|
.setPositiveButton(R.string.quit) { _, _ ->
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
.setNeutralButton(R.string.logout) { _, _ ->
|
||||||
|
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
context,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
sharedPreferences.edit().putString("token", "").apply()
|
||||||
|
sharedPreferences.edit().putString("server", "").apply()
|
||||||
|
sharedPreferences.edit().putStringSet("groups", emptySet()).apply()
|
||||||
|
sharedPreferences.edit().putString("currentGroup", "").apply()
|
||||||
|
exitProcess(0)
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import okhttp3.*
|
||||||
|
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class WebSocketClient(private val context: Context, private val server: String) {
|
||||||
|
|
||||||
|
private lateinit var webSocket: WebSocket
|
||||||
|
|
||||||
|
fun connect(token: String, currentGroup: String) {
|
||||||
|
val client = OkHttpClient.Builder()
|
||||||
|
.connectTimeout(5, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(1, TimeUnit.MINUTES)
|
||||||
|
.writeTimeout(10, TimeUnit.SECONDS)
|
||||||
|
.callTimeout(1, TimeUnit.MINUTES)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("wss://$server")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
client.newWebSocket(request, object : WebSocketListener() {
|
||||||
|
override fun onOpen(webSocket: WebSocket, response: Response) {
|
||||||
|
this@WebSocketClient.webSocket = webSocket
|
||||||
|
sendMessage("{\"token\":\"$token\",\"currentGroup\":\"$currentGroup\"}")
|
||||||
|
keepAlive()
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||||
|
val payload = JSONObject(text)
|
||||||
|
|
||||||
|
val item = payload["item"] as String
|
||||||
|
val data = payload["data"] as JSONObject
|
||||||
|
val action = payload["action"] as String
|
||||||
|
val currentGroup = payload["groupId"] as String
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(context, currentGroup)
|
||||||
|
val abstractProductDAO = AbstractProductDAO(dbHelper)
|
||||||
|
val productDAO = ProductDAO(dbHelper)
|
||||||
|
val categoryDAO = CategoryDAO(dbHelper)
|
||||||
|
|
||||||
|
when (action) {
|
||||||
|
"create" -> {
|
||||||
|
when (item) {
|
||||||
|
"abstractproduct" -> {
|
||||||
|
val newAbstractProduct = AbstractProduct.createFromJSON(data)
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
net.server = server
|
||||||
|
net.token = token
|
||||||
|
|
||||||
|
val picturesDir = File(context.filesDir, "pictures")
|
||||||
|
picturesDir.mkdirs()
|
||||||
|
|
||||||
|
val pictureFile =
|
||||||
|
File(picturesDir, "${data["image_filename"]}.png")
|
||||||
|
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${data["local_id"]}"
|
||||||
|
net.downloadImage(url, pictureFile)
|
||||||
|
|
||||||
|
abstractProductDAO.addAbstractProduct(newAbstractProduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
"product" -> {
|
||||||
|
val newProduct = Product.createFromJSON(data)
|
||||||
|
productDAO.insertNewProduct(newProduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
"category" -> {
|
||||||
|
val newCategory = Category.createFromJSON(data)
|
||||||
|
categoryDAO.addCategory(newCategory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"update" -> {
|
||||||
|
when (item) {
|
||||||
|
"abstractproduct" -> {
|
||||||
|
val updatedAbstractProduct = AbstractProduct.createFromJSON(data)
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
net.server = server
|
||||||
|
net.token = token
|
||||||
|
|
||||||
|
val picturesDir = File(context.filesDir, "pictures")
|
||||||
|
picturesDir.mkdirs()
|
||||||
|
|
||||||
|
val pictureFile =
|
||||||
|
File(picturesDir, "${data["image_filename"]}.png")
|
||||||
|
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${data["local_id"]}"
|
||||||
|
net.downloadImage(url, pictureFile)
|
||||||
|
|
||||||
|
abstractProductDAO.updateAbstractProduct(updatedAbstractProduct)
|
||||||
|
}
|
||||||
|
"product" -> {
|
||||||
|
val updatedProduct = Product.createFromJSON(data)
|
||||||
|
productDAO.updateProduct(updatedProduct)
|
||||||
|
}
|
||||||
|
"category" -> {
|
||||||
|
val updatedCategory = Category.createFromJSON(data)
|
||||||
|
categoryDAO.updateCategory(updatedCategory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"delete" -> {
|
||||||
|
val id = data["local_id"] as String
|
||||||
|
|
||||||
|
when(item) {
|
||||||
|
"abstractproduct" -> {
|
||||||
|
abstractProductDAO.eraseAbstractProduct(id.toInt(), context)
|
||||||
|
}
|
||||||
|
"product" -> {
|
||||||
|
productDAO.eraseProduct(id.toInt())
|
||||||
|
}
|
||||||
|
"category" -> {
|
||||||
|
categoryDAO.eraseCategory(id.toInt(), context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(context as MainActivity).updateAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
|
||||||
|
Log.d("QWERTYUIOP", "Closing ws. Reason: $reason")
|
||||||
|
noInternetConnectionAvailableNotification(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
||||||
|
Log.d("QWERTYUIOP","Connection failed: ${t.message}")
|
||||||
|
noInternetConnectionAvailableNotification(context)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun keepAlive() {
|
||||||
|
Thread {
|
||||||
|
while (true) {
|
||||||
|
Thread.sleep(30000)
|
||||||
|
webSocket.send("keepalive")
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendMessage(message: String) {
|
||||||
|
webSocket.send(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun close() {
|
||||||
|
webSocket.close(1000, "Closing WebSocket connection")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
|
import org.foxarmy.barcodescannerforemployees.databinding.ActivityAccountSettingsBinding
|
||||||
|
|
||||||
|
class AccountSettingsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityAccountSettingsBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityAccountSettingsBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
||||||
|
|
||||||
|
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
masterKeyAlias,
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
|
||||||
|
net.server = sharedPreferences.getString("server", "bsfe.foxarmy.org")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "")!!
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
|
||||||
|
binding.saveUsernameButton.setOnClickListener {
|
||||||
|
val response = net.changeUsername(binding.newUsernameTextEdit.text.toString())
|
||||||
|
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.savePasswordButton.setOnClickListener {
|
||||||
|
val response = net.changePassword(binding.newPasswordTextEdit.text.toString())
|
||||||
|
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,14 @@
|
||||||
package org.foxarmy.barcodescannerforemployees.activities
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
import android.content.ContentValues
|
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import android.media.ExifInterface
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.BaseColumns
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
@ -14,15 +17,23 @@ 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.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
import com.journeyapps.barcodescanner.ScanContract
|
import com.journeyapps.barcodescanner.ScanContract
|
||||||
import com.journeyapps.barcodescanner.ScanIntentResult
|
import com.journeyapps.barcodescanner.ScanIntentResult
|
||||||
import com.journeyapps.barcodescanner.ScanOptions
|
import com.journeyapps.barcodescanner.ScanOptions
|
||||||
|
import okhttp3.Response
|
||||||
import org.foxarmy.barcodescannerforemployees.*
|
import org.foxarmy.barcodescannerforemployees.*
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
class AddAbstractProductActivity : AppCompatActivity() {
|
class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
private lateinit var imageView: ImageView
|
private lateinit var imageView: ImageView
|
||||||
|
@ -47,11 +58,24 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private var scanningBarcode = false
|
private var scanningBarcode = false
|
||||||
|
|
||||||
|
private lateinit var DAO: AbstractProductDAO
|
||||||
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContentView(R.layout.fragment_add_abstract_product)
|
setContentView(R.layout.fragment_add_abstract_product)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!)
|
||||||
|
DAO = AbstractProductDAO(dbHelper)
|
||||||
|
|
||||||
picturesPath = File(filesDir, "pictures")
|
picturesPath = File(filesDir, "pictures")
|
||||||
val thumbnailsDir = File(cacheDir, "thumbnails")
|
val thumbnailsDir = File(cacheDir, "thumbnails")
|
||||||
|
@ -75,9 +99,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
fillupUnitsSpinner()
|
fillupUnitsSpinner()
|
||||||
|
|
||||||
noBarcodeCheckBox.setOnClickListener {
|
noBarcodeCheckBox.setOnClickListener {
|
||||||
if (noBarcodeCheckBox.isChecked) {
|
barcodeText.isEnabled = !noBarcodeCheckBox.isChecked
|
||||||
barcodeText.setText("")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
barcodeText.addTextChangedListener {
|
barcodeText.addTextChangedListener {
|
||||||
|
@ -88,24 +110,28 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
action = extras!!.get("action") as String
|
action = extras!!.get("action") as String
|
||||||
when (action) {
|
when (action) {
|
||||||
"update" -> {
|
"update" -> {
|
||||||
abstractProduct = extras.get("abstractProduct") as AbstractProduct?
|
abstractProduct = extras.get("abstractProduct") as AbstractProduct
|
||||||
}
|
}
|
||||||
|
|
||||||
"new_from_barcode" -> {
|
"new_from_barcode" -> {
|
||||||
abstractProduct = extras.get("abstractProduct") as AbstractProduct?
|
abstractProduct = extras.get("abstractProduct") as AbstractProduct
|
||||||
barcode = abstractProduct!!.barcode
|
barcode = abstractProduct!!.barcode
|
||||||
performRequest(abstractProduct!!.barcode)
|
performRequest(abstractProduct!!.barcode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abstractProduct != null) {
|
if (abstractProduct != null && action == "update") {
|
||||||
val imageThumbnailUri = getImageUri(this, File(thumbnailsDir, "${abstractProduct!!.imageHash}.webp"))
|
val imageThumbnailUri = getImageUri(this, File(thumbnailsDir, "${abstractProduct!!.imageHash}.webp"))
|
||||||
pictureFile = File(picturesPath, "${abstractProduct!!.imageHash}.png]")
|
pictureFile = File(picturesPath, "${abstractProduct!!.imageHash}.png")
|
||||||
imageView.setImageURI(imageThumbnailUri)
|
imageView.setImageURI(imageThumbnailUri)
|
||||||
barcodeText.setText(abstractProduct!!.barcode)
|
barcodeText.setText(abstractProduct!!.barcode)
|
||||||
productNameText.text = abstractProduct!!.name
|
productNameText.text = abstractProduct!!.name
|
||||||
netWeightText.text = abstractProduct!!.netWeight.toString()
|
netWeightText.text = abstractProduct!!.netWeight.toString()
|
||||||
categorySpinner.setSelection(abstractProduct!!.category)
|
categorySpinner.setSelection(abstractProduct!!.category - 1)
|
||||||
unitTypeSpinner.setSelection(abstractProduct!!.unit)
|
unitTypeSpinner.setSelection(abstractProduct!!.unit)
|
||||||
|
if (abstractProduct!!.barcode == "" || abstractProduct!!.barcode == " ") {
|
||||||
|
noBarcodeCheckBox.isChecked = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveButton.setOnClickListener {
|
saveButton.setOnClickListener {
|
||||||
|
@ -127,28 +153,46 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
if (netWeight.toString() == "" || netWeight.toString().toDoubleOrNull() == null) {
|
if (netWeight.toString() == "" || netWeight.toString().toDoubleOrNull() == null) {
|
||||||
Toast.makeText(this, getString(R.string.product_net_weight_request), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.product_net_weight_request), Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
val currentGroup: Int
|
||||||
|
val currentGroupString = sharedPreferences.getString("currentGroup", "offline")!!
|
||||||
|
|
||||||
|
lateinit var response: Response
|
||||||
|
val net = Net()
|
||||||
|
|
||||||
|
if (currentGroupString != "offline") {
|
||||||
|
currentGroup = currentGroupString.toInt()
|
||||||
|
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
|
||||||
|
} else {
|
||||||
|
currentGroup = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
val db = DBStorageController(this).writableDatabase
|
abstractProduct = AbstractProduct(
|
||||||
val values = ContentValues().apply {
|
if (abstractProduct == null) 0 else abstractProduct!!.id,
|
||||||
put(AbstractProductContract.AbstractProductEntry.BARCODE, barcode)
|
if (noBarcodeCheckBox.isChecked) "" else barcode,
|
||||||
put(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME, productName)
|
productName,
|
||||||
put(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT, netWeight.toString())
|
netWeight.toString().toDouble(),
|
||||||
put(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME, pictureFile.nameWithoutExtension)
|
pictureFile.nameWithoutExtension,
|
||||||
put(AbstractProductContract.AbstractProductEntry.CATEGORY, categorySpinner.selectedItemPosition)
|
categorySpinner.selectedItemPosition + 1,
|
||||||
put(AbstractProductContract.AbstractProductEntry.UNIT, unitTypeSpinner.selectedItemPosition)
|
unitTypeSpinner.selectedItemPosition
|
||||||
}
|
)
|
||||||
|
|
||||||
|
val pictureFile = File(File(filesDir, "pictures"), "${abstractProduct!!.imageHash}.png")
|
||||||
|
|
||||||
if (action == "update") {
|
if (action == "update") {
|
||||||
db.update(
|
DAO.updateAbstractProduct(abstractProduct!!)
|
||||||
AbstractProductContract.AbstractProductEntry.TABLE_NAME,
|
if (currentGroup > 0) response = net.updateAbstractProduct(currentGroup, abstractProduct!!, pictureFile)
|
||||||
values,
|
} else if (action == "new" || action == "new_from_barcode") {
|
||||||
"${BaseColumns._ID} = ?",
|
abstractProduct!!.id = DAO.addAbstractProduct(abstractProduct!!).toInt()
|
||||||
arrayOf(abstractProduct!!.id.toString())
|
if (currentGroup > 0) response =
|
||||||
)
|
net.uploadAbstractProduct(currentGroup, abstractProduct!!, pictureFile)
|
||||||
} else if (action == "new" || action == "new_from_barcode"){
|
|
||||||
db.insert(AbstractProductContract.AbstractProductEntry.TABLE_NAME, null, values)
|
|
||||||
}
|
}
|
||||||
|
if (currentGroup > 0) Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
|
||||||
|
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
@ -165,15 +209,12 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
|
|
||||||
fun performRequest(barcode: String) {
|
fun performRequest(barcode: String) {
|
||||||
barcodeText.setText(this.barcode)
|
barcodeText.setText(this.barcode)
|
||||||
val requester = Requester("https://ean-online.ru", "match.php")
|
val net = Net();
|
||||||
requester.request(this, barcode)
|
val result = net.requestProductFromOnlineDB(barcode)
|
||||||
|
|
||||||
var abstractProduct: AbstractProduct
|
var abstractProduct: AbstractProduct
|
||||||
|
|
||||||
if (DBStorageController(this).findAbstractProductByBarcode(
|
if (DAO.findAbstractProductByBarcode(this.barcode) != null) {
|
||||||
DBStorageController(this).readableDatabase,
|
|
||||||
this.barcode
|
|
||||||
) != null
|
|
||||||
) {
|
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setMessage(getString(R.string.abstract_product_already_exists))
|
.setMessage(getString(R.string.abstract_product_already_exists))
|
||||||
.setPositiveButton(getString(R.string.quit)) { _: DialogInterface, _: Int ->
|
.setPositiveButton(getString(R.string.quit)) { _: DialogInterface, _: Int ->
|
||||||
|
@ -182,10 +223,8 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
.setNegativeButton(getString(R.string.edit_existing)) { _: DialogInterface, _: Int ->
|
.setNegativeButton(getString(R.string.edit_existing)) { _: DialogInterface, _: Int ->
|
||||||
val addProductIntent = Intent(this, AddAbstractProductActivity::class.java)
|
val addProductIntent = Intent(this, AddAbstractProductActivity::class.java)
|
||||||
val extras = Bundle()
|
val extras = Bundle()
|
||||||
val existingAbstractProduct = DBStorageController(this).findAbstractProductByBarcode(
|
val existingAbstractProduct = DAO.findAbstractProductByBarcode(this.barcode)
|
||||||
DBStorageController(this).readableDatabase,
|
|
||||||
this.barcode
|
|
||||||
)
|
|
||||||
extras.putParcelable("abstractProduct", existingAbstractProduct)
|
extras.putParcelable("abstractProduct", existingAbstractProduct)
|
||||||
addProductIntent.putExtras(extras)
|
addProductIntent.putExtras(extras)
|
||||||
ContextCompat.startActivity(this, addProductIntent, extras)
|
ContextCompat.startActivity(this, addProductIntent, extras)
|
||||||
|
@ -194,19 +233,17 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
thread {
|
thread {
|
||||||
// Я сам в ахуях какой это костыль, пока хз как фиксить, потом придумаю :))
|
if (result == "Not found 404") {
|
||||||
while (requester.response == "") {
|
|
||||||
}
|
|
||||||
if (requester.response == "Not found 404") {
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
Toast.makeText(this, getString(R.string.no_product_in_online_database), Toast.LENGTH_LONG)
|
Toast.makeText(this, getString(R.string.no_product_in_online_database), Toast.LENGTH_LONG)
|
||||||
.show()
|
.show()
|
||||||
|
productNameText.setText("")
|
||||||
|
netWeightText.setText("")
|
||||||
}
|
}
|
||||||
return@thread
|
return@thread
|
||||||
}
|
}
|
||||||
|
|
||||||
abstractProduct = Parser().parse(requester.response)
|
abstractProduct = Parser().parse(result)
|
||||||
requester.response = ""
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
productNameText.text = abstractProduct.name
|
productNameText.text = abstractProduct.name
|
||||||
netWeightText.text = abstractProduct.netWeight.toString()
|
netWeightText.text = abstractProduct.netWeight.toString()
|
||||||
|
@ -224,40 +261,21 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
getString(R.string.pieces)
|
getString(R.string.pieces)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
val arrayAdapter =
|
val arrayAdapter =
|
||||||
ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, units)
|
ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, units)
|
||||||
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
|
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
|
||||||
unitTypeSpinner.adapter = arrayAdapter
|
unitTypeSpinner.adapter = arrayAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fillupCategorySpinner() {
|
fun fillupCategorySpinner() {
|
||||||
val db = DBStorageController(this).readableDatabase
|
|
||||||
|
|
||||||
val categories = mutableListOf("")
|
val categoriesDAO =
|
||||||
|
CategoryDAO(DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!))
|
||||||
|
|
||||||
val projection = arrayOf(
|
val categories = categoriesDAO.getAllCategories().map { category -> category.name }
|
||||||
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 =
|
val arrayAdapter =
|
||||||
ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, categories)
|
ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, categories)
|
||||||
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
|
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
|
||||||
categorySpinner.adapter = arrayAdapter
|
categorySpinner.adapter = arrayAdapter
|
||||||
}
|
}
|
||||||
|
@ -267,12 +285,34 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
if (success) {
|
if (success) {
|
||||||
//Move picture to a proper directory according to its calculated hash
|
//Move picture to a proper directory according to its calculated hash
|
||||||
val tempfile = File(filesDir, "image.png")
|
val tempfile = File(filesDir, "image.png")
|
||||||
|
|
||||||
val imageContent = tempfile.inputStream().readBytes()
|
val imageContent = tempfile.inputStream().readBytes()
|
||||||
val imageHash = imageContent.toString(Charsets.UTF_8).md5()
|
var img = BitmapFactory.decodeByteArray(imageContent, 0, imageContent.size)
|
||||||
|
|
||||||
|
val exif = ExifInterface(tempfile.absoluteFile.toString())
|
||||||
|
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||||
|
val matrix = Matrix()
|
||||||
|
|
||||||
|
when (orientation) {
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
|
||||||
|
}
|
||||||
|
val rotated = Bitmap.createBitmap(img, 0, 0, img.width, img.height, matrix, true)
|
||||||
|
|
||||||
|
val compressionFactor = sharedPreferences.getInt("compression", 1)
|
||||||
|
rotated.compress(
|
||||||
|
Bitmap.CompressFormat.WEBP_LOSSY,
|
||||||
|
100 * (15 / (16 - compressionFactor)),
|
||||||
|
FileOutputStream(tempfile)
|
||||||
|
)
|
||||||
|
|
||||||
|
val imageHash = calculateMd5Hash(tempfile)
|
||||||
|
|
||||||
pictureFile = File(picturesPath, "$imageHash.png")
|
pictureFile = File(picturesPath, "$imageHash.png")
|
||||||
Files.move(tempfile.toPath(), pictureFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
Files.move(tempfile.toPath(), pictureFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||||
tempfile.delete()
|
tempfile.delete()
|
||||||
|
|
||||||
generateThumbnailForImage(this, imageHash)
|
generateThumbnailForImage(this, imageHash)
|
||||||
|
|
||||||
imageView.setImageURI(getImageUri(this, pictureFile))
|
imageView.setImageURI(getImageUri(this, pictureFile))
|
||||||
|
@ -303,7 +343,8 @@ class AddAbstractProductActivity : AppCompatActivity() {
|
||||||
getPicture()
|
getPicture()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, getString(R.string.camera_permission_for_picture_request), Toast.LENGTH_LONG).show()
|
Toast.makeText(this, getString(R.string.camera_permission_for_picture_request), Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,40 @@
|
||||||
package org.foxarmy.barcodescannerforemployees.activities
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ContentValues
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.BaseColumns
|
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import org.foxarmy.barcodescannerforemployees.CategoriesContract
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
||||||
|
|
||||||
class AddCategoryActivity : Activity() {
|
class AddCategoryActivity : Activity() {
|
||||||
|
|
||||||
|
private lateinit var DAO: CategoryDAO
|
||||||
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContentView(R.layout.activity_add_category)
|
setContentView(R.layout.activity_add_category)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!)
|
||||||
|
DAO = CategoryDAO(dbHelper)
|
||||||
|
|
||||||
val extras = intent.extras
|
val extras = intent.extras
|
||||||
val category = extras!!.get("category") as Category?
|
val category = extras!!.get("category") as Category?
|
||||||
|
|
||||||
|
@ -25,25 +42,28 @@ class AddCategoryActivity : Activity() {
|
||||||
|
|
||||||
categoryNameTextEdit.setText(category!!.name)
|
categoryNameTextEdit.setText(category!!.name)
|
||||||
|
|
||||||
findViewById<Button>(R.id.saveButton).setOnClickListener {
|
val net = Net()
|
||||||
val db = DBStorageController(this).writableDatabase
|
|
||||||
|
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "")!!
|
||||||
|
|
||||||
|
val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt()
|
||||||
|
|
||||||
|
findViewById<Button>(R.id.saveButton).setOnClickListener {
|
||||||
if (categoryNameTextEdit.text.toString() == "") {
|
if (categoryNameTextEdit.text.toString() == "") {
|
||||||
Toast.makeText(this, getString(R.string.category_name_required), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.category_name_required), Toast.LENGTH_SHORT).show()
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
if (category.id == 0) { // Inserting new category
|
if (category.id == 0) { // Inserting new category
|
||||||
val values = ContentValues().apply {
|
val newCategory = Category(0, categoryNameTextEdit.text.toString())
|
||||||
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, categoryNameTextEdit.text.toString())
|
newCategory.id = DAO.addCategory(newCategory).toInt()
|
||||||
}
|
if (currentGroup > 0) net.uploadCategory(currentGroup, newCategory)
|
||||||
|
|
||||||
db.insert(CategoriesContract.CategoryEntry.TABLE_NAME, null, values)
|
|
||||||
} else { // Updating existing category
|
} else { // Updating existing category
|
||||||
val values = ContentValues().apply {
|
category.name = categoryNameTextEdit.text.toString()
|
||||||
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, categoryNameTextEdit.text.toString())
|
DAO.updateCategory(category)
|
||||||
}
|
if (currentGroup > 0) net.updateCategory(currentGroup, category)
|
||||||
db.update(CategoriesContract.CategoryEntry.TABLE_NAME, values, "${BaseColumns._ID} = ?", arrayOf(category.id.toString()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
finish()
|
finish()
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -12,11 +13,17 @@ 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.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
import com.journeyapps.barcodescanner.ScanContract
|
import com.journeyapps.barcodescanner.ScanContract
|
||||||
import com.journeyapps.barcodescanner.ScanIntentResult
|
import com.journeyapps.barcodescanner.ScanIntentResult
|
||||||
import com.journeyapps.barcodescanner.ScanOptions
|
import com.journeyapps.barcodescanner.ScanOptions
|
||||||
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
import okhttp3.Response
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
|
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
|
||||||
|
@ -45,11 +52,28 @@ class AddProductActivity : AppCompatActivity() {
|
||||||
private var product: Product? = null
|
private var product: Product? = null
|
||||||
private var abstractProduct: AbstractProduct? = null
|
private var abstractProduct: AbstractProduct? = null
|
||||||
|
|
||||||
|
private lateinit var productDAO: ProductDAO
|
||||||
|
private lateinit var abstractProductDAO: AbstractProductDAO
|
||||||
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!)
|
||||||
|
productDAO = ProductDAO(dbHelper)
|
||||||
|
abstractProductDAO = AbstractProductDAO(dbHelper)
|
||||||
|
|
||||||
|
|
||||||
scanButton = findViewById(R.id.scanButton)
|
scanButton = findViewById(R.id.scanButton)
|
||||||
noBarcodeButton = findViewById(R.id.noBarcodeButton)
|
noBarcodeButton = findViewById(R.id.noBarcodeButton)
|
||||||
abstractProductView = findViewById(R.id.abstractProductView)
|
abstractProductView = findViewById(R.id.abstractProductView)
|
||||||
|
@ -70,7 +94,7 @@ class AddProductActivity : AppCompatActivity() {
|
||||||
product = extras!!.get("product") as Product?
|
product = extras!!.get("product") as Product?
|
||||||
|
|
||||||
if (product != null) {
|
if (product != null) {
|
||||||
abstractProduct = DBStorageController(this).findAbstractProductById(DBStorageController(this).readableDatabase, product!!.abstractProductId)
|
abstractProduct = abstractProductDAO.findAbstractProductById(product!!.abstractProductId)
|
||||||
abstractProductView.abstractProduct = abstractProduct!!
|
abstractProductView.abstractProduct = abstractProduct!!
|
||||||
expiryDateRadioButton.isSelected = true
|
expiryDateRadioButton.isSelected = true
|
||||||
shelfLifeRadioButton.isSelected = false
|
shelfLifeRadioButton.isSelected = false
|
||||||
|
@ -116,6 +140,12 @@ class AddProductActivity : AppCompatActivity() {
|
||||||
product!!.amount = text.toInt()
|
product!!.amount = text.toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
|
||||||
saveProductButton.setOnClickListener {
|
saveProductButton.setOnClickListener {
|
||||||
if (abstractProduct == null) {
|
if (abstractProduct == null) {
|
||||||
Toast.makeText(this, getString(R.string.abstract_product_request), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.abstract_product_request), Toast.LENGTH_SHORT).show()
|
||||||
|
@ -147,12 +177,22 @@ class AddProductActivity : AppCompatActivity() {
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt()
|
||||||
|
|
||||||
|
var response: Response? = null
|
||||||
|
|
||||||
if (updatingExistentProduct) {
|
if (updatingExistentProduct) {
|
||||||
DBStorageController(this).updateProduct(DBStorageController(this).writableDatabase, product!!)
|
productDAO.updateProduct(product!!)
|
||||||
|
if (currentGroup > 0) response = net.updateProduct(currentGroup, product!!)
|
||||||
} else {
|
} else {
|
||||||
DBStorageController(this).insertNewProduct(DBStorageController(this).writableDatabase, product!!)
|
product!!.id = productDAO.insertNewProduct(product!!).toInt()
|
||||||
|
if (currentGroup > 0) response = net.uploadProduct(currentGroup, product!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
update()
|
update()
|
||||||
|
@ -160,8 +200,11 @@ class AddProductActivity : AppCompatActivity() {
|
||||||
val today = SimpleDateFormat("dd.MM.yyyy").format(Calendar.getInstance().time).split(".")
|
val today = SimpleDateFormat("dd.MM.yyyy").format(Calendar.getInstance().time).split(".")
|
||||||
|
|
||||||
|
|
||||||
dateOfProductionDatePicker.updateDate(today[2].toInt(), today[1].toInt(), today[0].toInt())
|
dateOfProductionDatePicker.updateDate(today[2].toInt(), today[1].toInt() - 1, today[0].toInt())
|
||||||
expiryDatePicker.updateDate(today[2].toInt(), today[1].toInt(), today[0].toInt())
|
expiryDatePicker.updateDate(today[2].toInt(), today[1].toInt() - 1, today[0].toInt())
|
||||||
|
product!!.dateOfProduction = SimpleDateFormat("dd.MM.yyyy").parse("${today[0]}.${today[1]}.${today[2]}")!!.time / 1000
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val intentLauncher =
|
private val intentLauncher =
|
||||||
|
@ -245,7 +288,7 @@ class AddProductActivity : AppCompatActivity() {
|
||||||
Toast.makeText(this, getString(R.string.cancelled), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.cancelled), Toast.LENGTH_SHORT).show()
|
||||||
} else {
|
} else {
|
||||||
val scannedBarcode = result.contents
|
val scannedBarcode = result.contents
|
||||||
abstractProduct = DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, scannedBarcode)
|
abstractProduct = abstractProductDAO.findAbstractProductByBarcode(scannedBarcode)
|
||||||
|
|
||||||
displayAbstractProduct(abstractProduct, scannedBarcode)
|
displayAbstractProduct(abstractProduct, scannedBarcode)
|
||||||
if (abstractProduct != null) {
|
if (abstractProduct != null) {
|
||||||
|
|
|
@ -2,23 +2,41 @@ package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
|
||||||
import org.foxarmy.barcodescannerforemployees.databinding.ActivityExpiryCalendarBinding
|
import org.foxarmy.barcodescannerforemployees.databinding.ActivityExpiryCalendarBinding
|
||||||
import org.foxarmy.barcodescannerforemployees.views.ExpiryGroupView
|
import org.foxarmy.barcodescannerforemployees.views.ExpiryGroupView
|
||||||
|
|
||||||
class ExpiryCalendarActivity : AppCompatActivity() {
|
class ExpiryCalendarActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityExpiryCalendarBinding
|
private lateinit var binding: ActivityExpiryCalendarBinding
|
||||||
|
|
||||||
|
private lateinit var productDAO: ProductDAO
|
||||||
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContentView(R.layout.fragment_expiry_dates)
|
setContentView(R.layout.fragment_expiry_dates)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!)
|
||||||
|
productDAO = ProductDAO(dbHelper)
|
||||||
|
|
||||||
fillUp()
|
fillUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +49,7 @@ class ExpiryCalendarActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillUp() {
|
private fun fillUp() {
|
||||||
val dates = DBStorageController(this).findAllExpiryDates(DBStorageController(this).readableDatabase)
|
val dates = productDAO.findAllExpiryDates()
|
||||||
|
|
||||||
val container = findViewById<LinearLayout>(R.id.datesLinearLayout)
|
val container = findViewById<LinearLayout>(R.id.datesLinearLayout)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ class FindBarcodelessAbstractProduct() : AppCompatActivity() {
|
||||||
binding = ActivityFindBarcodelessAbstractProductBinding.inflate(layoutInflater)
|
binding = ActivityFindBarcodelessAbstractProductBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
val ft = supportFragmentManager.beginTransaction()
|
val ft = supportFragmentManager.beginTransaction()
|
||||||
val fragment = StorageFragment.newInstance("barcodeless", arrayOf(""))
|
val fragment = StorageFragment.newInstance("barcodeless", arrayOf("", " "))
|
||||||
ft.replace(R.id.content, fragment)
|
ft.replace(R.id.content, fragment)
|
||||||
ft.commit()
|
ft.commit()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
|
import org.foxarmy.barcodescannerforemployees.databinding.ActivityGroupBinding
|
||||||
|
import org.foxarmy.barcodescannerforemployees.noInternetConnectionAvailableNotification
|
||||||
|
|
||||||
|
class GroupActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityGroupBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityGroupBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.createGroupButton.setOnClickListener {
|
||||||
|
val groupName = binding.groupNameTextEdit.text.toString()
|
||||||
|
val groupPassword = binding.groupPasswordTextEdit.text.toString()
|
||||||
|
val net = Net()
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
|
||||||
|
if (!net.serverIsAvailable(this)) {
|
||||||
|
noInternetConnectionAvailableNotification(this)
|
||||||
|
} else {
|
||||||
|
val response = net.createGroup(groupName, groupPassword)
|
||||||
|
val responseText = response.body!!.string()
|
||||||
|
|
||||||
|
if (response.code == 200) {
|
||||||
|
val currentGroups = sharedPreferences.getStringSet("groups", mutableSetOf())
|
||||||
|
currentGroups!!.add(responseText)
|
||||||
|
sharedPreferences.edit().putStringSet("groups", currentGroups).apply()
|
||||||
|
sharedPreferences.edit().putString("currentGroup", responseText).apply()
|
||||||
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, responseText, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.joinGroupButton.setOnClickListener {
|
||||||
|
val groupName = binding.groupNameTextEdit.text.toString()
|
||||||
|
val groupPassword = binding.groupPasswordTextEdit.text.toString()
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
|
||||||
|
if (!net.serverIsAvailable(this)) {
|
||||||
|
noInternetConnectionAvailableNotification(this)
|
||||||
|
} else {
|
||||||
|
val requestGroupIdResponse = net.getGroupId(groupName)
|
||||||
|
var groupId: Int
|
||||||
|
try {
|
||||||
|
groupId = requestGroupIdResponse.toInt()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(this, requestGroupIdResponse, Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
val response = net.joinGroup(groupId, groupPassword)
|
||||||
|
val responseText = response.body!!.string()
|
||||||
|
|
||||||
|
if (response.code == 200) {
|
||||||
|
val currentGroups = sharedPreferences.getStringSet("groups", mutableSetOf())
|
||||||
|
currentGroups!!.add(groupId.toString())
|
||||||
|
sharedPreferences.edit().putStringSet("groups", currentGroups).apply()
|
||||||
|
sharedPreferences.edit().putString("currentGroup", groupId.toString()).apply()
|
||||||
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, responseText, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.databinding.ActivityLoginBinding
|
||||||
|
import org.foxarmy.barcodescannerforemployees.noInternetConnectionAvailableNotification
|
||||||
|
import org.foxarmy.barcodescannerforemployees.parseArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class LoginActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityLoginBinding;
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityLoginBinding.inflate(layoutInflater);
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
fillUpLanguagesSpinner()
|
||||||
|
|
||||||
|
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
binding.loginButton.setOnClickListener {
|
||||||
|
val server = binding.serverTextEdit.text.toString()
|
||||||
|
val username = binding.usernameTextEdit.text.toString()
|
||||||
|
val password = binding.passwordTextEdit.text.toString()
|
||||||
|
val language = resources.getStringArray(R.array.languages)[binding.languageSpinner.selectedItemPosition]
|
||||||
|
sharedPreferences.edit().putString("language", language).apply()
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
net.server = server
|
||||||
|
|
||||||
|
if (!net.serverIsAvailable(this)) {
|
||||||
|
noInternetConnectionAvailableNotification(this)
|
||||||
|
} else {
|
||||||
|
val response = net.login(username, password)
|
||||||
|
val responseText = response.body!!.string()
|
||||||
|
if (response.code != 200) {
|
||||||
|
Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show()
|
||||||
|
} else {
|
||||||
|
val json = JSONObject(responseText)
|
||||||
|
sharedPreferences.edit().putString("token", json["token"].toString()).apply()
|
||||||
|
net.token = json["token"].toString()
|
||||||
|
sharedPreferences.edit().putInt("userId", json["id"].toString().toInt()).apply()
|
||||||
|
sharedPreferences.edit().putString("server", server).apply()
|
||||||
|
|
||||||
|
|
||||||
|
val r = net.getMyGroups().body!!.string()
|
||||||
|
val myGroups = parseArray(r).map { a -> a.toString()}
|
||||||
|
|
||||||
|
sharedPreferences.edit().putStringSet("groups", myGroups.toSet()).apply()
|
||||||
|
sharedPreferences.edit().putString("currentGroup", myGroups[0]).apply()
|
||||||
|
|
||||||
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.registerButton.setOnClickListener {
|
||||||
|
val server = binding.serverTextEdit.text.toString()
|
||||||
|
val username = binding.usernameTextEdit.text.toString()
|
||||||
|
val password = binding.passwordTextEdit.text.toString()
|
||||||
|
val language = resources.getStringArray(R.array.languages)[binding.languageSpinner.selectedItemPosition]
|
||||||
|
sharedPreferences.edit().putString("language", language).apply()
|
||||||
|
sharedPreferences.edit().putString("server", server).apply()
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
net.language = language
|
||||||
|
net.server = server
|
||||||
|
|
||||||
|
if (!net.serverIsAvailable(this)) {
|
||||||
|
noInternetConnectionAvailableNotification(this)
|
||||||
|
} else {
|
||||||
|
val response = net.registerAccount(username, password);
|
||||||
|
val responseText = response.body!!.string()
|
||||||
|
if (response.code != 200) {
|
||||||
|
Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
sharedPreferences.edit().putInt("userId", responseText.toInt()).apply()
|
||||||
|
val token = JSONObject(net.login(username, password).body!!.string())["token"].toString()
|
||||||
|
sharedPreferences.edit().putString("token", token).apply()
|
||||||
|
|
||||||
|
sharedPreferences.edit().putString("server", server).apply()
|
||||||
|
val intent = Intent(this, GroupActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.offlineButton.setOnClickListener {
|
||||||
|
sharedPreferences.edit().putString("currentGroup", "offline").apply()
|
||||||
|
sharedPreferences.edit().putStringSet("groups", setOf("offline")).apply()
|
||||||
|
|
||||||
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillUpLanguagesSpinner() {
|
||||||
|
val languages = resources.getStringArray(R.array.languages)
|
||||||
|
val arrayAdapter = ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, languages)
|
||||||
|
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
|
||||||
|
binding.languageSpinner.adapter = arrayAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,26 +1,50 @@
|
||||||
package org.foxarmy.barcodescannerforemployees.activities
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
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.Toast
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import com.google.android.material.navigation.NavigationView
|
||||||
import org.foxarmy.barcodescannerforemployees.ViewPagerAdapter
|
import org.foxarmy.barcodescannerforemployees.*
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
|
||||||
import org.foxarmy.barcodescannerforemployees.databinding.ActivityMainBinding
|
import org.foxarmy.barcodescannerforemployees.databinding.ActivityMainBinding
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
import org.foxarmy.barcodescannerforemployees.fragments.CategoriesFragment
|
import org.foxarmy.barcodescannerforemployees.fragments.CategoriesFragment
|
||||||
import org.foxarmy.barcodescannerforemployees.fragments.ShelfFragment
|
import org.foxarmy.barcodescannerforemployees.fragments.ShelfFragment
|
||||||
import org.foxarmy.barcodescannerforemployees.fragments.StorageFragment
|
import org.foxarmy.barcodescannerforemployees.fragments.StorageFragment
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
|
||||||
|
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
lateinit var adapter: ViewPagerAdapter
|
private lateinit var adapter: ViewPagerAdapter
|
||||||
|
private var drawerLayout: DrawerLayout? = null
|
||||||
|
private var actionBarDrawerToggle: ActionBarDrawerToggle? = null
|
||||||
|
private lateinit var ws: WebSocketClient
|
||||||
|
|
||||||
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -28,10 +52,35 @@ class MainActivity : AppCompatActivity() {
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
setSupportActionBar(binding.toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
setupViewPager(binding.tabViewpager)
|
setupViewPager(binding.tabViewpager)
|
||||||
binding.tabTablayout.setupWithViewPager(binding.tabViewpager)
|
binding.tabTablayout.setupWithViewPager(binding.tabViewpager)
|
||||||
|
|
||||||
|
drawerLayout = binding.drawerLayout
|
||||||
|
actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, R.string.nav_open, R.string.nav_close)
|
||||||
|
|
||||||
|
drawerLayout!!.addDrawerListener(actionBarDrawerToggle!!)
|
||||||
|
actionBarDrawerToggle!!.syncState()
|
||||||
|
binding.navView.setNavigationItemSelectedListener(this)
|
||||||
|
|
||||||
|
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
|
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
binding.expiryCalendarFab.setOnClickListener { _ ->
|
binding.expiryCalendarFab.setOnClickListener { _ ->
|
||||||
val expiryCalendarIntent = Intent(this, ExpiryCalendarActivity::class.java)
|
val expiryCalendarIntent = Intent(this, ExpiryCalendarActivity::class.java)
|
||||||
val extras = Bundle()
|
val extras = Bundle()
|
||||||
|
@ -47,8 +96,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
"StorageFragment" -> {
|
"StorageFragment" -> {
|
||||||
val addAbstractProductIntent = Intent(this, AddAbstractProductActivity::class.java)
|
val addAbstractProductIntent = Intent(this, AddAbstractProductActivity::class.java)
|
||||||
val extras = Bundle()
|
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)
|
extras.putParcelable("abstractProduct", null)
|
||||||
extras.putString("action", "new")
|
extras.putString("action", "new")
|
||||||
addAbstractProductIntent.putExtras(extras)
|
addAbstractProductIntent.putExtras(extras)
|
||||||
|
@ -69,6 +116,216 @@ class MainActivity : AppCompatActivity() {
|
||||||
extras.putParcelable("product", null)
|
extras.putParcelable("product", null)
|
||||||
addProductIntent.putExtras(extras)
|
addProductIntent.putExtras(extras)
|
||||||
ContextCompat.startActivity(this, addProductIntent, extras)
|
ContextCompat.startActivity(this, addProductIntent, extras)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val net = Net()
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
val currentGroup = sharedPreferences.getString("currentGroup", "offline")!!
|
||||||
|
if ( currentGroup != "offline" && !net.serverIsAvailable(this)) {
|
||||||
|
noInternetConnectionAvailableNotification(this)
|
||||||
|
} else if (currentGroup != "offline" && net.serverIsAvailable(this)) {
|
||||||
|
synchronize()
|
||||||
|
ws = WebSocketClient(this, sharedPreferences.getString("server", "")!!)
|
||||||
|
val token = sharedPreferences.getString("token", "")!!
|
||||||
|
ws.connect(token, currentGroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun synchronize() {
|
||||||
|
if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") return
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
|
||||||
|
val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt()
|
||||||
|
|
||||||
|
val data = JSONObject(net.synchronize(currentGroup).body!!.string())
|
||||||
|
|
||||||
|
val remoteAbstractProducts = data["abstract_products"] as JSONArray
|
||||||
|
val remoteProducts = data["products"] as JSONArray
|
||||||
|
val remoteCategories = data["categories"] as JSONArray
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "")!!)
|
||||||
|
|
||||||
|
val abstractProductDAO = AbstractProductDAO(dbHelper)
|
||||||
|
val productDAO = ProductDAO(dbHelper)
|
||||||
|
val categoryDAO = CategoryDAO(dbHelper)
|
||||||
|
|
||||||
|
val localAbstractProducts = abstractProductDAO.getSortedListOfAbstractProducts(0, "", arrayOf(""))
|
||||||
|
val localProducts = productDAO.getSortedListOfProducts(0, "", "")
|
||||||
|
val localCategories = categoryDAO.getAllCategories()
|
||||||
|
|
||||||
|
syncAbstractProducts(net, currentGroup, abstractProductDAO, remoteAbstractProducts, localAbstractProducts)
|
||||||
|
syncProducts(productDAO, remoteProducts, localProducts)
|
||||||
|
syncCategories(categoryDAO, remoteCategories, localCategories)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun syncCategories(
|
||||||
|
categoryDAO: CategoryDAO,
|
||||||
|
remoteCategories: JSONArray,
|
||||||
|
localCategories: List<Category>
|
||||||
|
) {
|
||||||
|
for (localCategory in localCategories) {
|
||||||
|
var found = false
|
||||||
|
for (i in 0 until remoteCategories.length()) {
|
||||||
|
val remoteProduct = remoteCategories.getJSONObject(i)
|
||||||
|
if (remoteProduct["local_id"] == localCategory.id) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) categoryDAO.eraseCategory(localCategory.id, this)
|
||||||
|
}
|
||||||
|
for (i in 0 until remoteCategories.length()) {
|
||||||
|
var categoryInRemoteDB: JSONObject? = null
|
||||||
|
var categoryInLocalDB: Category? = null
|
||||||
|
|
||||||
|
val remoteCategory = remoteCategories.getJSONObject(i)
|
||||||
|
|
||||||
|
for (localCategory in localCategories) {
|
||||||
|
if (remoteCategory["local_id"] == localCategory.id) {
|
||||||
|
categoryInRemoteDB = remoteCategory
|
||||||
|
categoryInLocalDB = localCategory
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (categoryInLocalDB == null && remoteCategory != null) {
|
||||||
|
var newCategory: Category
|
||||||
|
|
||||||
|
with(remoteCategory) {
|
||||||
|
newCategory = Category(
|
||||||
|
this["local_id"].toString().toInt(),
|
||||||
|
this["name"].toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryDAO.addCategory(newCategory)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (categoryInRemoteDB != null && categoryInLocalDB != null) {
|
||||||
|
if (categoryInRemoteDB["hash"] != categoryInLocalDB.calculateHash()) {
|
||||||
|
val updatedData = Category.createFromJSON(categoryInRemoteDB)
|
||||||
|
|
||||||
|
categoryDAO.updateCategory(updatedData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun syncProducts(
|
||||||
|
productDAO: ProductDAO,
|
||||||
|
remoteProducts: JSONArray,
|
||||||
|
localProducts: List<Product>
|
||||||
|
) {
|
||||||
|
for (localProduct in localProducts) {
|
||||||
|
var found = false
|
||||||
|
for (i in 0 until remoteProducts.length()) {
|
||||||
|
val remoteProduct = remoteProducts.getJSONObject(i)
|
||||||
|
if (remoteProduct["local_id"] == localProduct.id) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) productDAO.eraseProduct(localProduct.id)
|
||||||
|
}
|
||||||
|
for (i in 0 until remoteProducts.length()) {
|
||||||
|
var productInRemoteDB: JSONObject? = null
|
||||||
|
var productInLocalDB: Product? = null
|
||||||
|
|
||||||
|
val remoteProduct = remoteProducts.getJSONObject(i)
|
||||||
|
|
||||||
|
for (localProduct in localProducts) {
|
||||||
|
if (remoteProduct["local_id"] == localProduct.id) {
|
||||||
|
productInRemoteDB = remoteProduct
|
||||||
|
productInLocalDB = localProduct
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productInLocalDB == null && remoteProduct != null) {
|
||||||
|
val newProduct = Product.createFromJSON(remoteProduct)
|
||||||
|
|
||||||
|
productDAO.insertNewProduct(newProduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productInRemoteDB != null && productInLocalDB != null) {
|
||||||
|
if (productInRemoteDB["hash"] != productInLocalDB.calculateHash()) {
|
||||||
|
val updatedData = Product.createFromJSON(productInRemoteDB)
|
||||||
|
|
||||||
|
productDAO.updateProduct(updatedData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun syncAbstractProducts(
|
||||||
|
net: Net,
|
||||||
|
currentGroup: Int,
|
||||||
|
abstractProductDAO: AbstractProductDAO,
|
||||||
|
remoteAbstractProducts: JSONArray,
|
||||||
|
localAbstractProducts: List<AbstractProduct>
|
||||||
|
) {
|
||||||
|
for (localAbstractProduct in localAbstractProducts) {
|
||||||
|
var found = false
|
||||||
|
for (i in 0 until remoteAbstractProducts.length()) {
|
||||||
|
val remoteAbstractProduct = remoteAbstractProducts.getJSONObject(i)
|
||||||
|
if (remoteAbstractProduct["local_id"] == localAbstractProduct.id) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) abstractProductDAO.eraseAbstractProduct(localAbstractProduct.id, this)
|
||||||
|
}
|
||||||
|
for (i in 0 until remoteAbstractProducts.length()) {
|
||||||
|
var abstractProductInRemoteDB: JSONObject? = null
|
||||||
|
var abstractProductInLocalDB: AbstractProduct? = null
|
||||||
|
|
||||||
|
val remoteAbstractProduct = remoteAbstractProducts.getJSONObject(i)
|
||||||
|
|
||||||
|
for (localAbstractProduct in localAbstractProducts) {
|
||||||
|
if (remoteAbstractProduct["local_id"] == localAbstractProduct.id) {
|
||||||
|
abstractProductInRemoteDB = remoteAbstractProduct
|
||||||
|
abstractProductInLocalDB = localAbstractProduct
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abstractProductInLocalDB == null && remoteAbstractProduct != null) {
|
||||||
|
val localId = remoteAbstractProduct["local_id"].toString().toInt()
|
||||||
|
|
||||||
|
val picturesDir = File(filesDir, "pictures")
|
||||||
|
picturesDir.mkdirs()
|
||||||
|
|
||||||
|
val pictureFile =
|
||||||
|
File(picturesDir, "${remoteAbstractProduct["image_filename"]}.png")
|
||||||
|
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${localId}"
|
||||||
|
net.downloadImage(url, pictureFile)
|
||||||
|
|
||||||
|
val newAbstractProduct = AbstractProduct.createFromJSON(remoteAbstractProduct)
|
||||||
|
|
||||||
|
abstractProductDAO.addAbstractProduct(newAbstractProduct)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abstractProductInRemoteDB != null && abstractProductInLocalDB != null) {
|
||||||
|
if (abstractProductInRemoteDB["hash"] != abstractProductInLocalDB.calculateHash()) {
|
||||||
|
val localId = abstractProductInRemoteDB["local_id"].toString().toInt()
|
||||||
|
|
||||||
|
val pictureFile =
|
||||||
|
File(File(filesDir, "pictures"), "${abstractProductInRemoteDB["image_filename"]}.png")
|
||||||
|
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${localId}"
|
||||||
|
net.downloadImage(url, pictureFile)
|
||||||
|
|
||||||
|
val updatedData = AbstractProduct.createFromJSON(abstractProductInRemoteDB)
|
||||||
|
|
||||||
|
abstractProductDAO.updateAbstractProduct(updatedData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,84 +338,156 @@ class MainActivity : AppCompatActivity() {
|
||||||
adapter.addFragment(ShelfFragment(), getString(R.string.shelf_title))
|
adapter.addFragment(ShelfFragment(), getString(R.string.shelf_title))
|
||||||
adapter.addFragment(CategoriesFragment(), getString(R.string.categories_title))
|
adapter.addFragment(CategoriesFragment(), getString(R.string.categories_title))
|
||||||
|
|
||||||
//TODO: settings fragments
|
|
||||||
|
|
||||||
// setting adapter to view pager.
|
|
||||||
viewpager.adapter = adapter
|
viewpager.adapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.menu_main, menu)
|
menuInflater.inflate(R.menu.menu_main, menu)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
private fun isOffline(): Boolean {
|
||||||
val currentPosition = binding.tabTablayout.selectedTabPosition
|
return sharedPreferences.getString("currentGroup", "") == "offline"
|
||||||
val fragment = adapter.getItem(currentPosition)
|
}
|
||||||
|
|
||||||
return when (item.itemId) {
|
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||||
|
lateinit var intent: Intent
|
||||||
|
when (item.itemId) {
|
||||||
R.id.action_settings -> {
|
R.id.nav_account -> {
|
||||||
true
|
if (isOffline()) {
|
||||||
}
|
Toast.makeText(this, getString(R.string.online_only_feature), Toast.LENGTH_SHORT).show()
|
||||||
|
return false
|
||||||
R.id.action_delete -> {
|
|
||||||
|
|
||||||
when (fragment::class.simpleName.toString()) {
|
|
||||||
"StorageFragment" -> {
|
|
||||||
AlertDialog.Builder(this)
|
|
||||||
.setMessage(getString(R.string.deleting_abstract_product_warning))
|
|
||||||
.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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
true
|
intent = Intent(this, AccountSettingsActivity::class.java)
|
||||||
|
settingsActivityLauncher.launch(intent)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_update -> {
|
R.id.nav_groups -> {
|
||||||
when (fragment::class.simpleName.toString()) {
|
if (isOffline()) {
|
||||||
"StorageFragment" -> {
|
Toast.makeText(this, getString(R.string.online_only_feature), Toast.LENGTH_SHORT).show()
|
||||||
val storageFragment = fragment as StorageFragment
|
return false
|
||||||
storageFragment.updateSelected()
|
|
||||||
}
|
|
||||||
|
|
||||||
"CategoriesFragment" -> {
|
|
||||||
val categoriesFragment = fragment as CategoriesFragment
|
|
||||||
categoriesFragment.updateSelected()
|
|
||||||
}
|
|
||||||
|
|
||||||
"ShelfFragment" -> {
|
|
||||||
val shelfFragment = fragment as ShelfFragment
|
|
||||||
shelfFragment.updateSelected()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
true
|
intent = Intent(this, MyGroupsActivity::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> super.onOptionsItemSelected(item)
|
R.id.nav_settings -> {
|
||||||
|
intent = Intent(this, SettingsActivity::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.nav_logout -> {
|
||||||
|
sharedPreferences.edit().putString("currentGroup", "").apply()
|
||||||
|
sharedPreferences.edit().putStringSet("groups", emptySet()).apply()
|
||||||
|
sharedPreferences.edit().putString("token", "").apply()
|
||||||
|
sharedPreferences.edit().putString("server", "").apply()
|
||||||
|
|
||||||
|
intent = Intent(this, LoginActivity::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val settingsActivityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
|
synchronize()
|
||||||
|
updateAll()
|
||||||
|
}/* else if (result.resultCode == Activity.RESULT_CANCELED) {
|
||||||
|
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateAll() {
|
||||||
|
runOnUiThread {
|
||||||
|
try {
|
||||||
|
val storageFragment: StorageFragment = adapter.getItem(0) as StorageFragment
|
||||||
|
val shelfFragment: ShelfFragment = adapter.getItem(1) as ShelfFragment
|
||||||
|
val categoriesFragment: CategoriesFragment = adapter.getItem(2) as CategoriesFragment
|
||||||
|
|
||||||
|
storageFragment.updateContent()
|
||||||
|
shelfFragment.updateContent()
|
||||||
|
categoriesFragment.updateContent()
|
||||||
|
} catch (e:Exception) {
|
||||||
|
Log.e("BSFE/MainActivity", e.message!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (actionBarDrawerToggle!!.onOptionsItemSelected(item)) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
|
||||||
|
val currentPosition = binding.tabTablayout.selectedTabPosition
|
||||||
|
val fragment = adapter.getItem(currentPosition)
|
||||||
|
|
||||||
|
return when (item.itemId) {
|
||||||
|
R.id.action_settings -> {
|
||||||
|
val intent = Intent(this, SettingsActivity::class.java)
|
||||||
|
settingsActivityLauncher.launch(intent)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_delete -> {
|
||||||
|
when (fragment::class.simpleName.toString()) {
|
||||||
|
"StorageFragment" -> {
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setMessage(getString(R.string.deleting_abstract_product_warning))
|
||||||
|
.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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterAbstractProductsByCategory(id: Int) {
|
fun filterAbstractProductsByCategory(id: Int) {
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
|
import org.foxarmy.barcodescannerforemployees.databinding.ActivityManageGroupBinding
|
||||||
|
import org.foxarmy.barcodescannerforemployees.parseArray
|
||||||
|
import org.foxarmy.barcodescannerforemployees.views.GroupMemberView
|
||||||
|
|
||||||
|
class ManageGroupActivity : AppCompatActivity(){
|
||||||
|
private lateinit var binding: ActivityManageGroupBinding
|
||||||
|
private var isAdmin: Boolean = true
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityManageGroupBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
val groupId = intent.extras!!.getInt("groupId")
|
||||||
|
|
||||||
|
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
||||||
|
|
||||||
|
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
masterKeyAlias,
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
|
||||||
|
|
||||||
|
val users = parseArray(net.getUsersInGroup(groupId).body!!.string())
|
||||||
|
|
||||||
|
for (user in users) {
|
||||||
|
val groupMemberView = GroupMemberView(this, this as Context, net.getUsernameById(user).body!!.string(), user)
|
||||||
|
|
||||||
|
binding.groupsContent.addView(groupMemberView)
|
||||||
|
}
|
||||||
|
|
||||||
|
isAdmin = amIAnAdminIn(groupId)
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
binding.renameButton.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun amIAnAdminIn(groupId: Int): Boolean {
|
||||||
|
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
|
||||||
|
val result = sharedPreferences.getInt("userId", 0) == net.getGroupAdminId(groupId).body!!.string().toInt()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.databinding.ActivityMyGroupsBinding
|
||||||
|
import org.foxarmy.barcodescannerforemployees.parseArray
|
||||||
|
import org.foxarmy.barcodescannerforemployees.views.GroupView
|
||||||
|
|
||||||
|
class MyGroupsActivity : AppCompatActivity(){
|
||||||
|
private lateinit var binding: ActivityMyGroupsBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityMyGroupsBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val net = Net()
|
||||||
|
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
|
||||||
|
val groups = parseArray(net.getMyGroups().body!!.string())
|
||||||
|
|
||||||
|
val container = findViewById<LinearLayout>(R.id.groupsLayout)
|
||||||
|
|
||||||
|
for (group in groups) {
|
||||||
|
val groupView = GroupView(this, this as Context, net.getGroupName(group), group)
|
||||||
|
|
||||||
|
|
||||||
|
container.addView(groupView)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.newGroup.setOnClickListener {
|
||||||
|
val intent = Intent(this, GroupActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
|
||||||
|
class NavigatorActivity : Activity() {
|
||||||
|
|
||||||
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
var intent = Intent(this, MainActivity::class.java)
|
||||||
|
|
||||||
|
if (!isInGroup() && !isOffline()) {
|
||||||
|
intent = Intent(this, GroupActivity::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAuthenticated() && !isOffline()) {
|
||||||
|
intent = Intent(this, LoginActivity::class.java);
|
||||||
|
}
|
||||||
|
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isOffline(): Boolean {
|
||||||
|
return sharedPreferences.getString("currentGroup", "")!! == "offline"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isAuthenticated(): Boolean {
|
||||||
|
return sharedPreferences.getString("token", "") != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isInGroup(): Boolean {
|
||||||
|
return sharedPreferences.getStringSet("groups", emptySet())!!.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
|
import org.foxarmy.barcodescannerforemployees.databinding.ActivitySettingsBinding
|
||||||
|
|
||||||
|
class SettingsActivity : AppCompatActivity() {
|
||||||
|
lateinit var binding: ActivitySettingsBinding
|
||||||
|
lateinit var sharedPreferences: SharedPreferences
|
||||||
|
lateinit var myGroups: List<String>
|
||||||
|
lateinit var currentGroup: String
|
||||||
|
lateinit var groupsNames: MutableList<String>
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
applicationContext,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
myGroups = sharedPreferences.getStringSet("groups", emptySet())!!.toList()
|
||||||
|
currentGroup = sharedPreferences.getString("currentGroup", "offline")!!
|
||||||
|
val net = Net()
|
||||||
|
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
|
||||||
|
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
setUpImageCompressionSeekBar()
|
||||||
|
|
||||||
|
|
||||||
|
if (isOffline()) {
|
||||||
|
binding.currentGroupSetting.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
groupsNames = mutableListOf()
|
||||||
|
|
||||||
|
for (myGroup in myGroups) {
|
||||||
|
groupsNames.add(net.getGroupName(myGroup.toInt()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fillUpCurrentGroupSpinner()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.saveButton.setOnClickListener {
|
||||||
|
sharedPreferences.edit().putString("currentGroup", net.getGroupId(binding.currentGroupSpinner.selectedItem.toString())).apply()
|
||||||
|
sharedPreferences.edit().putInt("imageCompression", binding.imageCompressionFactorSeekBar.progress + 1).apply()
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.cancelButton.setOnClickListener {
|
||||||
|
setResult(Activity.RESULT_CANCELED)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isOffline(): Boolean {
|
||||||
|
return sharedPreferences.getString("currentGroup", "offline") == "offline"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpImageCompressionSeekBar() {
|
||||||
|
val compressionFactor = sharedPreferences.getInt("imageCompression", 1)
|
||||||
|
binding.imageCompressionFactorSeekBar.progress = compressionFactor - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillUpCurrentGroupSpinner() {
|
||||||
|
if (currentGroup == "offline") {
|
||||||
|
binding.currentGroupSetting.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
val arrayAdapter =
|
||||||
|
ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, groupsNames)
|
||||||
|
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
|
||||||
|
binding.currentGroupSpinner.adapter = arrayAdapter
|
||||||
|
binding.currentGroupSpinner.setSelection(myGroups.indexOf(currentGroup))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,348 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.database
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.DatabaseUtils
|
||||||
|
import android.provider.BaseColumns
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class AbstractProductDAO(private val dbHelper: DBStorageController) {
|
||||||
|
|
||||||
|
fun getAllAbstractProductInCategory(id: Int): List<AbstractProduct> {
|
||||||
|
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
|
||||||
|
val 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(android.provider.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(id: Int): Int {
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
return DatabaseUtils.longForQuery(
|
||||||
|
db,
|
||||||
|
"SELECT COUNT(*) FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?",
|
||||||
|
arrayOf(id.toString())
|
||||||
|
).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun eraseAbstractProduct(id: Int, context: Context) {
|
||||||
|
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
var imageHash: String = ""
|
||||||
|
|
||||||
|
with(cursorForAbstractProduct) {
|
||||||
|
while (moveToNext()) {
|
||||||
|
val productImageHash =
|
||||||
|
getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME))
|
||||||
|
imageHash = productImageHash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val picturesDir = File(context.filesDir, "pictures")
|
||||||
|
val thumbnailsDir = File(context.cacheDir, "thumbnails")
|
||||||
|
|
||||||
|
File(picturesDir, "$imageHash.png").delete()
|
||||||
|
File(thumbnailsDir, "$imageHash.webp").delete()
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
ProductDAO(dbHelper).eraseProduct(getInt(getColumnIndexOrThrow(android.provider.BaseColumns._ID)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.delete(AbstractProductContract.AbstractProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findAbstractProductByBarcode(barcode: String): AbstractProduct? {
|
||||||
|
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
|
||||||
|
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(id: Int): AbstractProduct? {
|
||||||
|
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
|
||||||
|
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(android.provider.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 addAbstractProduct(abstractProduct: AbstractProduct): Long {
|
||||||
|
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
if (abstractProduct.id > 0) put(BaseColumns._ID, abstractProduct.id)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.BARCODE, abstractProduct.barcode)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME, abstractProduct.name)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT, abstractProduct.netWeight.toString())
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME, abstractProduct.imageHash)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.CATEGORY, abstractProduct.category)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.UNIT, abstractProduct.unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
val id = db.insert(AbstractProductContract.AbstractProductEntry.TABLE_NAME, null, values)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateAbstractProduct(abstractProduct: AbstractProduct) {
|
||||||
|
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(BaseColumns._ID, abstractProduct.id)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.BARCODE, abstractProduct.barcode)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME, abstractProduct.name)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT, abstractProduct.netWeight.toString())
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME, abstractProduct.imageHash)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.CATEGORY, abstractProduct.category)
|
||||||
|
put(AbstractProductContract.AbstractProductEntry.UNIT, abstractProduct.unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.update(
|
||||||
|
AbstractProductContract.AbstractProductEntry.TABLE_NAME,
|
||||||
|
values,
|
||||||
|
"${BaseColumns._ID} = ?",
|
||||||
|
arrayOf(abstractProduct.id.toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSortedListOfAbstractProducts(
|
||||||
|
selectedSort: Int,
|
||||||
|
filterBy: String,
|
||||||
|
filter: Array<String>
|
||||||
|
): List<AbstractProduct> {
|
||||||
|
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
|
||||||
|
val abstractProducts = mutableListOf<AbstractProduct>()
|
||||||
|
|
||||||
|
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 (selectedSort) {
|
||||||
|
0 -> {
|
||||||
|
orderBy = "${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} ASC"
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> {
|
||||||
|
orderBy = "${AbstractProductContract.AbstractProductEntry.CATEGORY} ASC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var selection = ""
|
||||||
|
var selectionArgs: Array<String>? = null
|
||||||
|
|
||||||
|
when (filterBy) {
|
||||||
|
"category" -> {
|
||||||
|
selection = "${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?"
|
||||||
|
selectionArgs = filter
|
||||||
|
}
|
||||||
|
|
||||||
|
"barcodeless" -> {
|
||||||
|
selection = "${AbstractProductContract.AbstractProductEntry.BARCODE} = ? OR ${AbstractProductContract.AbstractProductEntry.BARCODE} = ?"
|
||||||
|
selectionArgs = filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val cursor = db.query(
|
||||||
|
AbstractProductContract.AbstractProductEntry.TABLE_NAME,
|
||||||
|
projection,
|
||||||
|
selection,
|
||||||
|
selectionArgs,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
orderBy
|
||||||
|
)
|
||||||
|
|
||||||
|
with(cursor) {
|
||||||
|
while (moveToNext()) {
|
||||||
|
val productId = getInt(getColumnIndexOrThrow(android.provider.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)
|
||||||
|
|
||||||
|
abstractProducts.add(product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return abstractProducts
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.database
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.provider.BaseColumns
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
||||||
|
|
||||||
|
class CategoryDAO(private val dbHelper: DBStorageController) {
|
||||||
|
fun eraseCategory(id: Int, context: Context) {
|
||||||
|
|
||||||
|
val abstractProductDAO = AbstractProductDAO(dbHelper)
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
|
||||||
|
val productsInCategory = abstractProductDAO.getAllAbstractProductInCategory(id)
|
||||||
|
|
||||||
|
for (product in productsInCategory.iterator()) {
|
||||||
|
abstractProductDAO.eraseAbstractProduct(product.id, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.delete(CategoriesContract.CategoryEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCategoryNameById(id: Int): String {
|
||||||
|
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
val projection = arrayOf(CategoriesContract.CategoryEntry.CATEGORY_NAME)
|
||||||
|
val selection = "${BaseColumns._ID} = ?"
|
||||||
|
val selectionArgs = arrayOf(id.toString())
|
||||||
|
|
||||||
|
val cursor = db.query(
|
||||||
|
CategoriesContract.CategoryEntry.TABLE_NAME,
|
||||||
|
projection,
|
||||||
|
selection,
|
||||||
|
selectionArgs,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
with(cursor) {
|
||||||
|
while (moveToNext()) {
|
||||||
|
result = getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllCategories(): List<Category> {
|
||||||
|
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
|
||||||
|
val categories = mutableListOf<Category>()
|
||||||
|
|
||||||
|
val projection = arrayOf(
|
||||||
|
BaseColumns._ID,
|
||||||
|
CategoriesContract.CategoryEntry.CATEGORY_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
val cursor = db.query(
|
||||||
|
CategoriesContract.CategoryEntry.TABLE_NAME,
|
||||||
|
projection,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
BaseColumns._ID + " ASC"
|
||||||
|
)
|
||||||
|
|
||||||
|
with(cursor) {
|
||||||
|
while (moveToNext()) {
|
||||||
|
val category = Category(
|
||||||
|
getInt(getColumnIndexOrThrow(BaseColumns._ID)),
|
||||||
|
getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME))
|
||||||
|
)
|
||||||
|
categories.add(category)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addCategory(category: Category): Long {
|
||||||
|
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
if (category.id > 0) put(BaseColumns._ID, category.id)
|
||||||
|
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, category.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val id = db.insert(CategoriesContract.CategoryEntry.TABLE_NAME, null, values)
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateCategory(category: Category) {
|
||||||
|
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, category.name)
|
||||||
|
}
|
||||||
|
db.update(
|
||||||
|
CategoriesContract.CategoryEntry.TABLE_NAME,
|
||||||
|
values,
|
||||||
|
"${BaseColumns._ID} = ?",
|
||||||
|
arrayOf(category.id.toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
import android.provider.BaseColumns
|
||||||
|
|
||||||
|
object AbstractProductContract {
|
||||||
|
object AbstractProductEntry : BaseColumns {
|
||||||
|
const val TABLE_NAME = "abstract_products"
|
||||||
|
const val BARCODE = "barcode"
|
||||||
|
const val PRODUCT_NAME = "name"
|
||||||
|
const val PRODUCT_NET_WEIGHT = "net_weight"
|
||||||
|
const val IMAGE_FILENAME = "image_filename"
|
||||||
|
const val CATEGORY = "category"
|
||||||
|
const val UNIT = "unit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object CategoriesContract {
|
||||||
|
object CategoryEntry : BaseColumns {
|
||||||
|
const val TABLE_NAME = "categories"
|
||||||
|
const val CATEGORY_NAME = "category_name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ProductContract {
|
||||||
|
object ProductEntry : BaseColumns {
|
||||||
|
const val TABLE_NAME = "products"
|
||||||
|
const val ABSTRACT_PRODUCT_ID = "abstract_product_id"
|
||||||
|
const val AMOUNT = "amount"
|
||||||
|
const val DATE_OF_PRODUCTION = "date_of_production"
|
||||||
|
const val EXPIRY_DATE = "expiry_date"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const val SQL_CREATE_ABSTRACT_PRODUCTS_TABLE =
|
||||||
|
"CREATE TABLE ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} (" +
|
||||||
|
"${BaseColumns._ID} INTEGER PRIMARY KEY," +
|
||||||
|
"${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 =
|
||||||
|
"CREATE TABLE ${CategoriesContract.CategoryEntry.TABLE_NAME} (" +
|
||||||
|
"${BaseColumns._ID} INTEGER PRIMARY KEY," +
|
||||||
|
"${CategoriesContract.CategoryEntry.CATEGORY_NAME} TEXT)"
|
||||||
|
|
||||||
|
class DBStorageController(context: Context, dbname: String) :
|
||||||
|
SQLiteOpenHelper(context, dbname, null, DATABASE_VERSION) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
DATABASE_NAME = "$dbname.db"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
|
|
||||||
|
db.execSQL(SQL_CREATE_ABSTRACT_PRODUCTS_TABLE)
|
||||||
|
db.execSQL(SQL_CREATE_PRODUCTS_TABLE)
|
||||||
|
db.execSQL(SQL_CREATE_CATEGORIES_TABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUpgrade(db: SQLiteDatabase?, oldV: Int, newV: Int) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DATABASE_VERSION = 1
|
||||||
|
var DATABASE_NAME = "database.db"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.database
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.provider.BaseColumns
|
||||||
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
|
|
||||||
|
class ProductDAO (private val dbHelper: DBStorageController) {
|
||||||
|
|
||||||
|
fun eraseProduct(id: Int) {
|
||||||
|
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
|
||||||
|
db.delete(ProductContract.ProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insertNewProduct(product: Product) : Long{
|
||||||
|
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
if (product.id > 0) put(BaseColumns._ID, product.id)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
val id = db.insert(ProductContract.ProductEntry.TABLE_NAME, null, values)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findAmountOfProductsWithExpiryDate(date: Long): Int {
|
||||||
|
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
|
||||||
|
var amount = 0
|
||||||
|
|
||||||
|
val projection = arrayOf(
|
||||||
|
ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID,
|
||||||
|
ProductContract.ProductEntry.AMOUNT,
|
||||||
|
ProductContract.ProductEntry.DATE_OF_PRODUCTION,
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = "${ProductContract.ProductEntry.EXPIRY_DATE} = ?"
|
||||||
|
val selectionArgs = arrayOf(date.toString())
|
||||||
|
|
||||||
|
val cursor =
|
||||||
|
db.query(ProductContract.ProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, null)
|
||||||
|
|
||||||
|
with(cursor) {
|
||||||
|
while (moveToNext()) {
|
||||||
|
amount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findAllExpiryDates(): Set<Long> {
|
||||||
|
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
|
||||||
|
val dates: MutableSet<Long> = mutableSetOf<Long>()
|
||||||
|
|
||||||
|
val projection = arrayOf(
|
||||||
|
ProductContract.ProductEntry.EXPIRY_DATE
|
||||||
|
)
|
||||||
|
|
||||||
|
val orderBy = "${ProductContract.ProductEntry.EXPIRY_DATE} ASC"
|
||||||
|
|
||||||
|
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, null, null, null, orderBy)
|
||||||
|
|
||||||
|
with(cursor) {
|
||||||
|
while (moveToNext()) {
|
||||||
|
dates.add(getLong(getColumnIndexOrThrow(ProductContract.ProductEntry.EXPIRY_DATE)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dates
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateProduct(product: Product) {
|
||||||
|
|
||||||
|
val db = dbHelper.writableDatabase
|
||||||
|
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(BaseColumns._ID, product.id)
|
||||||
|
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())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSortedListOfProducts(selectedSort: Int, filterBy: String, filter: String): List<Product> {
|
||||||
|
|
||||||
|
val db = dbHelper.readableDatabase
|
||||||
|
|
||||||
|
val products = mutableListOf<Product>()
|
||||||
|
|
||||||
|
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 (selectedSort) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
3 -> {
|
||||||
|
orderBy = "${ProductContract.ProductEntry.DATE_OF_PRODUCTION} ASC"
|
||||||
|
}
|
||||||
|
|
||||||
|
4 -> {
|
||||||
|
orderBy = "${ProductContract.ProductEntry.EXPIRY_DATE} ASC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var selection: String? = null
|
||||||
|
var selectionArgs: Array<String>? = null
|
||||||
|
|
||||||
|
when (filterBy) {
|
||||||
|
"expiryDate" -> {
|
||||||
|
selection = "${ProductContract.ProductEntry.EXPIRY_DATE} = ?"
|
||||||
|
selectionArgs = arrayOf(filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
"" -> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy)
|
||||||
|
|
||||||
|
with(cursor) {
|
||||||
|
while (moveToNext()) {
|
||||||
|
val productId = getInt(getColumnIndexOrThrow(android.provider.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)
|
||||||
|
|
||||||
|
products.add(product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
products.sortWith(compareByDescending { it.freshness })
|
||||||
|
|
||||||
|
return products
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,8 @@ package org.foxarmy.barcodescannerforemployees.dataclasses
|
||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import org.foxarmy.barcodescannerforemployees.md5
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
class AbstractProduct() : Parcelable {
|
class AbstractProduct() : Parcelable {
|
||||||
var id: Int = 0
|
var id: Int = 0
|
||||||
|
@ -32,6 +34,10 @@ class AbstractProduct() : Parcelable {
|
||||||
unit = parcel.readInt()
|
unit = parcel.readInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateHash(): String {
|
||||||
|
return "$id:$barcode:$name:${if (netWeight % 1 == 0.0) netWeight.toInt() else netWeight}:$imageHash:$category:$unit".md5()
|
||||||
|
}
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
parcel.writeInt(id)
|
parcel.writeInt(id)
|
||||||
parcel.writeString(barcode)
|
parcel.writeString(barcode)
|
||||||
|
@ -54,5 +60,17 @@ class AbstractProduct() : Parcelable {
|
||||||
override fun newArray(size: Int): Array<AbstractProduct?> {
|
override fun newArray(size: Int): Array<AbstractProduct?> {
|
||||||
return arrayOfNulls(size)
|
return arrayOfNulls(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createFromJSON(json: JSONObject): AbstractProduct {
|
||||||
|
return AbstractProduct(
|
||||||
|
json["local_id"].toString().toInt(),
|
||||||
|
json["barcode"].toString(),
|
||||||
|
json["name"].toString(),
|
||||||
|
json["net_weight"].toString().toDouble(),
|
||||||
|
json["image_filename"].toString(),
|
||||||
|
json["category"].toString().toInt(),
|
||||||
|
json["unit"].toString().toInt()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,8 @@ package org.foxarmy.barcodescannerforemployees.dataclasses
|
||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import org.foxarmy.barcodescannerforemployees.md5
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
class Category() : Parcelable {
|
class Category() : Parcelable {
|
||||||
var id = 0
|
var id = 0
|
||||||
|
@ -27,6 +29,10 @@ class Category() : Parcelable {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateHash(): String {
|
||||||
|
return "$id:$name".md5()
|
||||||
|
}
|
||||||
|
|
||||||
companion object CREATOR : Parcelable.Creator<Category> {
|
companion object CREATOR : Parcelable.Creator<Category> {
|
||||||
override fun createFromParcel(parcel: Parcel): Category {
|
override fun createFromParcel(parcel: Parcel): Category {
|
||||||
return Category(parcel)
|
return Category(parcel)
|
||||||
|
@ -35,5 +41,12 @@ class Category() : Parcelable {
|
||||||
override fun newArray(size: Int): Array<Category?> {
|
override fun newArray(size: Int): Array<Category?> {
|
||||||
return arrayOfNulls(size)
|
return arrayOfNulls(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createFromJSON(json: JSONObject): Category {
|
||||||
|
return Category(
|
||||||
|
json["local_id"].toString().toInt(),
|
||||||
|
json["name"].toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,6 +5,9 @@ import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness
|
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness
|
||||||
|
import org.foxarmy.barcodescannerforemployees.convertToUnixEpochTimestamp
|
||||||
|
import org.foxarmy.barcodescannerforemployees.md5
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
class Product() : Parcelable {
|
class Product() : Parcelable {
|
||||||
var id = 0
|
var id = 0
|
||||||
|
@ -46,6 +49,10 @@ class Product() : Parcelable {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateHash(): String {
|
||||||
|
return "$id:$abstractProductId:$amount:$dateOfProduction:$dateOfExpiry".md5()
|
||||||
|
}
|
||||||
|
|
||||||
companion object CREATOR : Parcelable.Creator<Product> {
|
companion object CREATOR : Parcelable.Creator<Product> {
|
||||||
override fun createFromParcel(parcel: Parcel): Product {
|
override fun createFromParcel(parcel: Parcel): Product {
|
||||||
return Product(parcel)
|
return Product(parcel)
|
||||||
|
@ -54,5 +61,16 @@ class Product() : Parcelable {
|
||||||
override fun newArray(size: Int): Array<Product?> {
|
override fun newArray(size: Int): Array<Product?> {
|
||||||
return arrayOfNulls(size)
|
return arrayOfNulls(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun createFromJSON(json: JSONObject): Product {
|
||||||
|
return Product(
|
||||||
|
json["local_id"].toString().toInt(),
|
||||||
|
json["abstract_product_id"].toString().toInt(),
|
||||||
|
json["amount"].toString().toInt(),
|
||||||
|
convertToUnixEpochTimestamp(json["date_of_production"].toString()),
|
||||||
|
convertToUnixEpochTimestamp(json["expiry_date"].toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package org.foxarmy.barcodescannerforemployees.fragments
|
package org.foxarmy.barcodescannerforemployees.fragments
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
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
|
||||||
|
@ -11,14 +11,39 @@ import android.widget.Toast
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.foxarmy.barcodescannerforemployees.CategoriesContract
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import org.foxarmy.barcodescannerforemployees.activities.AddCategoryActivity
|
import org.foxarmy.barcodescannerforemployees.activities.AddCategoryActivity
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
import org.foxarmy.barcodescannerforemployees.views.CategoryView
|
import org.foxarmy.barcodescannerforemployees.views.CategoryView
|
||||||
|
|
||||||
class CategoriesFragment : Fragment() {
|
class CategoriesFragment : Fragment() {
|
||||||
|
|
||||||
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
private lateinit var categoryDAO: CategoryDAO
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
requireContext(),
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
prepareDatabaseConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepareDatabaseConnection() {
|
||||||
|
val dbHelper = DBStorageController(requireContext(), sharedPreferences.getString("currentGroup", "database")!!)
|
||||||
|
categoryDAO = CategoryDAO(dbHelper)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
|
@ -35,11 +60,21 @@ class CategoriesFragment : Fragment() {
|
||||||
fun removeSelected() {
|
fun removeSelected() {
|
||||||
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
|
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
|
||||||
|
|
||||||
val db = DBStorageController(requireContext())
|
|
||||||
var deleted = false
|
var deleted = false
|
||||||
for (view: CategoryView in layout?.children!!.iterator() as Iterator<CategoryView>) {
|
for (view: CategoryView in layout?.children!!.iterator() as Iterator<CategoryView>) {
|
||||||
if (view.isCategorySelected) {
|
if (view.isCategorySelected) {
|
||||||
db.eraseCategory(db.writableDatabase, view.category.id, requireContext())
|
val net = Net()
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt()
|
||||||
|
|
||||||
|
categoryDAO.eraseCategory(view.category.id, requireContext())
|
||||||
|
val response = net.deleteCategory(currentGroup, view.category.id)
|
||||||
|
activity!!.runOnUiThread{
|
||||||
|
Toast.makeText(context, response.body!!.string(), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
deleted = true
|
deleted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,28 +99,15 @@ class CategoriesFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateContent() {
|
fun updateContent() {
|
||||||
|
prepareDatabaseConnection()
|
||||||
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
|
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
|
||||||
layout?.removeAllViews()
|
layout?.removeAllViews()
|
||||||
|
|
||||||
val db = DBStorageController(requireContext()).readableDatabase
|
val categories = categoryDAO.getAllCategories()
|
||||||
|
|
||||||
val projection = arrayOf(
|
for (category in categories) {
|
||||||
BaseColumns._ID,
|
val categoryView = CategoryView(requireActivity(), requireContext(), category)
|
||||||
CategoriesContract.CategoryEntry.CATEGORY_NAME
|
layout?.addView(categoryView)
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package org.foxarmy.barcodescannerforemployees.fragments
|
package org.foxarmy.barcodescannerforemployees.fragments
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
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
|
||||||
|
@ -15,13 +14,14 @@ 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 androidx.gridlayout.widget.GridLayout
|
||||||
import org.foxarmy.barcodescannerforemployees.AbstractProductContract
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
import androidx.security.crypto.MasterKeys
|
||||||
import org.foxarmy.barcodescannerforemployees.ProductContract
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import org.foxarmy.barcodescannerforemployees.activities.AddProductActivity
|
import org.foxarmy.barcodescannerforemployees.activities.AddProductActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
|
||||||
import org.foxarmy.barcodescannerforemployees.databinding.FragmentShelfBinding
|
import org.foxarmy.barcodescannerforemployees.databinding.FragmentShelfBinding
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
|
||||||
import org.foxarmy.barcodescannerforemployees.views.ProductView
|
import org.foxarmy.barcodescannerforemployees.views.ProductView
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
@ -32,6 +32,24 @@ class ShelfFragment : Fragment() {
|
||||||
private var filterBy = ""
|
private var filterBy = ""
|
||||||
private var filter = ""
|
private var filter = ""
|
||||||
|
|
||||||
|
private lateinit var productDAO: ProductDAO
|
||||||
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
requireContext(),
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
prepareDatabaseConnection()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
|
@ -61,6 +79,11 @@ class ShelfFragment : Fragment() {
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun prepareDatabaseConnection() {
|
||||||
|
val dbHelper = DBStorageController(requireContext(), sharedPreferences.getString("currentGroup", "database")!!)
|
||||||
|
productDAO = ProductDAO(dbHelper)
|
||||||
|
}
|
||||||
|
|
||||||
fun updateSelected() {
|
fun updateSelected() {
|
||||||
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
|
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
|
||||||
|
|
||||||
|
@ -99,6 +122,7 @@ class ShelfFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateContent() {
|
fun updateContent() {
|
||||||
|
prepareDatabaseConnection()
|
||||||
thread {
|
thread {
|
||||||
if (updateInProgress) return@thread
|
if (updateInProgress) return@thread
|
||||||
updateInProgress = true
|
updateInProgress = true
|
||||||
|
@ -107,88 +131,7 @@ class ShelfFragment : Fragment() {
|
||||||
grv?.removeAllViews()
|
grv?.removeAllViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
val db = DBStorageController(requireContext()).readableDatabase
|
val products = productDAO.getSortedListOfProducts(binding.spinner.selectedItemPosition, filterBy, filter)
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection: String? = null
|
|
||||||
var selectionArgs: Array<String>? = null
|
|
||||||
|
|
||||||
when (filterBy) {
|
|
||||||
"expiryDate" -> {
|
|
||||||
selection = "${ProductContract.ProductEntry.EXPIRY_DATE} = ?"
|
|
||||||
selectionArgs = arrayOf(filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
"" -> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy)
|
|
||||||
|
|
||||||
val products = mutableListOf<Product>()
|
|
||||||
|
|
||||||
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()) {
|
for (product in products.iterator()) {
|
||||||
val productView = ProductView(
|
val productView = ProductView(
|
||||||
|
@ -201,6 +144,7 @@ class ShelfFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateInProgress = false
|
updateInProgress = false
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,14 +152,24 @@ class ShelfFragment : Fragment() {
|
||||||
thread {
|
thread {
|
||||||
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
|
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
|
||||||
|
|
||||||
val db = DBStorageController(requireContext())
|
|
||||||
var deleted = false
|
var deleted = false
|
||||||
for (view: ProductView in grv?.children!!.iterator() as Iterator<ProductView>) {
|
for (view: ProductView in grv?.children!!.iterator() as Iterator<ProductView>) {
|
||||||
activity!!.runOnUiThread {
|
activity!!.runOnUiThread {
|
||||||
view.findViewById<ImageView>(R.id.productPicture).setImageURI(null)
|
view.findViewById<ImageView>(R.id.productPicture).setImageURI(null)
|
||||||
}
|
}
|
||||||
if (view.isProductSelected) {
|
if (view.isProductSelected) {
|
||||||
db.eraseProduct(db.writableDatabase, view.product.id)
|
val net = Net()
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt()
|
||||||
|
|
||||||
|
productDAO.eraseProduct(view.product.id)
|
||||||
|
val response = net.deleteProduct(currentGroup, view.product.id)
|
||||||
|
activity!!.runOnUiThread{
|
||||||
|
Toast.makeText(context, response.body!!.string(), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
deleted = true
|
deleted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.foxarmy.barcodescannerforemployees.fragments
|
package org.foxarmy.barcodescannerforemployees.fragments
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
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
|
||||||
|
@ -10,13 +10,15 @@ import android.widget.*
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.foxarmy.barcodescannerforemployees.AbstractProductContract
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.Net
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import org.foxarmy.barcodescannerforemployees.activities.AddAbstractProductActivity
|
import org.foxarmy.barcodescannerforemployees.activities.AddAbstractProductActivity
|
||||||
import org.foxarmy.barcodescannerforemployees.activities.FindBarcodelessAbstractProduct
|
import org.foxarmy.barcodescannerforemployees.activities.FindBarcodelessAbstractProduct
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
import org.foxarmy.barcodescannerforemployees.databinding.FragmentStorageBinding
|
import org.foxarmy.barcodescannerforemployees.databinding.FragmentStorageBinding
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
|
||||||
import org.foxarmy.barcodescannerforemployees.generateThumbnailForImage
|
import org.foxarmy.barcodescannerforemployees.generateThumbnailForImage
|
||||||
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
|
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
@ -26,12 +28,27 @@ class StorageFragment : Fragment() {
|
||||||
private lateinit var binding: FragmentStorageBinding
|
private lateinit var binding: FragmentStorageBinding
|
||||||
private var filterBy = ""
|
private var filterBy = ""
|
||||||
private lateinit var filter: Array<String>
|
private lateinit var filter: Array<String>
|
||||||
|
|
||||||
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
private lateinit var abstractProductDAO: AbstractProductDAO
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentStorageBinding.inflate(inflater)
|
binding = FragmentStorageBinding.inflate(inflater)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
requireContext(),
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
prepareDatabaseConnection()
|
||||||
|
|
||||||
fillUpSortBySpinner()
|
fillUpSortBySpinner()
|
||||||
|
|
||||||
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
@ -55,6 +72,11 @@ class StorageFragment : Fragment() {
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun prepareDatabaseConnection() {
|
||||||
|
val dbHelper = DBStorageController(requireContext(), sharedPreferences.getString("currentGroup", "database")!!)
|
||||||
|
abstractProductDAO = AbstractProductDAO(dbHelper)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
|
@ -72,14 +94,24 @@ class StorageFragment : Fragment() {
|
||||||
thread {
|
thread {
|
||||||
val grv = binding.contentGridLayout
|
val grv = binding.contentGridLayout
|
||||||
|
|
||||||
val db = DBStorageController(requireContext())
|
|
||||||
var deleted = false
|
var deleted = false
|
||||||
for (view: AbstractProductView in grv.children.iterator() as Iterator<AbstractProductView>) {
|
for (view: AbstractProductView in grv.children.iterator() as Iterator<AbstractProductView>) {
|
||||||
activity!!.runOnUiThread {
|
activity!!.runOnUiThread {
|
||||||
view.findViewById<ImageView>(R.id.productPicture).setImageURI(null)
|
view.findViewById<ImageView>(R.id.productPicture).setImageURI(null)
|
||||||
}
|
}
|
||||||
if (view.isProductSelected) {
|
if (view.isProductSelected) {
|
||||||
db.eraseAbstractProduct(db.writableDatabase, view.abstractProduct.id, requireContext())
|
val net = Net()
|
||||||
|
net.token = sharedPreferences.getString("token", "")!!
|
||||||
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
|
val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt()
|
||||||
|
|
||||||
|
abstractProductDAO.eraseAbstractProduct(view.abstractProduct.id, requireContext())
|
||||||
|
val response = net.deleteAbstractProduct(currentGroup, view.abstractProduct.id)
|
||||||
|
activity!!.runOnUiThread{
|
||||||
|
Toast.makeText(context, response.body!!.string(), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
deleted = true
|
deleted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,84 +141,36 @@ class StorageFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateContent() {
|
fun updateContent() {
|
||||||
|
prepareDatabaseConnection()
|
||||||
thread {
|
thread {
|
||||||
val grv = binding.contentGridLayout
|
val grv = binding.contentGridLayout
|
||||||
activity!!.runOnUiThread{
|
activity!!.runOnUiThread{
|
||||||
grv.removeAllViews()
|
grv.removeAllViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
val db = DBStorageController(requireContext()).readableDatabase
|
val abstractProducts = abstractProductDAO.getSortedListOfAbstractProducts(binding.spinner.selectedItemPosition, filterBy, filter)
|
||||||
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 = ""
|
for (abstractProduct in abstractProducts) {
|
||||||
when(binding.spinner.selectedItemPosition) {
|
generateThumbnailForImage(context!!, abstractProduct.imageHash)
|
||||||
0 -> {
|
|
||||||
orderBy = "${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} ASC"
|
|
||||||
}
|
|
||||||
1 -> {
|
|
||||||
orderBy = "${AbstractProductContract.AbstractProductEntry.CATEGORY} ASC"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection = ""
|
val abstractProductView = AbstractProductView(
|
||||||
var selectionArgs: Array<String>? = null
|
requireActivity(),
|
||||||
|
requireContext(),
|
||||||
|
abstractProduct
|
||||||
|
)
|
||||||
|
|
||||||
when (filterBy) {
|
if (filterBy == "barcodeless") {
|
||||||
"category" -> {
|
abstractProductView.setOnClickListener {
|
||||||
selection = "${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?"
|
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
|
||||||
selectionArgs = filter
|
|
||||||
}
|
|
||||||
"barcodeless" -> {
|
|
||||||
selection = "${AbstractProductContract.AbstractProductEntry.BARCODE} = '' "
|
|
||||||
selectionArgs = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy)
|
|
||||||
|
|
||||||
with (cursor) {
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
if (filterBy == "barcodeless") {
|
|
||||||
abstractProduct.setOnClickListener {
|
|
||||||
(activity as FindBarcodelessAbstractProduct).selected(abstractProduct.abstractProduct)
|
|
||||||
}
|
|
||||||
abstractProduct.findViewById<TextView>(R.id.productNameView).setOnClickListener {
|
|
||||||
(activity as FindBarcodelessAbstractProduct).selected(abstractProduct.abstractProduct)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
abstractProductView.findViewById<TextView>(R.id.productNameView).setOnClickListener {
|
||||||
activity!!.runOnUiThread{
|
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
|
||||||
grv.addView(abstractProduct)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activity!!.runOnUiThread{
|
||||||
|
grv.addView(abstractProductView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.join()
|
}.join()
|
||||||
}
|
}
|
||||||
|
@ -202,6 +186,7 @@ class StorageFragment : Fragment() {
|
||||||
filter = arrayOf("$id")
|
filter = arrayOf("$id")
|
||||||
updateContent()
|
updateContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun newInstance(filterBy: String, filter: Array<String>):StorageFragment = StorageFragment().apply {
|
fun newInstance(filterBy: String, filter: Array<String>):StorageFragment = StorageFragment().apply {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.foxarmy.barcodescannerforemployees.views
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -13,9 +14,16 @@ import android.widget.Toast
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.ContextCompat.startActivity
|
import androidx.core.content.ContextCompat.startActivity
|
||||||
import org.foxarmy.barcodescannerforemployees.*
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
|
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
|
import org.foxarmy.barcodescannerforemployees.getActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.getImageUri
|
||||||
|
import org.foxarmy.barcodescannerforemployees.getUnitNameById
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class AbstractProductView: LinearLayout {
|
class AbstractProductView: LinearLayout {
|
||||||
|
@ -27,12 +35,26 @@ class AbstractProductView: LinearLayout {
|
||||||
private var unitField: TextView? = null
|
private var unitField: TextView? = null
|
||||||
var abstractProduct: AbstractProduct = AbstractProduct()
|
var abstractProduct: AbstractProduct = AbstractProduct()
|
||||||
var isProductSelected = false
|
var isProductSelected = false
|
||||||
private lateinit var activity: Activity
|
private var activity: Activity
|
||||||
|
|
||||||
|
private var categoryDAO: CategoryDAO
|
||||||
|
private var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
constructor(context: Context, a: AttributeSet) : super(context, a) {
|
constructor(context: Context, a: AttributeSet) : super(context, a) {
|
||||||
activity = getActivity(context)!!
|
activity = getActivity(context)!!
|
||||||
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)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
context,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
|
||||||
|
categoryDAO = CategoryDAO(dbHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(activity: Activity, context: Context, abstractProduct: AbstractProduct) : super(context) {
|
constructor(activity: Activity, context: Context, abstractProduct: AbstractProduct) : super(context) {
|
||||||
|
@ -41,6 +63,16 @@ class AbstractProductView: LinearLayout {
|
||||||
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)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
context,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
|
||||||
|
categoryDAO = CategoryDAO(dbHelper)
|
||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +121,6 @@ class AbstractProductView: LinearLayout {
|
||||||
|
|
||||||
productNameField!!.text = abstractProduct.name
|
productNameField!!.text = abstractProduct.name
|
||||||
netWeightField!!.text = abstractProduct.netWeight.toString()
|
netWeightField!!.text = abstractProduct.netWeight.toString()
|
||||||
categoryField!!.text = DBStorageController(context).getCategoryNameById(DBStorageController(context).readableDatabase, abstractProduct.category)
|
categoryField!!.text = categoryDAO.getCategoryNameById(abstractProduct.category)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,13 +2,17 @@ package org.foxarmy.barcodescannerforemployees.views
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
|
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
|
||||||
|
|
||||||
class CategoryView : LinearLayout {
|
class CategoryView : LinearLayout {
|
||||||
|
@ -17,9 +21,23 @@ class CategoryView : LinearLayout {
|
||||||
val amountOfProducts: TextView
|
val amountOfProducts: TextView
|
||||||
var isCategorySelected = false
|
var isCategorySelected = false
|
||||||
|
|
||||||
|
private var abstractProductDAO: AbstractProductDAO
|
||||||
|
private var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
constructor(activity: Activity, context: Context, category: Category) : super(context) {
|
constructor(activity: Activity, context: Context, category: Category) : super(context) {
|
||||||
this.category = category
|
this.category = category
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
context,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
|
||||||
|
abstractProductDAO = AbstractProductDAO(dbHelper)
|
||||||
|
|
||||||
val inflater: LayoutInflater = activity.layoutInflater
|
val inflater: LayoutInflater = activity.layoutInflater
|
||||||
inflater.inflate(R.layout.category_view, this)
|
inflater.inflate(R.layout.category_view, this)
|
||||||
|
|
||||||
|
@ -29,7 +47,7 @@ class CategoryView : LinearLayout {
|
||||||
amountOfProducts = findViewById(R.id.amountOfProducts)
|
amountOfProducts = findViewById(R.id.amountOfProducts)
|
||||||
|
|
||||||
categoryName.text = category.name
|
categoryName.text = category.name
|
||||||
amountOfProducts.text = DBStorageController(context).getAmountOfAbstractProductsInCategory(DBStorageController(context).readableDatabase, category.id).toString()
|
amountOfProducts.text = abstractProductDAO.getAmountOfAbstractProductsInCategory(category.id).toString()
|
||||||
|
|
||||||
setOnLongClickListener {
|
setOnLongClickListener {
|
||||||
isCategorySelected = !isCategorySelected
|
isCategorySelected = !isCategorySelected
|
||||||
|
|
|
@ -2,21 +2,39 @@ package org.foxarmy.barcodescannerforemployees.views
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import org.foxarmy.barcodescannerforemployees.DBStorageController
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
import org.foxarmy.barcodescannerforemployees.R
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import org.foxarmy.barcodescannerforemployees.activities.ExpiryCalendarActivity
|
import org.foxarmy.barcodescannerforemployees.activities.ExpiryCalendarActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
class ExpiryGroupView : LinearLayout {
|
class ExpiryGroupView : LinearLayout {
|
||||||
|
|
||||||
private var representingDate: Long? = null
|
private var representingDate: Long? = null
|
||||||
|
|
||||||
|
private var sharedPreferences: SharedPreferences
|
||||||
|
private var productDAO: ProductDAO
|
||||||
|
|
||||||
constructor(activity: Activity, context: Context, representingDate: Long) : super(context) {
|
constructor(activity: Activity, context: Context, representingDate: Long) : super(context) {
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
context,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
|
||||||
|
productDAO = ProductDAO(dbHelper)
|
||||||
|
|
||||||
val inflater: LayoutInflater = activity.layoutInflater
|
val inflater: LayoutInflater = activity.layoutInflater
|
||||||
inflater.inflate(R.layout.expiry_group_view, this)
|
inflater.inflate(R.layout.expiry_group_view, this)
|
||||||
|
|
||||||
|
@ -24,14 +42,13 @@ class ExpiryGroupView : LinearLayout {
|
||||||
|
|
||||||
this.background = ContextCompat.getDrawable(context,R.drawable.outline)
|
this.background = ContextCompat.getDrawable(context,R.drawable.outline)
|
||||||
|
|
||||||
var amount = DBStorageController(context).findAmountOfProductsWithExpiryDate(DBStorageController(context).readableDatabase, representingDate)
|
val amount = productDAO.findAmountOfProductsWithExpiryDate(representingDate)
|
||||||
|
|
||||||
findViewById<TextView>(R.id.representingDateTextView).text = SimpleDateFormat("dd.MM.yyyy").format(representingDate * 1000)
|
findViewById<TextView>(R.id.representingDateTextView).text = SimpleDateFormat("dd.MM.yyyy").format(representingDate * 1000)
|
||||||
findViewById<TextView>(R.id.amountTextView).text = amount.toString()
|
findViewById<TextView>(R.id.amountTextView).text = amount.toString()
|
||||||
|
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
(activity as ExpiryCalendarActivity).displayDate(representingDate)
|
(activity as ExpiryCalendarActivity).displayDate(representingDate)
|
||||||
// (activity as MainActivity).filterAbstractProductsByCategory(category.id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
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 org.foxarmy.barcodescannerforemployees.R
|
||||||
|
|
||||||
|
class GroupMemberView : LinearLayout {
|
||||||
|
|
||||||
|
private var username: String
|
||||||
|
private var userId: Int
|
||||||
|
|
||||||
|
constructor(activity: Activity, context: Context, username: String, userId: Int) : super(context) {
|
||||||
|
|
||||||
|
this.username = username
|
||||||
|
this.userId = userId
|
||||||
|
|
||||||
|
val inflater: LayoutInflater = activity.layoutInflater
|
||||||
|
inflater.inflate(R.layout.group_member_view, this)
|
||||||
|
|
||||||
|
findViewById<TextView>(R.id.userNameTextView).text = username
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package org.foxarmy.barcodescannerforemployees.views
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
|
import org.foxarmy.barcodescannerforemployees.activities.ManageGroupActivity
|
||||||
|
|
||||||
|
class GroupView : LinearLayout {
|
||||||
|
private var name: String
|
||||||
|
private var id: Int
|
||||||
|
|
||||||
|
constructor(activity: Activity, context: Context, name: String, id: Int) : super(context) {
|
||||||
|
|
||||||
|
this.name = name
|
||||||
|
this.id = id
|
||||||
|
|
||||||
|
val inflater: LayoutInflater = activity.layoutInflater
|
||||||
|
inflater.inflate(R.layout.group_view, this)
|
||||||
|
|
||||||
|
findViewById<TextView>(R.id.nameTextView).text = name
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
val intent = Intent(activity, ManageGroupActivity::class.java)
|
||||||
|
val extras = Bundle()
|
||||||
|
extras.putInt("groupId", id)
|
||||||
|
intent.putExtras(extras)
|
||||||
|
ContextCompat.startActivity(context, intent, extras)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package org.foxarmy.barcodescannerforemployees.views
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
@ -20,10 +21,18 @@ import androidx.core.graphics.blue
|
||||||
import androidx.core.graphics.green
|
import androidx.core.graphics.green
|
||||||
import androidx.core.graphics.red
|
import androidx.core.graphics.red
|
||||||
import androidx.core.math.MathUtils.clamp
|
import androidx.core.math.MathUtils.clamp
|
||||||
import org.foxarmy.barcodescannerforemployees.*
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKeys
|
||||||
|
import org.foxarmy.barcodescannerforemployees.R
|
||||||
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
|
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
|
||||||
|
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
|
||||||
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
|
||||||
|
import org.foxarmy.barcodescannerforemployees.getActivity
|
||||||
|
import org.foxarmy.barcodescannerforemployees.getImageUri
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
@ -33,7 +42,7 @@ import kotlin.concurrent.thread
|
||||||
class ProductView: LinearLayout {
|
class ProductView: LinearLayout {
|
||||||
var product: Product = Product()
|
var product: Product = Product()
|
||||||
var isProductSelected = false
|
var isProductSelected = false
|
||||||
private lateinit var activity: Activity
|
private var activity: Activity
|
||||||
|
|
||||||
private lateinit var productImageView: ImageView
|
private lateinit var productImageView: ImageView
|
||||||
private lateinit var productNameTextView: TextView
|
private lateinit var productNameTextView: TextView
|
||||||
|
@ -47,8 +56,25 @@ class ProductView: LinearLayout {
|
||||||
private var strokeColor: Int = 0x000000
|
private var strokeColor: Int = 0x000000
|
||||||
private lateinit var outline: GradientDrawable
|
private lateinit var outline: GradientDrawable
|
||||||
|
|
||||||
|
private var categoryDAO: CategoryDAO
|
||||||
|
private var abstractProductDAO: AbstractProductDAO
|
||||||
|
private var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
constructor(context: Context, a: AttributeSet) : super(context, a) {
|
constructor(context: Context, a: AttributeSet) : super(context, a) {
|
||||||
activity = getActivity(context)!!
|
activity = getActivity(context)!!
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
context,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "offline")!!)
|
||||||
|
abstractProductDAO = AbstractProductDAO(dbHelper)
|
||||||
|
categoryDAO = CategoryDAO(dbHelper)
|
||||||
|
|
||||||
val inflater: LayoutInflater = activity.layoutInflater
|
val inflater: LayoutInflater = activity.layoutInflater
|
||||||
inflater.inflate(R.layout.product_view, this)
|
inflater.inflate(R.layout.product_view, this)
|
||||||
}
|
}
|
||||||
|
@ -60,6 +86,18 @@ class ProductView: LinearLayout {
|
||||||
val inflater: LayoutInflater = activity.layoutInflater
|
val inflater: LayoutInflater = activity.layoutInflater
|
||||||
inflater.inflate(R.layout.product_view, this)
|
inflater.inflate(R.layout.product_view, this)
|
||||||
|
|
||||||
|
sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
|
"sensitive",
|
||||||
|
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||||
|
context,
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
|
)
|
||||||
|
|
||||||
|
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "offline")!!)
|
||||||
|
abstractProductDAO = AbstractProductDAO(dbHelper)
|
||||||
|
categoryDAO = CategoryDAO(dbHelper)
|
||||||
|
|
||||||
productImageView = findViewById(R.id.productPicture)
|
productImageView = findViewById(R.id.productPicture)
|
||||||
productNameTextView = findViewById(R.id.productNameView)
|
productNameTextView = findViewById(R.id.productNameView)
|
||||||
productNetWeightTextView = findViewById(R.id.productNetWeightView)
|
productNetWeightTextView = findViewById(R.id.productNetWeightView)
|
||||||
|
@ -77,7 +115,7 @@ class ProductView: LinearLayout {
|
||||||
productImageView.setOnClickListener {
|
productImageView.setOnClickListener {
|
||||||
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
|
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
|
||||||
val extras = Bundle()
|
val extras = Bundle()
|
||||||
val abstractProduct = DBStorageController(context).findAbstractProductById(DBStorageController(context).readableDatabase, product.abstractProductId)
|
val abstractProduct = abstractProductDAO.findAbstractProductById(product.abstractProductId)
|
||||||
extras.putString("imagehash", abstractProduct!!.imageHash)
|
extras.putString("imagehash", abstractProduct!!.imageHash)
|
||||||
fullscreenIntent.putExtras(extras)
|
fullscreenIntent.putExtras(extras)
|
||||||
startActivity(context, fullscreenIntent, extras)
|
startActivity(context, fullscreenIntent, extras)
|
||||||
|
@ -88,11 +126,7 @@ class ProductView: LinearLayout {
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
fun update () {
|
fun update () {
|
||||||
val linkedAbstractProduct: AbstractProduct? = DBStorageController(activity).findAbstractProductById(DBStorageController(activity).readableDatabase, product.abstractProductId)
|
val linkedAbstractProduct: AbstractProduct = abstractProductDAO.findAbstractProductById(product.abstractProductId) ?: return
|
||||||
|
|
||||||
if(linkedAbstractProduct == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val thumbnailsDir = File(activity.cacheDir, "thumbnails")
|
val thumbnailsDir = File(activity.cacheDir, "thumbnails")
|
||||||
thumbnailsDir.mkdirs()
|
thumbnailsDir.mkdirs()
|
||||||
|
@ -104,7 +138,7 @@ class ProductView: LinearLayout {
|
||||||
productNameTextView.text = linkedAbstractProduct.name
|
productNameTextView.text = linkedAbstractProduct.name
|
||||||
productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString()
|
productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString()
|
||||||
productAmountTextView.text = product.amount.toString()
|
productAmountTextView.text = product.amount.toString()
|
||||||
productCategoryView.text = DBStorageController(activity).getCategoryNameById(DBStorageController(activity).readableDatabase, linkedAbstractProduct.category)
|
productCategoryView.text = categoryDAO.getCategoryNameById(linkedAbstractProduct.category)
|
||||||
productLifeSpan.text = "${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfProduction*1000))}-${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfExpiry*1000))}"
|
productLifeSpan.text = "${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfProduction*1000))}-${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfExpiry*1000))}"
|
||||||
productFreshnessTextView.text =
|
productFreshnessTextView.text =
|
||||||
if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY || product.freshness < 0) {
|
if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY || product.freshness < 0) {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<size android:width="4dp" android:height="4dp" />
|
||||||
|
<solid android:color="#FFFFFF" />
|
||||||
|
</shape>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/newPasswordTextEdit"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.497" android:hint="@string/new_password"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/saveUsernameButton" android:layout_marginTop="64dp"/>
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/newUsernameTextEdit"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:hint="@string/new_name" app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="48dp"/>
|
||||||
|
<Button
|
||||||
|
android:text="@string/saveButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/saveUsernameButton"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/newUsernameTextEdit" android:layout_marginTop="16dp"/>
|
||||||
|
<Button
|
||||||
|
android:text="@string/saveButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/savePasswordButton"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
android:layout_marginTop="16dp" app:layout_constraintTop_toBottomOf="@+id/newPasswordTextEdit"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/groupNameTextEdit"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:hint="@string/group_name" app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_marginTop="210dp"/>
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/groupPasswordTextEdit"
|
||||||
|
android:layout_marginTop="27dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/groupNameTextEdit" android:hint="@string/group_password"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"/>
|
||||||
|
<Button
|
||||||
|
android:text="@string/create_group"
|
||||||
|
android:layout_width="91dp"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/createGroupButton"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/groupPasswordTextEdit"
|
||||||
|
android:layout_marginTop="24dp" app:layout_constraintEnd_toStartOf="@+id/joinGroupButton"
|
||||||
|
android:layout_marginEnd="12dp"/>
|
||||||
|
<Button
|
||||||
|
android:text="@string/join_group"
|
||||||
|
android:layout_width="91dp"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/joinGroupButton"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/groupPasswordTextEdit"
|
||||||
|
android:layout_marginTop="24dp" android:layout_marginStart="12dp"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/createGroupButton"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/register"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/registerButton"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/loginButton" app:layout_constraintHorizontal_bias="0.15"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/offlineButton" android:layout_marginBottom="64dp"/>
|
||||||
|
<Button
|
||||||
|
android:text="@string/login"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/loginButton"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/registerButton" app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginStart="2dp"
|
||||||
|
app:layout_constraintHorizontal_bias="0.1" app:layout_constraintBottom_toTopOf="@+id/offlineButton"
|
||||||
|
android:layout_marginBottom="64dp"/>
|
||||||
|
<Button
|
||||||
|
android:text="@string/offline"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/offlineButton"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_marginBottom="250dp"/>
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:inputType="text"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/usernameTextEdit"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:hint="@string/username" app:layout_constraintBottom_toTopOf="@+id/passwordTextEdit"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/passwordTextEdit"
|
||||||
|
android:hint="@string/password" app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/languageSpinner" android:layout_marginBottom="24dp"/>
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/serverTextEdit"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/usernameTextEdit" android:layout_marginBottom="16dp"
|
||||||
|
android:hint="@string/server" android:text="bsfe.foxarmy.org"/>
|
||||||
|
<Spinner
|
||||||
|
android:layout_width="409dp"
|
||||||
|
android:layout_height="32dp" android:id="@+id/languageSpinner"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/registerButton" android:layout_marginBottom="24dp"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,61 +1,74 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.drawerlayout.widget.DrawerLayout
|
||||||
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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fitsSystemWindows="true"
|
tools:context=".activities.MainActivity"
|
||||||
tools:context=".activities.MainActivity">
|
android:fitsSystemWindows="true" android:id="@+id/drawer_layout">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" android:id="@+id/storageLayout">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
>
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"/>
|
android:layout_height="match_parent" android:id="@+id/storageLayout">
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/tab_tablayout"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:fitsSystemWindows="true"
|
||||||
app:tabIndicatorColor="#FFF"
|
android:id="@+id/appBarLayout">
|
||||||
app:tabIndicatorHeight="3dp"
|
|
||||||
app:tabMode="fixed" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"/>
|
||||||
|
|
||||||
<!-- <include layout="@layout/content_storage"/>-->
|
<com.google.android.material.tabs.TabLayout
|
||||||
<androidx.viewpager.widget.ViewPager
|
android:id="@+id/tab_tablayout"
|
||||||
android:id="@+id/tab_viewpager"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
|
app:tabIndicatorColor="#FFF"
|
||||||
|
app:tabIndicatorHeight="3dp"
|
||||||
|
app:tabMode="fixed"/>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
<androidx.viewpager.widget.ViewPager
|
||||||
|
android:id="@+id/tab_viewpager"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="777dp"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" android:layout_gravity="bottom"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/appBarLayout"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="6dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="6dp"/>
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/new_element_fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginEnd="16dp" android:layout_marginBottom="16dp"/>
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="true" app:srcCompat="@android:drawable/ic_menu_my_calendar"
|
||||||
|
android:id="@+id/expiryCalendarFab" android:layout_gravity="bottom|end"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/new_element_fab" android:layout_marginBottom="16dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="16dp"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<!-- <include layout="@layout/content_storage"/>-->
|
||||||
|
|
||||||
|
<com.google.android.material.navigation.NavigationView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_gravity="start"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:menu="@menu/drawer_menu" android:id="@+id/nav_view"/>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
</androidx.drawerlayout.widget.DrawerLayout>
|
||||||
android:id="@+id/new_element_fab"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:layout_marginEnd="@dimen/fab_margin"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
app:srcCompat="@android:drawable/ic_input_add"/>
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clickable="true" app:srcCompat="@android:drawable/ic_menu_my_calendar"
|
|
||||||
android:id="@+id/expiryCalendarFab" android:layout_gravity="bottom|end"
|
|
||||||
tools:layout_editor_absoluteY="742dp" tools:layout_editor_absoluteX="337dp"
|
|
||||||
android:layout_marginBottom="84dp" android:layout_marginRight="16dp"/>
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/leave_group"
|
||||||
|
android:layout_width="91dp"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/leaveButton"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="12dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="48dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/renameButton" android:layout_marginEnd="12dp"/>
|
||||||
|
<Button
|
||||||
|
android:text="@string/rename_group"
|
||||||
|
android:layout_width="91dp"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/renameButton"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_marginTop="48dp"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/leaveButton" app:layout_constraintEnd_toStartOf="@+id/button3"
|
||||||
|
android:visibility="visible"/>
|
||||||
|
<Button
|
||||||
|
android:text="Button"
|
||||||
|
android:layout_width="91dp"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/button3"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_marginTop="48dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="12dp"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/renameButton" android:visibility="gone"
|
||||||
|
android:layout_marginStart="12dp"/>
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/leaveButton"
|
||||||
|
android:layout_marginTop="48dp" android:id="@+id/membersScrollView">
|
||||||
|
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical" android:id="@+id/groupsContent"/>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/join_or_create_group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/newGroup"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="24dp"/>
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/groupsContent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/newGroup" android:layout_marginTop="16dp">
|
||||||
|
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical" android:id="@+id/groupsLayout"/>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" android:id="@+id/imageCompressionSetting"
|
||||||
|
android:layout_marginTop="16dp">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/image_compress_factor"
|
||||||
|
android:layout_width="187dp"
|
||||||
|
android:layout_height="30dp" android:id="@+id/imageCompressionFactorTextView" android:textSize="20sp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
/>
|
||||||
|
<SeekBar
|
||||||
|
style="@style/Widget.AppCompat.SeekBar.Discrete"
|
||||||
|
android:layout_width="219dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:max="15"
|
||||||
|
android:progress="0"
|
||||||
|
android:id="@+id/imageCompressionFactorSeekBar"
|
||||||
|
android:tickMark="@drawable/seekbar_tick"
|
||||||
|
android:tickMarkTint="@color/seekbar_tick_color"
|
||||||
|
android:layout_marginTop="6dp" android:indeterminateOnly="false" android:splitTrack="true"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/currentGroupSetting"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/imageCompressionSetting">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/current_group"
|
||||||
|
android:layout_width="81dp"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/currentGroupTextView" android:layout_weight="1"
|
||||||
|
android:textSize="20sp" android:textAlignment="center"/>
|
||||||
|
<Spinner
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/currentGroupSpinner" android:layout_weight="1"
|
||||||
|
android:layout_marginTop="-1dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<Button
|
||||||
|
android:text="@string/saveButton"
|
||||||
|
android:layout_width="88dp"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/saveButton"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/currentGroupSetting"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toStartOf="@+id/cancelButton"
|
||||||
|
android:layout_marginTop="32dp"/>
|
||||||
|
<Button
|
||||||
|
android:text="@string/cancel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/cancelButton"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/currentGroupSetting" app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/saveButton" android:layout_marginTop="32dp"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -62,7 +62,7 @@
|
||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:id="@+id/barcodeTextEdit" app:layout_constraintTop_toBottomOf="@+id/imageView"
|
android:id="@+id/barcodeTextEdit" app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:layout_marginTop="8dp" android:hint="Barcode" android:textColorHint="#737373"
|
android:layout_marginTop="8dp" android:hint="@string/barcode" android:textColorHint="#737373"
|
||||||
android:layout_marginStart="5dp"/>
|
android:layout_marginStart="5dp"/>
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:text="@string/no_barcode"
|
android:text="@string/no_barcode"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
|
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#141218">
|
>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?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="User"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/userNameTextView" android:layout_weight="1"
|
||||||
|
android:textSize="30sp"/>
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:text="Group"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/nameTextView" android:textSize="30sp"/>
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_account"
|
||||||
|
android:title="@string/my_account" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_groups"
|
||||||
|
android:title="@string/my_groups" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_settings"
|
||||||
|
android:title="@string/settings"/>
|
||||||
|
<item android:title="@string/logout" android:id="@+id/nav_logout"/>
|
||||||
|
</menu>
|
|
@ -3,7 +3,7 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context="org.foxarmy.barcodescannerforemployees.activities.MainActivity">
|
tools:context="org.foxarmy.barcodescannerforemployees.activities.MainActivity">
|
||||||
<item android:id="@+id/action_settings"
|
<item android:id="@+id/action_settings"
|
||||||
android:title="@string/action_settings"
|
android:title="@string/settings"
|
||||||
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"/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">СканнерШтрихкодовДляРаботников</string>
|
<string name="app_name">СканнерШтрихкодовДляРаботников</string>
|
||||||
<string name="action_settings">Настройки</string>
|
<string name="settings">Настройки</string>
|
||||||
<string name="first_fragment_label">Добавить продукт</string>
|
<string name="first_fragment_label">Добавить продукт</string>
|
||||||
<string name="second_fragment_label">Продукты</string>
|
<string name="second_fragment_label">Продукты</string>
|
||||||
<string name="scan_label">Сканировать</string>
|
<string name="scan_label">Сканировать</string>
|
||||||
|
@ -87,6 +87,39 @@
|
||||||
ошибка сканирования или введите данные вручную
|
ошибка сканирования или введите данные вручную
|
||||||
</string>
|
</string>
|
||||||
<string name="abstract_product_request">Пожалуйста, отсканируйте штрихкод, чтобы добавить продукт</string>
|
<string name="abstract_product_request">Пожалуйста, отсканируйте штрихкод, чтобы добавить продукт</string>
|
||||||
<string name="no_barcode">No barcode present</string>
|
<string name="no_barcode">Нет штрихкода</string>
|
||||||
<string name="no_barcode_button">No barcode</string>
|
<string name="no_barcode_button">Без штрихкода</string>
|
||||||
|
<string name="register">Зарегистрироваться</string>
|
||||||
|
<string name="login">Войти</string>
|
||||||
|
<string name="offline">Оффлайн</string>
|
||||||
|
<string name="username">Имя пользователя</string>
|
||||||
|
<string name="password">Пароль</string>
|
||||||
|
<string name="username_already_exists">Имя пользователя занято</string>
|
||||||
|
<string name="wrong_password">Неправильный пароль</string>
|
||||||
|
<string name="server">Сервер</string>
|
||||||
|
<string name="my_account">Мой аккаунт</string>
|
||||||
|
<string name="my_groups">Мои группы</string>
|
||||||
|
<string name="nav_open">Открыть навигатор</string>
|
||||||
|
<string name="nav_close">Закрыть навигатор</string>
|
||||||
|
<string name="new_name">Новое имя</string>
|
||||||
|
<string name="new_password">Новый пароль</string>
|
||||||
|
<string name="group_name">Имя группы</string>
|
||||||
|
<string name="group_password">Пароль группы</string>
|
||||||
|
<string name="create_group">Создать группу</string>
|
||||||
|
<string name="join_group">Присоединиться к группе</string>
|
||||||
|
<string name="leave">Выйти</string>
|
||||||
|
<string name="leave_group">Покинуть группу</string>
|
||||||
|
<string name="rename_group">Переименовать группу</string>
|
||||||
|
<string name="join_or_create_group">Присоединиться или создать группу</string>
|
||||||
|
<string name="image_compress_factor">Степень сжатия изображения</string>
|
||||||
|
<string name="current_group">Текущая группа</string>
|
||||||
|
<string name="cancel">Отмена</string>
|
||||||
|
<string name="no_internet_connection">Не могу установить соединение с сервером. Возможно, вы отключены от сети?</string>
|
||||||
|
<string name="ok">Ок</string>
|
||||||
|
<string name="logout">Выйти</string>
|
||||||
|
<string name="online_only_feature">Эта возможность доступна только с онлайн аккаунтом</string>
|
||||||
|
<string-array name="languages">
|
||||||
|
<item>en-US</item>
|
||||||
|
<item>ru-RU</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
|
@ -14,4 +14,5 @@
|
||||||
<color name="full_freshness">#5AFF30</color>
|
<color name="full_freshness">#5AFF30</color>
|
||||||
<color name="half_freshness">#FFF200</color>
|
<color name="half_freshness">#FFF200</color>
|
||||||
<color name="expired_freshness">#ff0000</color>
|
<color name="expired_freshness">#ff0000</color>
|
||||||
|
<color name="seekbar_tick_color">#FFFFFF</color>
|
||||||
</resources>
|
</resources>
|
|
@ -1,6 +1,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">BarcodeScannerForEmployees</string>
|
<string name="app_name">BarcodeScannerForEmployees</string>
|
||||||
<string name="action_settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
<string name="first_fragment_label">Add new product</string>
|
<string name="first_fragment_label">Add new product</string>
|
||||||
<string name="second_fragment_label">Products</string>
|
<string name="second_fragment_label">Products</string>
|
||||||
<string name="netWeight">Net weight</string>
|
<string name="netWeight">Net weight</string>
|
||||||
|
@ -87,4 +87,37 @@
|
||||||
<string name="abstract_product_request">Please, scan a barcode in order to add product</string>
|
<string name="abstract_product_request">Please, scan a barcode in order to add product</string>
|
||||||
<string name="no_barcode">No barcode present</string>
|
<string name="no_barcode">No barcode present</string>
|
||||||
<string name="no_barcode_button">No barcode</string>
|
<string name="no_barcode_button">No barcode</string>
|
||||||
|
<string name="register">Register</string>
|
||||||
|
<string name="login">Login</string>
|
||||||
|
<string name="offline">Offline</string>
|
||||||
|
<string name="username">Username</string>
|
||||||
|
<string name="password">Password</string>
|
||||||
|
<string name="username_already_exists">Such account already exists</string>
|
||||||
|
<string name="wrong_password">Wrong password</string>
|
||||||
|
<string name="server">Server</string>
|
||||||
|
<string name="my_account">My account</string>
|
||||||
|
<string name="my_groups">My groups</string>
|
||||||
|
<string name="nav_open">navigation open</string>
|
||||||
|
<string name="nav_close">Navigation close</string>
|
||||||
|
<string name="new_name">New name</string>
|
||||||
|
<string name="new_password">New password</string>
|
||||||
|
<string name="group_name">Group name</string>
|
||||||
|
<string name="group_password">Group password</string>
|
||||||
|
<string name="create_group">Create group</string>
|
||||||
|
<string name="join_group">Join group</string>
|
||||||
|
<string name="leave">Leave</string>
|
||||||
|
<string name="leave_group">Leave group</string>
|
||||||
|
<string name="rename_group">Rename group</string>
|
||||||
|
<string name="join_or_create_group">Join or create a group</string>
|
||||||
|
<string name="image_compress_factor">Image compression factor</string>
|
||||||
|
<string name="current_group">Current group</string>
|
||||||
|
<string name="cancel">Cancel</string>
|
||||||
|
<string name="no_internet_connection">Cannot reach the server. Maybe, you lost internet connection?</string>
|
||||||
|
<string name="ok">OK</string>
|
||||||
|
<string name="logout">Log out</string>
|
||||||
|
<string name="online_only_feature">This feature is online only</string>
|
||||||
|
<string-array name="languages">
|
||||||
|
<item>en-US</item>
|
||||||
|
<item>ru-RU</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
|
@ -17,15 +17,19 @@ gridlayout = "1.0.0"
|
||||||
activity = "1.9.2"
|
activity = "1.9.2"
|
||||||
legacySupportV4 = "1.0.0"
|
legacySupportV4 = "1.0.0"
|
||||||
fragment = "1.8.4"
|
fragment = "1.8.4"
|
||||||
|
okhttp = "4.10.0"
|
||||||
playServicesCodeScanner = "16.1.0"
|
playServicesCodeScanner = "16.1.0"
|
||||||
|
securityCrypto = "1.0.0"
|
||||||
volley = "1.2.1"
|
volley = "1.2.1"
|
||||||
zxingAndroidEmbedded = "4.3.0"
|
zxingAndroidEmbedded = "4.3.0"
|
||||||
|
material3Android = "1.3.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraView" }
|
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraView" }
|
||||||
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", 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" }
|
||||||
|
androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCrypto" }
|
||||||
barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "barcodeScanning" }
|
barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "barcodeScanning" }
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
|
@ -40,9 +44,11 @@ androidx-gridlayout = { group = "androidx.gridlayout", name = "gridlayout", vers
|
||||||
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
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-legacy-support-v4 = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacySupportV4" }
|
||||||
androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
|
androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
|
||||||
|
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||||
play-services-code-scanner = { module = "com.google.android.gms:play-services-code-scanner", version.ref = "playServicesCodeScanner" }
|
play-services-code-scanner = { module = "com.google.android.gms:play-services-code-scanner", version.ref = "playServicesCodeScanner" }
|
||||||
volley = { module = "com.android.volley:volley", version.ref = "volley" }
|
volley = { module = "com.android.volley:volley", version.ref = "volley" }
|
||||||
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
|
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
|
||||||
|
androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|
Loading…
Reference in New Issue