Compare commits

..

No commits in common. "master" and "alpha-0.0.2" have entirely different histories.

74 changed files with 900 additions and 4200 deletions

View File

@ -50,16 +50,13 @@ dependencies {
implementation(libs.androidx.activity)
implementation(libs.androidx.legacy.support.v4)
implementation(libs.androidx.fragment)
implementation(libs.androidx.material3.android)
testImplementation(libs.junit)
implementation(libs.volley)
androidTestImplementation(libs.androidx.junit)
implementation(libs.zxing.android.embedded)
implementation("com.google.zxing:core:3.4.1")
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
implementation (libs.barcode.scanning)

View File

@ -13,7 +13,6 @@
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="true"
@ -37,46 +36,6 @@
android:name=".activities.AddAbstractProductActivity"
android:exported="false"
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
<activity
android:name=".activities.ExpiryCalendarActivity"
android:exported="false"
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
<activity
android:name=".activities.ExpiryCalendarGroupActivity"
android:exported="false"
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
<activity
android:name=".activities.FindBarcodelessAbstractProduct"
android:exported="false"
android:theme="@style/Theme.BarcodeScannerForEmployees"/>
<activity
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
android:name=".activities.FullscreenActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
@ -90,7 +49,7 @@
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.google.firebase.components.activities.MainActivity.provider;com.google.firebase.components.activities.FullscreenActivity.provider;com.google.firebase.components.activities.AddAbstractProductActivity.provider;com.google.firebase.components.activities.AddProductActivity.provider;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:authorities="com.google.firebase.components.activities.MainActivity.provider;com.google.firebase.components.activities.FullscreenActivity.provider;com.google.firebase.components.activities.AddAbstractProductActivity.provider;com.google.firebase.components.activities.AddProductActivity.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
@ -99,7 +58,7 @@
</provider>
<activity
android:name=".activities.NavigatorActivity"
android:name=".activities.MainActivity"
android:exported="true"
android:theme="@style/Theme.BarcodeScannerForEmployees">
<intent-filter>

View File

@ -0,0 +1,283 @@
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 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"
}
}

View File

@ -1,656 +0,0 @@
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
}
}

View File

@ -13,6 +13,7 @@ class Parser constructor() {
val found = foundByRegex.groupValues[0]
text = text.replace(found, "")
netWeight = stripNetWeight(found)
// }
return Triple(text, netWeight, found)
}
}
@ -39,11 +40,9 @@ class Parser constructor() {
"мл" -> { 3 }
"шт" -> { 4 }
else -> {
4
}
else -> { -1 }
}
return AbstractProduct(0, "", name, netWeight, "", 0, unitNumber)
}
}
}

View File

@ -0,0 +1,40 @@
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)
}
}

View File

@ -5,39 +5,20 @@ import android.content.Context
import android.content.ContextWrapper
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.media.ExifInterface
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.core.content.FileProvider
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import androidx.core.graphics.scale
import com.google.firebase.components.BuildConfig
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.net.URLEncoder
import java.security.MessageDigest
import java.text.SimpleDateFormat
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? {
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)
@ -52,19 +33,8 @@ fun generateThumbnailForImage(context: Context, imageHash: String) {
val imageFile = File(picturesDir, "$imageHash.png")
val imageContent = imageFile.inputStream().readBytes()
var img = BitmapFactory.decodeByteArray(imageContent, 0, imageContent.size)
val exif = ExifInterface(imageFile.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 scaled = Bitmap.createScaledBitmap(rotated, rotated.width / 4, rotated.height / 4, true)
scaled.compress(Bitmap.CompressFormat.WEBP_LOSSY, 25, FileOutputStream(thumbnailFile))
img = img.scale(img.width/4,img.height/4)
img.compress(Bitmap.CompressFormat.WEBP_LOSSY, 25, FileOutputStream(thumbnailFile))
}
@OptIn(ExperimentalStdlibApi::class)
@ -74,11 +44,8 @@ fun String.md5(): String {
return digest.toHexString()
}
fun stripNetWeight(netWeight: String): Double {
return removeSubstringsFromString(
netWeight,
arrayOf("Л", "л", "мл", "Мл", "г", "Г", "кг", "Кг", "шт", "Шт", ",", " ", ".")
).toDouble()
fun stripNetWeight (netWeight: String): Double {
return removeSubstringsFromString(netWeight, arrayOf("Л", "л", "мл", "Мл", "г", "Г", "кг", "Кг", "шт", "Шт", ",", " ", ".")).toDouble()
}
fun removeSubstringsFromString(text: String, toRemove: Array<String>): String {
@ -89,6 +56,8 @@ fun removeSubstringsFromString(text: String, toRemove: Array<String>): String {
return result
}
fun String.utf8(): String = URLEncoder.encode(this, "UTF-8")
fun getActivity(context: Context?): Activity? {
if (context == null) {
return null
@ -112,104 +81,13 @@ fun calculateProductFreshness(dateOfProduction: Long, dateOfExpiry: Long): Doubl
return lifeSpanLeft / productLifeSpan.toDouble()
}
fun getUnitNameById(context: Context, id: Int): String {
return when (id) {
0 -> {
context.getString(R.string.kilogram)
}
1 -> {
context.getString(R.string.gram)
}
2 -> {
context.getString(R.string.liter)
}
3 -> {
context.getString(R.string.milliliter)
}
4 -> {
context.getString(R.string.pieces)
}
else -> {
""
}
fun getUnitNameById (context: Context, id: Int): String {
return when(id) {
0 -> { context.getString(R.string.kilogram) }
1 -> { context.getString(R.string.gram) }
2 -> { context.getString(R.string.liter) }
3 -> { context.getString(R.string.milliliter) }
4 -> { context.getString(R.string.pieces) }
else -> { "" }
}
}
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()
}
}

View File

@ -1,168 +0,0 @@
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")
}
}

View File

@ -1,48 +0,0 @@
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()
}
}
}

View File

@ -1,14 +1,14 @@
package org.foxarmy.barcodescannerforemployees.activities
//import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
//import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import android.content.ContentValues
import android.content.DialogInterface
import android.content.Intent
import android.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.Bundle
import android.provider.BaseColumns
import android.util.Log
import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts
@ -17,23 +17,15 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions
import okhttp3.Response
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 java.io.File
import java.io.FileOutputStream
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import kotlin.concurrent.thread
import kotlin.math.abs
class AddAbstractProductActivity : AppCompatActivity() {
private lateinit var imageView: ImageView
@ -43,7 +35,6 @@ class AddAbstractProductActivity : AppCompatActivity() {
private lateinit var scanButton: Button
private lateinit var barcodeText: EditText
private lateinit var noBarcodeCheckBox: CheckBox
private lateinit var productNameText: TextView
private lateinit var netWeightText: TextView
private lateinit var unitTypeSpinner: Spinner
@ -54,28 +45,19 @@ class AddAbstractProductActivity : AppCompatActivity() {
private lateinit var pictureFile: File
private lateinit var picturesPath: File
private var barcode: String = ""
private var action: String = "new"
private var scanningBarcode = false
private lateinit var DAO: AbstractProductDAO
private lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
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)
val extras = intent.extras
abstractProduct = extras!!.get("abstractProduct") as AbstractProduct?
if (abstractProduct != null) {
barcode = abstractProduct!!.barcode
}
picturesPath = File(filesDir, "pictures")
val thumbnailsDir = File(cacheDir, "thumbnails")
@ -88,7 +70,6 @@ class AddAbstractProductActivity : AppCompatActivity() {
scanButton = findViewById(R.id.scan_button)
barcodeText = findViewById(R.id.barcodeTextEdit)
noBarcodeCheckBox = findViewById(R.id.noBarcodeCheckBox)
productNameText = findViewById(R.id.productName)
netWeightText = findViewById(R.id.netWeight)
unitTypeSpinner = findViewById(R.id.unitTypeSpinner)
@ -98,51 +79,35 @@ class AddAbstractProductActivity : AppCompatActivity() {
fillupCategorySpinner()
fillupUnitsSpinner()
noBarcodeCheckBox.setOnClickListener {
barcodeText.isEnabled = !noBarcodeCheckBox.isChecked
}
barcodeText.addTextChangedListener {
this.barcode = barcodeText.text.toString()
}
val extras = intent.extras
action = extras!!.get("action") as String
when (action) {
"update" -> {
abstractProduct = extras.get("abstractProduct") as AbstractProduct
}
"new_from_barcode" -> {
abstractProduct = extras.get("abstractProduct") as AbstractProduct
barcode = abstractProduct!!.barcode
performRequest(abstractProduct!!.barcode)
}
if (abstractProduct?.name == "" && abstractProduct?.barcode != "") {
performRequest(abstractProduct?.barcode!!)
}
if (abstractProduct != null && action == "update") {
if (abstractProduct != null) {
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.rotation = 90f
barcodeText.setText(abstractProduct!!.barcode)
productNameText.text = abstractProduct!!.name
netWeightText.text = abstractProduct!!.netWeight.toString()
categorySpinner.setSelection(abstractProduct!!.category - 1)
categorySpinner.setSelection(abstractProduct!!.category)
unitTypeSpinner.setSelection(abstractProduct!!.unit)
if (abstractProduct!!.barcode == "" || abstractProduct!!.barcode == " ") {
noBarcodeCheckBox.isChecked = true
}
}
saveButton.setOnClickListener {
val productName = productNameText.text.toString()
val netWeight = netWeightText.text
if (action != "update" && (!this::pictureFile.isInitialized || !pictureFile.exists())) {
if (abstractProduct == null && (!this::pictureFile.isInitialized || !pictureFile.exists())) {
Toast.makeText(this, getString(R.string.product_picture_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (barcode == "" && !noBarcodeCheckBox.isChecked) {
if (barcode == "") {
Toast.makeText(this, getString(R.string.product_barcode_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
@ -153,47 +118,29 @@ class AddAbstractProductActivity : AppCompatActivity() {
}
if (netWeight.toString() == "" || netWeight.toString().toDoubleOrNull() == null) {
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")!!
val db = DBStorageController(this).writableDatabase
val values = ContentValues().apply {
put(AbstractProductContract.AbstractProductEntry.BARCODE, barcode)
put(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME, productName)
put(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT, netWeight.toString())
put(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME, pictureFile.nameWithoutExtension)
put(AbstractProductContract.AbstractProductEntry.CATEGORY, categorySpinner.selectedItemPosition)
put(AbstractProductContract.AbstractProductEntry.UNIT, unitTypeSpinner.selectedItemPosition)
}
if (abstractProduct == null) {
db.insert(AbstractProductContract.AbstractProductEntry.TABLE_NAME, null, values)
} else {
currentGroup = 0
db.update(
AbstractProductContract.AbstractProductEntry.TABLE_NAME,
values,
"${BaseColumns._ID} = ?",
arrayOf(abstractProduct!!.id.toString())
)
}
abstractProduct = AbstractProduct(
if (abstractProduct == null) 0 else abstractProduct!!.id,
if (noBarcodeCheckBox.isChecked) "" else barcode,
productName,
netWeight.toString().toDouble(),
pictureFile.nameWithoutExtension,
categorySpinner.selectedItemPosition + 1,
unitTypeSpinner.selectedItemPosition
)
val pictureFile = File(File(filesDir, "pictures"), "${abstractProduct!!.imageHash}.png")
if (action == "update") {
DAO.updateAbstractProduct(abstractProduct!!)
if (currentGroup > 0) response = net.updateAbstractProduct(currentGroup, abstractProduct!!, pictureFile)
} else if (action == "new" || action == "new_from_barcode") {
abstractProduct!!.id = DAO.addAbstractProduct(abstractProduct!!).toInt()
if (currentGroup > 0) response =
net.uploadAbstractProduct(currentGroup, abstractProduct!!, pictureFile)
}
if (currentGroup > 0) Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
finish()
}
@ -209,22 +156,27 @@ class AddAbstractProductActivity : AppCompatActivity() {
fun performRequest(barcode: String) {
barcodeText.setText(this.barcode)
val net = Net();
val result = net.requestProductFromOnlineDB(barcode)
val requester = Requester("https://ean-online.ru", "match.php")
requester.request(this, barcode)
var abstractProduct: AbstractProduct
if (DAO.findAbstractProductByBarcode(this.barcode) != null) {
if (DBStorageController(this).findAbstractProductByBarcode(
DBStorageController(this).readableDatabase,
this.barcode
) != null
) {
AlertDialog.Builder(this)
.setMessage(getString(R.string.abstract_product_already_exists))
.setPositiveButton(getString(R.string.quit)) { _: DialogInterface, _: Int ->
.setMessage("You've got an abstract product with such barcode in your database")
.setPositiveButton("Quit") { _: DialogInterface, _: Int ->
finish()
}
.setNegativeButton(getString(R.string.edit_existing)) { _: DialogInterface, _: Int ->
.setNegativeButton("Edit existing") { _: DialogInterface, _: Int ->
val addProductIntent = Intent(this, AddAbstractProductActivity::class.java)
val extras = Bundle()
val existingAbstractProduct = DAO.findAbstractProductByBarcode(this.barcode)
val existingAbstractProduct = DBStorageController(this).findAbstractProductByBarcode(
DBStorageController(this).readableDatabase,
this.barcode
)
extras.putParcelable("abstractProduct", existingAbstractProduct)
addProductIntent.putExtras(extras)
ContextCompat.startActivity(this, addProductIntent, extras)
@ -233,17 +185,19 @@ class AddAbstractProductActivity : AppCompatActivity() {
}
thread {
if (result == "Not found 404") {
// Я сам в ахуях какой это костыль, пока хз как фиксить, потом придумаю :))
while (requester.response == "") {
}
if (requester.response == "Not found 404") {
runOnUiThread {
Toast.makeText(this, getString(R.string.no_product_in_online_database), Toast.LENGTH_LONG)
Toast.makeText(this, "Product not found. Please, try again or type manually", Toast.LENGTH_LONG)
.show()
productNameText.setText("")
netWeightText.setText("")
}
return@thread
}
abstractProduct = Parser().parse(result)
abstractProduct = Parser().parse(requester.response)
requester.response = ""
runOnUiThread {
productNameText.text = abstractProduct.name
netWeightText.text = abstractProduct.netWeight.toString()
@ -261,21 +215,40 @@ class AddAbstractProductActivity : AppCompatActivity() {
getString(R.string.pieces)
)
val arrayAdapter =
ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, units)
ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, units)
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
unitTypeSpinner.adapter = arrayAdapter
}
fun fillupCategorySpinner() {
val db = DBStorageController(this).readableDatabase
val categoriesDAO =
CategoryDAO(DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!))
val categories = mutableListOf("")
val categories = categoriesDAO.getAllCategories().map { category -> category.name }
val projection = arrayOf(
CategoriesContract.CategoryEntry.CATEGORY_NAME
)
val cursor = db.query(
CategoriesContract.CategoryEntry.TABLE_NAME,
projection,
null,
null,
null,
null,
BaseColumns._ID + " ASC"
)
with(cursor) {
while (moveToNext()) {
categories.add(getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME)))
}
}
val arrayAdapter =
ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, categories)
ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, categories)
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
categorySpinner.adapter = arrayAdapter
}
@ -285,34 +258,12 @@ class AddAbstractProductActivity : AppCompatActivity() {
if (success) {
//Move picture to a proper directory according to its calculated hash
val tempfile = File(filesDir, "image.png")
val imageContent = tempfile.inputStream().readBytes()
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)
val imageHash = imageContent.toString(Charsets.UTF_8).md5()
pictureFile = File(picturesPath, "$imageHash.png")
Files.move(tempfile.toPath(), pictureFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
tempfile.delete()
generateThumbnailForImage(this, imageHash)
imageView.setImageURI(getImageUri(this, pictureFile))
@ -343,8 +294,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
getPicture()
}
} 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()
}
}
@ -353,11 +303,11 @@ class AddAbstractProductActivity : AppCompatActivity() {
if (result.contents == null) {
Toast.makeText(this, getString(R.string.cancelled), Toast.LENGTH_SHORT).show()
} else {
scanningBarcode = false
val scannedBarcode = result.contents
barcodeText.setText(scannedBarcode)
performRequest(scannedBarcode)
}
scanningBarcode = false
}
}

View File

@ -1,40 +1,22 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.app.Activity
import android.content.SharedPreferences
import android.content.ContentValues
import android.os.Bundle
import android.provider.BaseColumns
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.CategoriesContract
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
class AddCategoryActivity : Activity() {
private lateinit var DAO: CategoryDAO
private lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
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 category = extras!!.get("category") as Category?
@ -42,28 +24,20 @@ class AddCategoryActivity : Activity() {
categoryNameTextEdit.setText(category!!.name)
val net = Net()
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() == "") {
Toast.makeText(this, getString(R.string.category_name_required), Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
val db = DBStorageController(this).writableDatabase
if (category.id == 0) { // Inserting new category
val newCategory = Category(0, categoryNameTextEdit.text.toString())
newCategory.id = DAO.addCategory(newCategory).toInt()
if (currentGroup > 0) net.uploadCategory(currentGroup, newCategory)
val values = ContentValues().apply {
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, categoryNameTextEdit.text.toString())
}
db.insert(CategoriesContract.CategoryEntry.TABLE_NAME, null, values)
} else { // Updating existing category
category.name = categoryNameTextEdit.text.toString()
DAO.updateCategory(category)
if (currentGroup > 0) net.updateCategory(currentGroup, category)
val values = ContentValues().apply {
put(CategoriesContract.CategoryEntry.CATEGORY_NAME, categoryNameTextEdit.text.toString())
}
db.update(CategoriesContract.CategoryEntry.TABLE_NAME, values, "${BaseColumns._ID} = ?", arrayOf(category.id.toString()))
}
finish()

View File

@ -1,29 +1,19 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import android.app.DatePickerDialog
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions
import okhttp3.Response
import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.DBStorageController
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.Product
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
@ -33,18 +23,19 @@ import java.util.*
class AddProductActivity : AppCompatActivity() {
private lateinit var scanButton: Button
private lateinit var noBarcodeButton: Button
private lateinit var abstractProductView: AbstractProductView
private lateinit var expiryDateRadioButton: RadioButton
private lateinit var shelfLifeRadioButton: RadioButton
private lateinit var expiryDatePicker: DatePicker
private lateinit var expiryDateTextView: TextView
private lateinit var expiryDateSelectButton: Button
private lateinit var shelfLifeTextView: TextView
private lateinit var shelfLifeTextEdit: EditText
private lateinit var amountTextEdit: EditText
private lateinit var dateOfProductionDatePicker: DatePicker
private lateinit var dateOfProductionSelectButton: Button
private lateinit var saveProductButton: Button
private var expiryDateOverShelfLife: Boolean? = null
@ -52,49 +43,32 @@ class AddProductActivity : AppCompatActivity() {
private var product: Product? = 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?) {
super.onCreate(savedInstanceState)
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)
noBarcodeButton = findViewById(R.id.noBarcodeButton)
abstractProductView = findViewById(R.id.abstractProductView)
expiryDateRadioButton = findViewById(R.id.expiryDateRadio)
shelfLifeRadioButton = findViewById(R.id.shelfLifeRadio)
expiryDatePicker = findViewById(R.id.expiryDatePicker)
expiryDateTextView = findViewById(R.id.expiryDateTextView)
expiryDateSelectButton = findViewById(R.id.selectExpiryDateButton)
shelfLifeTextView = findViewById(R.id.shelfLife)
shelfLifeTextEdit = findViewById(R.id.shelfLifeTextEdit)
amountTextEdit = findViewById(R.id.amountTextEdit)
amountTextEdit.setText("1")
dateOfProductionDatePicker = findViewById(R.id.dateOfProductionDatePicker)
dateOfProductionSelectButton = findViewById(R.id.selectDateOfProductionButton)
saveProductButton = findViewById(R.id.saveProductButton)
val extras = intent.extras
product = extras!!.get("product") as Product?
if (product != null) {
abstractProduct = abstractProductDAO.findAbstractProductById(product!!.abstractProductId)
abstractProduct = DBStorageController(this).findAbstractProductById(DBStorageController(this).readableDatabase, product!!.abstractProductId)
abstractProductView.abstractProduct = abstractProduct!!
expiryDateRadioButton.isSelected = true
shelfLifeRadioButton.isSelected = false
@ -104,16 +78,13 @@ class AddProductActivity : AppCompatActivity() {
update()
} else {
product = Product(0, 0, 0, 0, 0)
abstractProduct = AbstractProduct(0, "", "", 0.0, "", 0, 0)
}
scanButton.setOnClickListener {
requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
}
noBarcodeButton.setOnClickListener {
intentLauncher.launch(Intent(this, FindBarcodelessAbstractProduct::class.java))
}
expiryDateRadioButton.setOnClickListener {
expiryDateOverShelfLife = true
update()
@ -124,14 +95,32 @@ class AddProductActivity : AppCompatActivity() {
update()
}
dateOfProductionDatePicker.setOnDateChangedListener { _, year, monthOfYear, dayOfMonth ->
product!!.dateOfProduction = SimpleDateFormat("dd.MM.yyyy").parse("$dayOfMonth.$monthOfYear.$year")!!.time / 1000
update()
dateOfProductionSelectButton.setOnClickListener {
val c = Calendar.getInstance()
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
val dpd = DatePickerDialog(this, { _, y, m, d ->
product!!.dateOfProduction = SimpleDateFormat("dd.MM.yyyy").parse("$d.${m+1}.$y")!!.time / 1000
update()
}, year, month, day)
dpd.show()
}
expiryDatePicker.setOnDateChangedListener { _, year, monthOfYear, dayOfMonth ->
product!!.dateOfExpiry = SimpleDateFormat("dd.MM.yyyy").parse("$dayOfMonth.$monthOfYear.$year")!!.time / 1000
update()
expiryDateSelectButton.setOnClickListener {
val c = Calendar.getInstance()
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
val dpd = DatePickerDialog(this, { _, y, m, d ->
product!!.dateOfExpiry = SimpleDateFormat("dd.MM.yyyy").parse("$d.${m+1}.$y")!!.time / 1000
update()
}, year, month, day)
dpd.show()
}
amountTextEdit.addTextChangedListener {
@ -140,18 +129,7 @@ class AddProductActivity : AppCompatActivity() {
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 {
if (abstractProduct == null) {
Toast.makeText(this, getString(R.string.abstract_product_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (expiryDateOverShelfLife == null) {
Toast.makeText(this, getString(R.string.shell_life_or_expiry_date_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener
@ -177,56 +155,35 @@ class AddProductActivity : AppCompatActivity() {
return@setOnClickListener
}
val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt()
var response: Response? = null
if (updatingExistentProduct) {
productDAO.updateProduct(product!!)
if (currentGroup > 0) response = net.updateProduct(currentGroup, product!!)
DBStorageController(this).updateProduct(DBStorageController(this).writableDatabase, product!!)
} else {
product!!.id = productDAO.insertNewProduct(product!!).toInt()
if (currentGroup > 0) response = net.uploadProduct(currentGroup, product!!)
DBStorageController(this).insertNewProduct(DBStorageController(this).writableDatabase, product!!)
}
if (response != null) {
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
}
finish()
}
update()
val today = SimpleDateFormat("dd.MM.yyyy").format(Calendar.getInstance().time).split(".")
dateOfProductionDatePicker.updateDate(today[2].toInt(), today[1].toInt() - 1, 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 =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val selectedAbstractProduct = (result.data?.extras!!.getParcelable("abstractProduct") as AbstractProduct?)!!
product!!.abstractProductId = selectedAbstractProduct.id
abstractProduct = selectedAbstractProduct
displayAbstractProduct(selectedAbstractProduct, "")
}
}
private fun update () {
if (expiryDateOverShelfLife == true) {
expiryDatePicker.visibility = View.VISIBLE
expiryDateTextView.visibility = View.VISIBLE
expiryDateSelectButton.visibility = View.VISIBLE
shelfLifeTextEdit.visibility = View.INVISIBLE
shelfLifeTextView.visibility = View.INVISIBLE
} else if (expiryDateOverShelfLife == false){
expiryDatePicker.visibility = View.INVISIBLE
expiryDateTextView.visibility = View.INVISIBLE
expiryDateSelectButton.visibility = View.INVISIBLE
shelfLifeTextEdit.visibility = View.VISIBLE
shelfLifeTextView.visibility = View.VISIBLE
}
val dateOfProductionParsed = SimpleDateFormat("dd.MM.yyyy").format(Date(product!!.dateOfProduction * 1000))
findViewById<TextView>(R.id.dateOfProductionTextView).text = "${getString(R.string.date_of_production)}: $dateOfProductionParsed"
val expiryDateParsed = SimpleDateFormat("dd.MM.yyyy").format(Date(product!!.dateOfExpiry * 1000))
expiryDateTextView.text = "${getString(R.string.expiry_date)}: $expiryDateParsed"
if (amountTextEdit.text.toString() == "") {
amountTextEdit.setText(product!!.amount.toString())
@ -249,25 +206,9 @@ class AddProductActivity : AppCompatActivity() {
product!!.dateOfExpiry = c.timeInMillis / 1000
}
private fun displayAbstractProduct(abstractProduct: AbstractProduct?, scannedBarcode: String) {
if (abstractProduct == null) {
AlertDialog.Builder(this)
.setMessage(getString(R.string.abstract_product_does_not_exist))
.setPositiveButton(R.string.yes) { _, _ ->
val addAbstractProductIntent = Intent(this, AddAbstractProductActivity::class.java)
val extras = Bundle()
extras.putParcelable("abstractProduct", AbstractProduct(0, scannedBarcode, "", 0.0, "", 0, 0))
extras.putString("action", "new_from_barcode")
addAbstractProductIntent.putExtras(extras)
ContextCompat.startActivity(this, addAbstractProductIntent, extras)
}
.setNegativeButton(R.string.no) {_, _ ->
}.show()
} else {
abstractProductView.abstractProduct = abstractProduct
abstractProductView.update()
}
private fun displayAbstractProduct(abstractProduct: AbstractProduct) {
abstractProductView.abstractProduct = abstractProduct
abstractProductView.update()
}
@RequiresApi(Build.VERSION_CODES.R)
@ -288,12 +229,9 @@ class AddProductActivity : AppCompatActivity() {
Toast.makeText(this, getString(R.string.cancelled), Toast.LENGTH_SHORT).show()
} else {
val scannedBarcode = result.contents
abstractProduct = abstractProductDAO.findAbstractProductByBarcode(scannedBarcode)
displayAbstractProduct(abstractProduct, scannedBarcode)
if (abstractProduct != null) {
product?.abstractProductId = abstractProduct!!.id
}
abstractProduct = DBStorageController(this).findAbstractProductByBarcode(DBStorageController(this).readableDatabase, scannedBarcode)
displayAbstractProduct(abstractProduct!!)
product?.abstractProductId = abstractProduct!!.id
}
}
}

View File

@ -1,61 +0,0 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
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.views.ExpiryGroupView
class ExpiryCalendarActivity : AppCompatActivity() {
private lateinit var binding: ActivityExpiryCalendarBinding
private lateinit var productDAO: ProductDAO
private lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
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()
}
fun displayDate(date:Long) {
val expiryCalendarGroupActivityIntent = Intent(this, ExpiryCalendarGroupActivity::class.java)
val extras = Bundle()
extras.putLong("date", date)
expiryCalendarGroupActivityIntent.putExtras(extras)
ContextCompat.startActivity(this, expiryCalendarGroupActivityIntent, extras)
}
private fun fillUp() {
val dates = productDAO.findAllExpiryDates()
val container = findViewById<LinearLayout>(R.id.datesLinearLayout)
dates.forEach { date ->
val newDate = ExpiryGroupView(this, this as Context, date)
container.addView(newDate)
}
}
}

View File

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

View File

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

View File

@ -1,96 +0,0 @@
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()
}
}
}
}
}

View File

@ -1,125 +0,0 @@
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
}
}

View File

@ -1,50 +1,26 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.app.Activity
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.view.Menu
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.AppCompatActivity
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 com.google.android.material.navigation.NavigationView
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.R
import org.foxarmy.barcodescannerforemployees.ViewPagerAdapter
import org.foxarmy.barcodescannerforemployees.databinding.ActivityMainBinding
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import org.foxarmy.barcodescannerforemployees.fragments.CategoriesFragment
import org.foxarmy.barcodescannerforemployees.fragments.ShelfFragment
import org.foxarmy.barcodescannerforemployees.fragments.StorageFragment
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
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
lateinit var adapter: ViewPagerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -52,42 +28,10 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
binding = ActivityMainBinding.inflate(layoutInflater)
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)
setupViewPager(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 { _ ->
val expiryCalendarIntent = Intent(this, ExpiryCalendarActivity::class.java)
val extras = Bundle()
expiryCalendarIntent.putExtras(extras)
ContextCompat.startActivity(this, expiryCalendarIntent, extras)
}
binding.newElementFab.setOnClickListener { view ->
val currentPosition = binding.tabTablayout.selectedTabPosition
val fragment = adapter.getItem(currentPosition)
@ -96,8 +40,9 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
"StorageFragment" -> {
val addAbstractProductIntent = Intent(this, AddAbstractProductActivity::class.java)
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.putString("action", "new")
addAbstractProductIntent.putExtras(extras)
ContextCompat.startActivity(this, addAbstractProductIntent, extras)
}
@ -105,7 +50,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
"CategoriesFragment" -> {
val addCategoryIntent = Intent(this, AddCategoryActivity::class.java)
val extras = Bundle()
extras.putParcelable("category", Category(0, ""))
extras.putParcelable("category", Category(0, "New category"))
addCategoryIntent.putExtras(extras)
ContextCompat.startActivity(this, addCategoryIntent, extras)
}
@ -116,216 +61,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
extras.putParcelable("product", null)
addProductIntent.putExtras(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)
}
}
}
@ -334,169 +69,86 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
private fun setupViewPager(viewpager: ViewPager) {
adapter = ViewPagerAdapter(supportFragmentManager)
adapter.addFragment(StorageFragment.newInstance("", arrayOf("")), getString(R.string.storage_title))
adapter.addFragment(ShelfFragment(), getString(R.string.shelf_title))
adapter.addFragment(CategoriesFragment(), getString(R.string.categories_title))
adapter.addFragment(StorageFragment(), getString(R.string.storage_title))
adapter.addFragment(ShelfFragment(), getString(R.string.shelf_title))
//TODO: settings fragments
viewpager.adapter = adapter
// setting adapter to view pager.
viewpager.setAdapter(adapter)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
private fun isOffline(): Boolean {
return sharedPreferences.getString("currentGroup", "") == "offline"
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
lateinit var intent: Intent
when (item.itemId) {
R.id.nav_account -> {
if (isOffline()) {
Toast.makeText(this, getString(R.string.online_only_feature), Toast.LENGTH_SHORT).show()
return false
}
intent = Intent(this, AccountSettingsActivity::class.java)
settingsActivityLauncher.launch(intent)
return true
}
R.id.nav_groups -> {
if (isOffline()) {
Toast.makeText(this, getString(R.string.online_only_feature), Toast.LENGTH_SHORT).show()
return false
}
intent = Intent(this, MyGroupsActivity::class.java)
}
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) {
binding.tabViewpager.setCurrentItem(0, true)
val currentPosition = binding.tabTablayout.selectedTabPosition
val fragment = adapter.getItem(currentPosition)
val storageFragment = fragment as StorageFragment
storageFragment.filterByCategory(id)
return when (item.itemId) {
R.id.action_settings -> {
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)
}
}
}

View File

@ -1,76 +0,0 @@
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
}
}

View File

@ -1,55 +0,0 @@
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)
}
}
}

View File

@ -1,50 +0,0 @@
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()
}
}

View File

@ -1,91 +0,0 @@
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))
}
}

View File

@ -1,348 +0,0 @@
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
}
}

View File

@ -1,114 +0,0 @@
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())
)
}
}

View File

@ -1,82 +0,0 @@
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"
}
}

View File

@ -1,172 +0,0 @@
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
}
}

View File

@ -2,8 +2,6 @@ package org.foxarmy.barcodescannerforemployees.dataclasses
import android.os.Parcel
import android.os.Parcelable
import org.foxarmy.barcodescannerforemployees.md5
import org.json.JSONObject
class AbstractProduct() : Parcelable {
var id: Int = 0
@ -34,10 +32,6 @@ class AbstractProduct() : Parcelable {
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) {
parcel.writeInt(id)
parcel.writeString(barcode)
@ -60,17 +54,5 @@ class AbstractProduct() : Parcelable {
override fun newArray(size: Int): Array<AbstractProduct?> {
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()
)
}
}
}

View File

@ -2,8 +2,6 @@ package org.foxarmy.barcodescannerforemployees.dataclasses
import android.os.Parcel
import android.os.Parcelable
import org.foxarmy.barcodescannerforemployees.md5
import org.json.JSONObject
class Category() : Parcelable {
var id = 0
@ -29,10 +27,6 @@ class Category() : Parcelable {
return 0
}
fun calculateHash(): String {
return "$id:$name".md5()
}
companion object CREATOR : Parcelable.Creator<Category> {
override fun createFromParcel(parcel: Parcel): Category {
return Category(parcel)
@ -41,12 +35,5 @@ class Category() : Parcelable {
override fun newArray(size: Int): Array<Category?> {
return arrayOfNulls(size)
}
fun createFromJSON(json: JSONObject): Category {
return Category(
json["local_id"].toString().toInt(),
json["name"].toString()
)
}
}
}

View File

@ -5,9 +5,6 @@ import android.os.Parcel
import android.os.Parcelable
import androidx.annotation.RequiresApi
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness
import org.foxarmy.barcodescannerforemployees.convertToUnixEpochTimestamp
import org.foxarmy.barcodescannerforemployees.md5
import org.json.JSONObject
class Product() : Parcelable {
var id = 0
@ -49,10 +46,6 @@ class Product() : Parcelable {
return 0
}
fun calculateHash(): String {
return "$id:$abstractProductId:$amount:$dateOfProduction:$dateOfExpiry".md5()
}
companion object CREATOR : Parcelable.Creator<Product> {
override fun createFromParcel(parcel: Parcel): Product {
return Product(parcel)
@ -61,16 +54,5 @@ class Product() : Parcelable {
override fun newArray(size: Int): Array<Product?> {
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())
)
}
}
}

View File

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

View File

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

View File

@ -1,8 +1,8 @@
package org.foxarmy.barcodescannerforemployees.fragments
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.provider.BaseColumns
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -11,39 +11,14 @@ import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.fragment.app.Fragment
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.CategoriesContract
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.AddCategoryActivity
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.views.CategoryView
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(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@ -60,21 +35,11 @@ class CategoriesFragment : Fragment() {
fun removeSelected() {
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
val db = DBStorageController(requireContext())
var deleted = false
for (view: CategoryView in layout?.children!!.iterator() as Iterator<CategoryView>) {
if (view.isCategorySelected) {
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()
}
db.eraseCategory(db.writableDatabase, view.category.id, requireContext())
deleted = true
}
}
@ -99,15 +64,28 @@ class CategoriesFragment : Fragment() {
}
fun updateContent() {
prepareDatabaseConnection()
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
layout?.removeAllViews()
val categories = categoryDAO.getAllCategories()
val db = DBStorageController(requireContext()).readableDatabase
for (category in categories) {
val categoryView = CategoryView(requireActivity(), requireContext(), category)
layout?.addView(categoryView)
val projection = arrayOf(
BaseColumns._ID,
CategoriesContract.CategoryEntry.CATEGORY_NAME
)
val cursor = db.query(CategoriesContract.CategoryEntry.TABLE_NAME, projection, null, null, null, null, null)
with (cursor) {
while(moveToNext()) {
val categoryId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
val categoryName = getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME))
val category = Category(categoryId, categoryName)
val categoryView = CategoryView(requireActivity(), requireContext(), category)
layout?.addView(categoryView)
}
}
}

View File

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

View File

@ -1,8 +1,8 @@
package org.foxarmy.barcodescannerforemployees.fragments
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.provider.BaseColumns
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -14,14 +14,13 @@ import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.fragment.app.Fragment
import androidx.gridlayout.widget.GridLayout
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.AbstractProductContract
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.ProductContract
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.AddProductActivity
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
import org.foxarmy.barcodescannerforemployees.databinding.FragmentShelfBinding
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import org.foxarmy.barcodescannerforemployees.views.ProductView
import kotlin.concurrent.thread
@ -29,26 +28,6 @@ class ShelfFragment : Fragment() {
private lateinit var binding: FragmentShelfBinding
private var updateInProgress = false
private var filterBy = ""
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(
inflater: LayoutInflater, container: ViewGroup?,
@ -58,12 +37,6 @@ class ShelfFragment : Fragment() {
fillUpSortBySpinner()
val filterByDate = arguments?.getLong("filterByExpiryDate") as Long?
if (filterByDate != null) {
filter = filterByDate.toString()
filterBy = "expiryDate"
}
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
updateContent()
@ -79,11 +52,6 @@ class ShelfFragment : Fragment() {
return binding.root
}
private fun prepareDatabaseConnection() {
val dbHelper = DBStorageController(requireContext(), sharedPreferences.getString("currentGroup", "database")!!)
productDAO = ProductDAO(dbHelper)
}
fun updateSelected() {
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
@ -122,7 +90,6 @@ class ShelfFragment : Fragment() {
}
fun updateContent() {
prepareDatabaseConnection()
thread {
if (updateInProgress) return@thread
updateInProgress = true
@ -131,7 +98,75 @@ class ShelfFragment : Fragment() {
grv?.removeAllViews()
}
val products = productDAO.getSortedListOfProducts(binding.spinner.selectedItemPosition, filterBy, filter)
val db = DBStorageController(requireContext()).readableDatabase
val projection = arrayOf(
BaseColumns._ID,
ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID,
ProductContract.ProductEntry.AMOUNT,
ProductContract.ProductEntry.DATE_OF_PRODUCTION,
ProductContract.ProductEntry.EXPIRY_DATE,
)
var orderBy: String = ""
when (binding.spinner.selectedItemPosition) {
0 -> {
orderBy =
"(SELECT ${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID} = ${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID}) ASC"
}
1 -> {
orderBy =
"(SELECT ${AbstractProductContract.AbstractProductEntry.CATEGORY} FROM ${AbstractProductContract.AbstractProductEntry.TABLE_NAME} WHERE ${BaseColumns._ID} = ${ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID}) ASC"
}
// Wow, I wrote this on first try but unfortunately SQLite can't do that :(
// I'll leave it here as a memory.
// "Freshness" -> {
// orderBy =
// "(SELECT ( (julianday(${ProductContract.ProductEntry.EXPIRY_DATE}) - julianday('now', 'localtime')) / (julianday(${ProductContract.ProductEntry.EXPIRY_DATE}) - julianday(${ProductContract.ProductEntry.DATE_OF_PRODUCTION})) )) ASC"
// }
3 -> {
orderBy = "${ProductContract.ProductEntry.DATE_OF_PRODUCTION} ASC"
}
4 -> {
orderBy = "${ProductContract.ProductEntry.EXPIRY_DATE} ASC"
}
}
val cursor = db.query(ProductContract.ProductEntry.TABLE_NAME, projection, null, null, null, null, orderBy)
val products = mutableListOf<Product>()
with(cursor) {
while (moveToNext()) {
val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
val abstractProductId =
getInt(getColumnIndexOrThrow(ProductContract.ProductEntry.ABSTRACT_PRODUCT_ID))
val amount = getInt(getColumnIndexOrThrow(ProductContract.ProductEntry.AMOUNT))
val dateOfProduction =
getLong(getColumnIndexOrThrow(ProductContract.ProductEntry.DATE_OF_PRODUCTION))
val dateOfExpiry = getLong(getColumnIndexOrThrow(ProductContract.ProductEntry.EXPIRY_DATE))
val product = Product(productId, abstractProductId, amount, dateOfProduction, dateOfExpiry)
if (binding.spinner.selectedItemPosition == 2) { //freshness
products.add(product)
} else {
val productView = ProductView(
requireActivity(),
requireContext(),
product
)
activity!!.runOnUiThread {
grv?.addView(productView)
}
}
}
}
products.sortWith(compareByDescending { it.freshness })
for (product in products.iterator()) {
val productView = ProductView(
@ -144,7 +179,6 @@ class ShelfFragment : Fragment() {
}
}
updateInProgress = false
}
}
@ -152,24 +186,14 @@ class ShelfFragment : Fragment() {
thread {
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
val db = DBStorageController(requireContext())
var deleted = false
for (view: ProductView in grv?.children!!.iterator() as Iterator<ProductView>) {
activity!!.runOnUiThread {
view.findViewById<ImageView>(R.id.productPicture).setImageURI(null)
}
if (view.isProductSelected) {
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()
}
db.eraseProduct(db.writableDatabase, view.product.id)
deleted = true
}
}
@ -182,16 +206,4 @@ class ShelfFragment : Fragment() {
updateContent()
}
}
companion object {
fun newInstance(date: Long):ShelfFragment = ShelfFragment().apply {
val fragment = ShelfFragment()
val args = Bundle()
args.putLong("filterByExpiryDate", date)
fragment.setArguments(args)
return fragment
}
}
}

View File

@ -1,24 +1,24 @@
package org.foxarmy.barcodescannerforemployees.fragments
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.provider.BaseColumns
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.fragment.app.Fragment
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.AbstractProductContract
import org.foxarmy.barcodescannerforemployees.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.AddAbstractProductActivity
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.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.generateThumbnailForImage
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
import kotlin.concurrent.thread
@ -26,12 +26,6 @@ import kotlin.concurrent.thread
class StorageFragment : Fragment() {
private lateinit var binding: FragmentStorageBinding
private var filterBy = ""
private lateinit var filter: Array<String>
private lateinit var sharedPreferences: SharedPreferences
private lateinit var abstractProductDAO: AbstractProductDAO
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@ -39,16 +33,6 @@ class StorageFragment : Fragment() {
): View {
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()
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
@ -61,22 +45,9 @@ class StorageFragment : Fragment() {
}
}
binding.dropFiltersButton.setOnClickListener {
filterBy = ""
updateContent()
}
filterBy = arguments?.getString("filterBy")!!
filter = arguments?.getStringArray("filter")!!
return binding.root
}
private fun prepareDatabaseConnection() {
val dbHelper = DBStorageController(requireContext(), sharedPreferences.getString("currentGroup", "database")!!)
abstractProductDAO = AbstractProductDAO(dbHelper)
}
override fun onResume() {
super.onResume()
@ -94,24 +65,14 @@ class StorageFragment : Fragment() {
thread {
val grv = binding.contentGridLayout
val db = DBStorageController(requireContext())
var deleted = false
for (view: AbstractProductView in grv.children.iterator() as Iterator<AbstractProductView>) {
activity!!.runOnUiThread {
view.findViewById<ImageView>(R.id.productPicture).setImageURI(null)
}
if (view.isProductSelected) {
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()
}
db.eraseAbstractProduct(db.writableDatabase, view.abstractProduct.id, requireContext())
deleted = true
}
}
@ -133,7 +94,6 @@ class StorageFragment : Fragment() {
val addProductIntent = Intent(requireContext(), AddAbstractProductActivity::class.java)
val extras = Bundle()
extras.putParcelable("abstractProduct", view.abstractProduct)
extras.putString("action", "update")
addProductIntent.putExtras(extras)
ContextCompat.startActivity(requireContext(), addProductIntent, extras)
}
@ -141,35 +101,58 @@ class StorageFragment : Fragment() {
}
fun updateContent() {
prepareDatabaseConnection()
thread {
val grv = binding.contentGridLayout
activity!!.runOnUiThread{
grv.removeAllViews()
}
val abstractProducts = abstractProductDAO.getSortedListOfAbstractProducts(binding.spinner.selectedItemPosition, filterBy, filter)
val db = DBStorageController(requireContext()).readableDatabase
val projection = arrayOf(
BaseColumns._ID,
AbstractProductContract.AbstractProductEntry.BARCODE,
AbstractProductContract.AbstractProductEntry.PRODUCT_NAME,
AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT,
AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME,
AbstractProductContract.AbstractProductEntry.CATEGORY,
AbstractProductContract.AbstractProductEntry.UNIT
)
for (abstractProduct in abstractProducts) {
generateThumbnailForImage(context!!, abstractProduct.imageHash)
val abstractProductView = AbstractProductView(
requireActivity(),
requireContext(),
abstractProduct
)
if (filterBy == "barcodeless") {
abstractProductView.setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
}
abstractProductView.findViewById<TextView>(R.id.productNameView).setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
}
var orderBy: String = ""
when(binding.spinner.selectedItemPosition) {
0 -> {
orderBy = "${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} ASC"
}
1 -> {
orderBy = "${AbstractProductContract.AbstractProductEntry.CATEGORY} ASC"
}
}
activity!!.runOnUiThread{
grv.addView(abstractProductView)
val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, null, null, null, null, orderBy)
with (cursor) {
while(moveToNext()) {
val productId = getInt(getColumnIndexOrThrow(BaseColumns._ID))
val barcode = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.BARCODE))
val productName = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME))
val netWeight = getDouble(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NET_WEIGHT))
val productImageHash = getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.IMAGE_FILENAME))
val category = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.CATEGORY))
val unit = getInt(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.UNIT))
val product = AbstractProduct(productId, barcode, productName, netWeight, productImageHash, category, unit)
generateThumbnailForImage(context!!, productImageHash)
val abstractProduct = AbstractProductView(
requireActivity(),
requireContext(),
product
)
activity!!.runOnUiThread{
grv.addView(abstractProduct)
}
}
}
}.join()
@ -180,23 +163,4 @@ class StorageFragment : Fragment() {
updateContent()
}
fun filterByCategory(id: Int) {
filterBy = "category"
filter = arrayOf("$id")
updateContent()
}
companion object {
fun newInstance(filterBy: String, filter: Array<String>):StorageFragment = StorageFragment().apply {
val fragment = StorageFragment()
val args = Bundle()
args.putString("filterBy", filterBy)
args.putStringArray("filter", filter)
fragment.setArguments(args)
return fragment
}
}
}

View File

@ -3,27 +3,18 @@ package org.foxarmy.barcodescannerforemployees.views
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.startActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.*
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.getActivity
import org.foxarmy.barcodescannerforemployees.getImageUri
import org.foxarmy.barcodescannerforemployees.getUnitNameById
import java.io.File
class AbstractProductView: LinearLayout {
@ -35,26 +26,12 @@ class AbstractProductView: LinearLayout {
private var unitField: TextView? = null
var abstractProduct: AbstractProduct = AbstractProduct()
var isProductSelected = false
private var activity: Activity
private var categoryDAO: CategoryDAO
private var sharedPreferences: SharedPreferences
private lateinit var activity: Activity
constructor(context: Context, a: AttributeSet) : super(context, a) {
activity = getActivity(context)!!
val inflater:LayoutInflater = activity.layoutInflater
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) {
@ -63,16 +40,6 @@ class AbstractProductView: LinearLayout {
val inflater:LayoutInflater = activity.layoutInflater
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()
}
@ -104,10 +71,6 @@ class AbstractProductView: LinearLayout {
this.background = ContextCompat.getDrawable(context, if (isProductSelected) R.drawable.outline_selected else R.drawable.outline)
true
}
productNameField!!.setOnClickListener {
Toast.makeText(activity, productNameField!!.text, Toast.LENGTH_SHORT).show()
}
}
fun update() {
@ -117,10 +80,10 @@ class AbstractProductView: LinearLayout {
thumbnailsDir.mkdirs()
val imageUri = getImageUri(activity, File(thumbnailsDir, "${abstractProduct.imageHash}.webp"))
productPicture!!.setImageURI(imageUri)
// productPicture!!.rotation = 90f
productPicture!!.rotation = 90f
productNameField!!.text = abstractProduct.name
netWeightField!!.text = abstractProduct.netWeight.toString()
categoryField!!.text = categoryDAO.getCategoryNameById(abstractProduct.category)
categoryField!!.text = DBStorageController(context).getCategoryNameById(DBStorageController(context).readableDatabase, abstractProduct.category)
}
}

View File

@ -2,18 +2,13 @@ package org.foxarmy.barcodescannerforemployees.views
import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
import android.view.LayoutInflater
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
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.DBStorageController
import org.foxarmy.barcodescannerforemployees.R
class CategoryView : LinearLayout {
var category: Category
@ -21,23 +16,9 @@ class CategoryView : LinearLayout {
val amountOfProducts: TextView
var isCategorySelected = false
private var abstractProductDAO: AbstractProductDAO
private var sharedPreferences: SharedPreferences
constructor(activity: Activity, context: Context, category: Category) : super(context) {
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
inflater.inflate(R.layout.category_view, this)
@ -47,15 +28,12 @@ class CategoryView : LinearLayout {
amountOfProducts = findViewById(R.id.amountOfProducts)
categoryName.text = category.name
amountOfProducts.text = abstractProductDAO.getAmountOfAbstractProductsInCategory(category.id).toString()
amountOfProducts.text = DBStorageController(context).getAmountOfAbstractProductsInCategory(DBStorageController(context).readableDatabase, category.id).toString()
setOnLongClickListener {
isCategorySelected = !isCategorySelected
this.background = ContextCompat.getDrawable(context, if (isCategorySelected) R.drawable.outline_selected else R.drawable.outline)
true
}
setOnClickListener {
(activity as MainActivity).filterAbstractProductsByCategory(category.id)
}
}
}

View File

@ -1,54 +0,0 @@
package org.foxarmy.barcodescannerforemployees.views
import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
import android.view.LayoutInflater
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.ExpiryCalendarActivity
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
import java.text.SimpleDateFormat
class ExpiryGroupView : LinearLayout {
private var representingDate: Long? = null
private var sharedPreferences: SharedPreferences
private var productDAO: ProductDAO
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
inflater.inflate(R.layout.expiry_group_view, this)
this.representingDate = representingDate
this.background = ContextCompat.getDrawable(context,R.drawable.outline)
val amount = productDAO.findAmountOfProductsWithExpiryDate(representingDate)
findViewById<TextView>(R.id.representingDateTextView).text = SimpleDateFormat("dd.MM.yyyy").format(representingDate * 1000)
findViewById<TextView>(R.id.amountTextView).text = amount.toString()
setOnClickListener {
(activity as ExpiryCalendarActivity).displayDate(representingDate)
}
}
}

View File

@ -1,25 +0,0 @@
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
}
}

View File

@ -1,37 +0,0 @@
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)
}
}
}

View File

@ -3,7 +3,6 @@ package org.foxarmy.barcodescannerforemployees.views
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.os.Build
@ -21,18 +20,10 @@ import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
import androidx.core.math.MathUtils.clamp
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.*
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.Product
import org.foxarmy.barcodescannerforemployees.getActivity
import org.foxarmy.barcodescannerforemployees.getImageUri
import java.io.File
import java.text.DecimalFormat
import java.text.SimpleDateFormat
@ -42,7 +33,7 @@ import kotlin.concurrent.thread
class ProductView: LinearLayout {
var product: Product = Product()
var isProductSelected = false
private var activity: Activity
private lateinit var activity: Activity
private lateinit var productImageView: ImageView
private lateinit var productNameTextView: TextView
@ -56,25 +47,8 @@ class ProductView: LinearLayout {
private var strokeColor: Int = 0x000000
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) {
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
inflater.inflate(R.layout.product_view, this)
}
@ -86,18 +60,6 @@ class ProductView: LinearLayout {
val inflater: LayoutInflater = activity.layoutInflater
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)
productNameTextView = findViewById(R.id.productNameView)
productNetWeightTextView = findViewById(R.id.productNetWeightView)
@ -115,7 +77,7 @@ class ProductView: LinearLayout {
productImageView.setOnClickListener {
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
val extras = Bundle()
val abstractProduct = abstractProductDAO.findAbstractProductById(product.abstractProductId)
val abstractProduct = DBStorageController(context).findAbstractProductById(DBStorageController(context).readableDatabase, product.abstractProductId)
extras.putString("imagehash", abstractProduct!!.imageHash)
fullscreenIntent.putExtras(extras)
startActivity(context, fullscreenIntent, extras)
@ -126,19 +88,19 @@ class ProductView: LinearLayout {
@RequiresApi(Build.VERSION_CODES.O)
fun update () {
val linkedAbstractProduct: AbstractProduct = abstractProductDAO.findAbstractProductById(product.abstractProductId) ?: return
val linkedAbstractProduct: AbstractProduct = DBStorageController(activity).findAbstractProductById(DBStorageController(activity).readableDatabase, product.abstractProductId)!!
val thumbnailsDir = File(activity.cacheDir, "thumbnails")
thumbnailsDir.mkdirs()
val pictureFile = File(thumbnailsDir, "${linkedAbstractProduct.imageHash}.webp")
val imageUri = getImageUri(activity, pictureFile)
productImageView.setImageURI(imageUri)
// productImageView.rotation = 90f
productImageView.rotation = 90f
productNameTextView.text = linkedAbstractProduct.name
productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString()
productAmountTextView.text = product.amount.toString()
productCategoryView.text = categoryDAO.getCategoryNameById(linkedAbstractProduct.category)
productCategoryView.text = DBStorageController(activity).getCategoryNameById(DBStorageController(activity).readableDatabase, linkedAbstractProduct.category)
productLifeSpan.text = "${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfProduction*1000))}-${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfExpiry*1000))}"
productFreshnessTextView.text =
if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY || product.freshness < 0) {

View File

@ -1,6 +0,0 @@
<?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>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" 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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" 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>

View File

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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>

View File

@ -1,74 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.MainActivity"
android:fitsSystemWindows="true" android:id="@+id/drawer_layout">
android:fitsSystemWindows="true"
tools:context=".activities.MainActivity">
<FrameLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent" android:id="@+id/storageLayout">
<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_height="match_parent" android:id="@+id/storageLayout">
android:layout_height="?attr/actionBarSize"/>
<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:id="@+id/appBarLayout">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="#FFF"
app:tabIndicatorHeight="3dp"
app:tabMode="fixed" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_tablayout"
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"
<!-- <include layout="@layout/content_storage"/>-->
<androidx.viewpager.widget.ViewPager
android:id="@+id/tab_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/drawer_menu" android:id="@+id/nav_view"/>
android:layout_marginTop="5dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.drawerlayout.widget.DrawerLayout>
<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"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_input_add"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" 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>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" 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>

View File

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" 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>

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@
android:text="@string/scan_label"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/categoryTextView"
android:layout_marginTop="40dp" android:layout_marginStart="5dp"/>
android:layout_marginTop="15dp" android:layout_marginStart="5dp"/>
<ImageView
android:src="@android:drawable/ic_menu_camera"
android:layout_width="0dp"
@ -61,42 +61,35 @@
android:inputType="text"
android:ems="10"
android:id="@+id/barcodeTextEdit" app:layout_constraintTop_toBottomOf="@+id/imageView"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="8dp" android:hint="@string/barcode" android:textColorHint="#737373"
android:layout_marginStart="5dp"/>
<CheckBox
android:text="@string/no_barcode"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/noBarcodeCheckBox"
app:layout_constraintStart_toEndOf="@+id/barcodeTextEdit"
app:layout_constraintTop_toBottomOf="@+id/imageView" android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="8dp" android:hint="Barcode" android:textColorHint="#737373"
android:layout_marginStart="5dp" android:layout_marginEnd="5dp"/>
<TextView
android:text="@string/category"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/categoryTextView"
app:layout_constraintTop_toBottomOf="@+id/netWeight"
android:layout_marginTop="30dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"/>
<Spinner
android:layout_width="140dp"
android:layout_height="50dp" android:id="@+id/categorySpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/categorySpinner"
app:layout_constraintStart_toEndOf="@+id/categoryTextView"
app:layout_constraintTop_toBottomOf="@+id/netWeight" android:layout_marginStart="8dp"
android:layout_marginTop="18dp" android:outlineProvider="bounds"/>
android:layout_marginTop="18dp"/>
<Button
android:text="@string/saveButton"
android:layout_width="100dp"
android:layout_height="50dp" android:id="@+id/saveButton"
app:layout_constraintTop_toBottomOf="@+id/categoryTextView"
android:layout_marginTop="40dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="15dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="5dp"/>
<Button
android:text="@string/takePicture"
android:layout_width="100dp"
android:layout_height="55dp" android:id="@+id/takePictureButton"
app:layout_constraintTop_toBottomOf="@+id/categoryTextView"
android:layout_marginTop="40dp"
android:layout_marginTop="15dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

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

View File

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

View File

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

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
android:layout_height="match_parent"
>
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"

View File

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

View File

@ -1,12 +0,0 @@
<?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>

View File

@ -1,11 +0,0 @@
<?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>

View File

@ -1,16 +0,0 @@
<?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>

View File

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
tools:context="org.foxarmy.barcodescannerforemployees.activities.MainActivity">
<item android:id="@+id/action_settings"
android:title="@string/settings"
android:title="@string/action_settings"
android:orderInCategory="100"
app:showAsAction="never"/>
<item android:id="@+id/action_delete" android:title="@string/delete_menu"/>

View File

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

View File

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

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">СканнерШтрихкодовДляРаботников</string>
<string name="settings">Настройки</string>
<string name="action_settings">Настройки</string>
<string name="first_fragment_label">Добавить продукт</string>
<string name="second_fragment_label">Продукты</string>
<string name="scan_label">Сканировать</string>
@ -77,49 +77,4 @@
<item>Имя</item>
<item>Категория</item>
</string-array>
<string name="abstract_product_does_not_exist">Абстрактный продукт с таким штрихкодом не существует. Хотите его добавить?. </string>
<string name="drop_filters">Убрать фильтры</string>
<string name="category_name_required">Требуется название категории</string>
<string name="abstract_product_already_exists">Такой абстрактный продукт в ваше базе данных уже есть</string>
<string name="quit">Выйти</string>
<string name="edit_existing">Редактировать существующий</string>
<string name="no_product_in_online_database">Продукт не найден в онлайн базе данных. Попробуйте снова, если это
ошибка сканирования или введите данные вручную
</string>
<string name="abstract_product_request">Пожалуйста, отсканируйте штрихкод, чтобы добавить продукт</string>
<string name="no_barcode">Нет штрихкода</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>

View File

@ -14,5 +14,4 @@
<color name="full_freshness">#5AFF30</color>
<color name="half_freshness">#FFF200</color>
<color name="expired_freshness">#ff0000</color>
<color name="seekbar_tick_color">#FFFFFF</color>
</resources>

View File

@ -1,6 +1,6 @@
<resources>
<string name="app_name">BarcodeScannerForEmployees</string>
<string name="settings">Settings</string>
<string name="action_settings">Settings</string>
<string name="first_fragment_label">Add new product</string>
<string name="second_fragment_label">Products</string>
<string name="netWeight">Net weight</string>
@ -75,49 +75,4 @@
<string name="expired">Expired</string>
<string name="barcode">Barcode</string>
<string name="scan_label">Scan</string>
<string name="abstract_product_does_not_exist">Abstract product with such barcode does not exist. Do you want to add one?</string>
<string name="drop_filters">Drop filters</string>
<string name="category_name_required">Category name required</string>
<string name="abstract_product_already_exists">You\'ve got an abstract product with such barcode in your database.</string>
<string name="quit">Quit</string>
<string name="edit_existing">Edit existing</string>
<string name="no_product_in_online_database">Product not found. Please, try again if you beleive barcode scanned
wrongly or type manually
</string>
<string name="abstract_product_request">Please, scan a barcode in order to add product</string>
<string name="no_barcode">No barcode present</string>
<string name="no_barcode_button">No barcode</string>
<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>

View File

@ -17,19 +17,15 @@ gridlayout = "1.0.0"
activity = "1.9.2"
legacySupportV4 = "1.0.0"
fragment = "1.8.4"
okhttp = "4.10.0"
playServicesCodeScanner = "16.1.0"
securityCrypto = "1.0.0"
volley = "1.2.1"
zxingAndroidEmbedded = "4.3.0"
material3Android = "1.3.0"
[libraries]
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraView" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "cameraView" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "cameraView" }
androidx-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" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
@ -44,11 +40,9 @@ androidx-gridlayout = { group = "androidx.gridlayout", name = "gridlayout", vers
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
androidx-legacy-support-v4 = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacySupportV4" }
androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
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" }
volley = { module = "com.android.volley:volley", version.ref = "volley" }
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]
android-application = { id = "com.android.application", version.ref = "agp" }