synchronizing
This commit is contained in:
		@@ -5,7 +5,11 @@ 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 {
 | 
			
		||||
@@ -383,4 +387,190 @@ class Net {
 | 
			
		||||
 | 
			
		||||
        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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,11 +12,19 @@ import androidx.annotation.RequiresApi
 | 
			
		||||
import androidx.core.content.FileProvider
 | 
			
		||||
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.*
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
}
 | 
			
		||||
@@ -60,8 +68,6 @@ 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
 | 
			
		||||
@@ -98,4 +104,29 @@ fun getUnitNameById (context: Context, id: Int): String {
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
}
 | 
			
		||||
@@ -154,7 +154,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
 | 
			
		||||
            net.server = sharedPreferences.getString("server", "")!!
 | 
			
		||||
            net.language = sharedPreferences.getString("language", "en-US")!!
 | 
			
		||||
 | 
			
		||||
            val currentGroup = sharedPreferences.getString("currentGroup", "")!!
 | 
			
		||||
            val currentGroup = sharedPreferences.getString("currentGroup", "offline")!!
 | 
			
		||||
 | 
			
		||||
            lateinit var response: Response
 | 
			
		||||
 | 
			
		||||
@@ -164,7 +164,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
 | 
			
		||||
                productName,
 | 
			
		||||
                netWeight.toString().toDouble(),
 | 
			
		||||
                pictureFile.nameWithoutExtension,
 | 
			
		||||
                categorySpinner.selectedItemPosition,
 | 
			
		||||
                categorySpinner.selectedItemPosition + 1,
 | 
			
		||||
                unitTypeSpinner.selectedItemPosition
 | 
			
		||||
            )
 | 
			
		||||
            val pictureFile = File(File(filesDir, "pictures"), "${abstractProduct!!.imageHash}.png")
 | 
			
		||||
@@ -269,8 +269,9 @@ 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()
 | 
			
		||||
            val imageHash = imageContent.toString(Charsets.UTF_8).md5()
 | 
			
		||||
            val imageHash = calculateMd5Hash(tempfile)
 | 
			
		||||
//            val imageContent = tempfile.inputStream().readBytes()
 | 
			
		||||
//            val imageHash = imageContent.toString(Charsets.UTF_8).md5()
 | 
			
		||||
 | 
			
		||||
            pictureFile = File(picturesPath, "$imageHash.png")
 | 
			
		||||
            Files.move(tempfile.toPath(), pictureFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ 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
 | 
			
		||||
@@ -31,7 +32,7 @@ class AddCategoryActivity : Activity() {
 | 
			
		||||
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!)
 | 
			
		||||
        val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!)
 | 
			
		||||
        DAO = CategoryDAO(dbHelper)
 | 
			
		||||
 | 
			
		||||
        val extras = intent.extras
 | 
			
		||||
@@ -41,6 +42,14 @@ 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 = sharedPreferences.getString("currentGroup", "offline")!!.toInt()
 | 
			
		||||
 | 
			
		||||
        findViewById<Button>(R.id.saveButton).setOnClickListener {
 | 
			
		||||
            if (categoryNameTextEdit.text.toString() == "") {
 | 
			
		||||
                Toast.makeText(this, getString(R.string.category_name_required), Toast.LENGTH_SHORT).show()
 | 
			
		||||
@@ -48,9 +57,13 @@ class AddCategoryActivity : Activity() {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (category.id == 0) { // Inserting new category
 | 
			
		||||
                DAO.addCategory(Category(0, categoryNameTextEdit.text.toString()))
 | 
			
		||||
                val newCategory = Category(0, categoryNameTextEdit.text.toString())
 | 
			
		||||
                newCategory.id = DAO.addCategory(newCategory).toInt()
 | 
			
		||||
                net.uploadCategory(currentGroup, newCategory)
 | 
			
		||||
            } else { // Updating existing category
 | 
			
		||||
                category.name = categoryNameTextEdit.text.toString()
 | 
			
		||||
                DAO.updateCategory(category)
 | 
			
		||||
                net.updateCategory(currentGroup, category)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            finish()
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ 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.R
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
 | 
			
		||||
@@ -67,10 +69,11 @@ class AddProductActivity : AppCompatActivity() {
 | 
			
		||||
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!)
 | 
			
		||||
        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)
 | 
			
		||||
@@ -137,6 +140,12 @@ 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()
 | 
			
		||||
@@ -168,12 +177,22 @@ class AddProductActivity : AppCompatActivity() {
 | 
			
		||||
                return@setOnClickListener
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "")!! == "") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt()
 | 
			
		||||
 | 
			
		||||
            var response: Response? = null
 | 
			
		||||
 | 
			
		||||
            if (updatingExistentProduct) {
 | 
			
		||||
                productDAO.updateProduct(product!!)
 | 
			
		||||
                if (currentGroup > 0) response = net.updateProduct(currentGroup, product!!)
 | 
			
		||||
            } else {
 | 
			
		||||
                productDAO.insertNewProduct(product!!)
 | 
			
		||||
                product!!.id = productDAO.insertNewProduct(product!!).toInt()
 | 
			
		||||
                if (currentGroup > 0) response = net.uploadProduct(currentGroup, product!!)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (response != null) {
 | 
			
		||||
                Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
 | 
			
		||||
            }   
 | 
			
		||||
 | 
			
		||||
            finish()
 | 
			
		||||
        }
 | 
			
		||||
        update()
 | 
			
		||||
 
 | 
			
		||||
@@ -57,9 +57,11 @@ class LoginActivity : AppCompatActivity() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                val r = n.getMyGroups().body!!.string()
 | 
			
		||||
                val myGroups = parseArray(r)
 | 
			
		||||
                val myGroups = parseArray(r).map { a -> a.toString()}
 | 
			
		||||
 | 
			
		||||
                sharedPreferences.edit().putStringSet("groups", myGroups.toSet()).apply()
 | 
			
		||||
                sharedPreferences.edit().putString("currentGroup", myGroups[0]).apply()
 | 
			
		||||
 | 
			
		||||
                sharedPreferences.edit().putStringSet("groups", myGroups.map { a -> a.toString()}.toSet())
 | 
			
		||||
                val intent = Intent(this, MainActivity::class.java)
 | 
			
		||||
                startActivity(intent)
 | 
			
		||||
                finish()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
package org.foxarmy.barcodescannerforemployees.activities
 | 
			
		||||
 | 
			
		||||
//import android.R
 | 
			
		||||
import android.content.DialogInterface
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
@@ -11,15 +10,28 @@ 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.Net
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.R
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.ViewPagerAdapter
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.convertToUnixEpochTimestamp
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.databinding.ActivityMainBinding
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.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 {
 | 
			
		||||
@@ -90,6 +102,234 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        synchronize()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun synchronize() {
 | 
			
		||||
 | 
			
		||||
        val sharedPreferences = EncryptedSharedPreferences.create(
 | 
			
		||||
            "sensitive",
 | 
			
		||||
            MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
 | 
			
		||||
            applicationContext,
 | 
			
		||||
            EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
 | 
			
		||||
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        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 (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()) {
 | 
			
		||||
 | 
			
		||||
                    var updatedData: Category
 | 
			
		||||
 | 
			
		||||
                    with(categoryInRemoteDB) {
 | 
			
		||||
                        updatedData = Category(
 | 
			
		||||
                            this["local_id"].toString().toInt(),
 | 
			
		||||
                            this["name"].toString()
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    categoryDAO.updateCategory(updatedData)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun syncProducts(
 | 
			
		||||
        productDAO: ProductDAO,
 | 
			
		||||
        remoteProducts: JSONArray,
 | 
			
		||||
        localProducts: List<Product>
 | 
			
		||||
    ) {
 | 
			
		||||
        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) {
 | 
			
		||||
                var newProduct: Product
 | 
			
		||||
 | 
			
		||||
                with(remoteProduct) {
 | 
			
		||||
                    newProduct = Product(
 | 
			
		||||
                        this["local_id"].toString().toInt(),
 | 
			
		||||
                        this["abstract_product_id"].toString().toInt(),
 | 
			
		||||
                        this["amount"].toString().toInt(),
 | 
			
		||||
                        convertToUnixEpochTimestamp(this["date_of_production"].toString()),
 | 
			
		||||
                        convertToUnixEpochTimestamp(this["expiry_date"].toString())
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                productDAO.insertNewProduct(newProduct)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (productInRemoteDB != null && productInLocalDB != null) {
 | 
			
		||||
                if (productInRemoteDB["hash"] != productInLocalDB.calculateHash()) {
 | 
			
		||||
 | 
			
		||||
                    var updatedData: Product
 | 
			
		||||
 | 
			
		||||
                    with(productInRemoteDB) {
 | 
			
		||||
                        updatedData = Product(
 | 
			
		||||
                            this["local_id"].toString().toInt(),
 | 
			
		||||
                            this["abstract_product_id"].toString().toInt(),
 | 
			
		||||
                            this["amount"].toString().toInt(),
 | 
			
		||||
                            convertToUnixEpochTimestamp(this["date_of_production"].toString()),
 | 
			
		||||
                            convertToUnixEpochTimestamp(this["expiry_date"].toString())
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    productDAO.updateProduct(updatedData)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun syncAbstractProducts(
 | 
			
		||||
        net: Net,
 | 
			
		||||
        currentGroup: Int,
 | 
			
		||||
        abstractProductDAO: AbstractProductDAO,
 | 
			
		||||
        remoteAbstractProducts: JSONArray,
 | 
			
		||||
        localAbstractProducts: List<AbstractProduct>
 | 
			
		||||
    ) {
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
                var newAbstractProduct: AbstractProduct
 | 
			
		||||
 | 
			
		||||
                with(remoteAbstractProduct) {
 | 
			
		||||
                    newAbstractProduct = AbstractProduct(
 | 
			
		||||
                        this["local_id"].toString().toInt(),
 | 
			
		||||
                        this["barcode"].toString(),
 | 
			
		||||
                        this["name"].toString(),
 | 
			
		||||
                        this["net_weight"].toString().toDouble(),
 | 
			
		||||
                        this["image_filename"].toString(),
 | 
			
		||||
                        this["category"].toString().toInt(),
 | 
			
		||||
                        this["unit"].toString().toInt()
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                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)
 | 
			
		||||
 | 
			
		||||
                    var updatedData: AbstractProduct
 | 
			
		||||
 | 
			
		||||
                    with(abstractProductInRemoteDB) {
 | 
			
		||||
                        updatedData = AbstractProduct(
 | 
			
		||||
                            this["local_id"].toString().toInt(),
 | 
			
		||||
                            this["barcode"].toString(),
 | 
			
		||||
                            this["name"].toString(),
 | 
			
		||||
                            this["net_weight"].toString().toDouble(),
 | 
			
		||||
                            this["image_filename"].toString(),
 | 
			
		||||
                            this["category"].toString().toInt(),
 | 
			
		||||
                            this["unit"].toString().toInt()
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    abstractProductDAO.updateAbstractProduct(updatedData)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setupViewPager(viewpager: ViewPager) {
 | 
			
		||||
@@ -117,9 +357,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
            R.id.nav_account -> {
 | 
			
		||||
                intent = Intent(this, AccountSettingsActivity::class.java)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            R.id.nav_groups -> {
 | 
			
		||||
                intent = Intent(this, MyGroupsActivity::class.java)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            R.id.nav_settings -> {
 | 
			
		||||
                //TODO: Settings
 | 
			
		||||
                return false
 | 
			
		||||
@@ -169,6 +411,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
 | 
			
		||||
                                }.show()
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        "ShelfFragment" -> {
 | 
			
		||||
                            val shelfFragment = fragment as ShelfFragment
 | 
			
		||||
                            shelfFragment.removeSelected()
 | 
			
		||||
 
 | 
			
		||||
@@ -232,6 +232,7 @@ class AbstractProductDAO(private val dbHelper: DBStorageController) {
 | 
			
		||||
        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())
 | 
			
		||||
@@ -249,6 +250,7 @@ class AbstractProductDAO(private val dbHelper: DBStorageController) {
 | 
			
		||||
        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())
 | 
			
		||||
@@ -265,7 +267,11 @@ class AbstractProductDAO(private val dbHelper: DBStorageController) {
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getSortedListOfAbstractProducts(selectedSort: Int, filterBy: String, filter: Array<String>): List<AbstractProduct> {
 | 
			
		||||
    fun getSortedListOfAbstractProducts(
 | 
			
		||||
        selectedSort: Int,
 | 
			
		||||
        filterBy: String,
 | 
			
		||||
        filter: Array<String>
 | 
			
		||||
    ): List<AbstractProduct> {
 | 
			
		||||
 | 
			
		||||
        val db = dbHelper.readableDatabase
 | 
			
		||||
 | 
			
		||||
@@ -282,10 +288,11 @@ class AbstractProductDAO(private val dbHelper: DBStorageController) {
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        var orderBy: String = ""
 | 
			
		||||
        when(selectedSort) {
 | 
			
		||||
        when (selectedSort) {
 | 
			
		||||
            0 -> {
 | 
			
		||||
                orderBy = "${AbstractProductContract.AbstractProductEntry.PRODUCT_NAME} ASC"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            1 -> {
 | 
			
		||||
                orderBy = "${AbstractProductContract.AbstractProductEntry.CATEGORY} ASC"
 | 
			
		||||
            }
 | 
			
		||||
@@ -299,6 +306,7 @@ class AbstractProductDAO(private val dbHelper: DBStorageController) {
 | 
			
		||||
                selection = "${AbstractProductContract.AbstractProductEntry.CATEGORY} = ?"
 | 
			
		||||
                selectionArgs = filter
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            "barcodeless" -> {
 | 
			
		||||
                selection = "${AbstractProductContract.AbstractProductEntry.BARCODE} = '' "
 | 
			
		||||
                selectionArgs = null
 | 
			
		||||
@@ -306,24 +314,35 @@ class AbstractProductDAO(private val dbHelper: DBStorageController) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        val cursor = db.query(AbstractProductContract.AbstractProductEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy)
 | 
			
		||||
        val cursor = db.query(
 | 
			
		||||
            AbstractProductContract.AbstractProductEntry.TABLE_NAME,
 | 
			
		||||
            projection,
 | 
			
		||||
            selection,
 | 
			
		||||
            selectionArgs,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            orderBy
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        with (cursor) {
 | 
			
		||||
            while(moveToNext()) {
 | 
			
		||||
        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 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)
 | 
			
		||||
                val product =
 | 
			
		||||
                    AbstractProduct(productId, barcode, productName, netWeight, productImageHash, category, unit)
 | 
			
		||||
 | 
			
		||||
                abstractProducts.add(product)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return  abstractProducts
 | 
			
		||||
        return abstractProducts
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,7 @@ import android.content.Context
 | 
			
		||||
import android.provider.BaseColumns
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
 | 
			
		||||
 | 
			
		||||
class CategoryDAO (private val dbHelper: DBStorageController) {
 | 
			
		||||
class CategoryDAO(private val dbHelper: DBStorageController) {
 | 
			
		||||
    fun eraseCategory(id: Int, context: Context) {
 | 
			
		||||
 | 
			
		||||
        val abstractProductDAO = AbstractProductDAO(dbHelper)
 | 
			
		||||
@@ -56,6 +56,7 @@ class CategoryDAO (private val dbHelper: DBStorageController) {
 | 
			
		||||
        val categories = mutableListOf<Category>()
 | 
			
		||||
 | 
			
		||||
        val projection = arrayOf(
 | 
			
		||||
            BaseColumns._ID,
 | 
			
		||||
            CategoriesContract.CategoryEntry.CATEGORY_NAME
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -72,7 +73,7 @@ class CategoryDAO (private val dbHelper: DBStorageController) {
 | 
			
		||||
        with(cursor) {
 | 
			
		||||
            while (moveToNext()) {
 | 
			
		||||
                val category = Category(
 | 
			
		||||
                    getInt(getColumnIndexOrThrow(android.provider.BaseColumns._ID)),
 | 
			
		||||
                    getInt(getColumnIndexOrThrow(BaseColumns._ID)),
 | 
			
		||||
                    getString(getColumnIndexOrThrow(CategoriesContract.CategoryEntry.CATEGORY_NAME))
 | 
			
		||||
                )
 | 
			
		||||
                categories.add(category)
 | 
			
		||||
@@ -82,15 +83,18 @@ class CategoryDAO (private val dbHelper: DBStorageController) {
 | 
			
		||||
        return categories
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun addCategory(category: Category) {
 | 
			
		||||
    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)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        db.insert(CategoriesContract.CategoryEntry.TABLE_NAME, null, values)
 | 
			
		||||
        val id = db.insert(CategoriesContract.CategoryEntry.TABLE_NAME, null, values)
 | 
			
		||||
 | 
			
		||||
        return id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateCategory(category: Category) {
 | 
			
		||||
@@ -100,6 +104,11 @@ class CategoryDAO (private val dbHelper: DBStorageController) {
 | 
			
		||||
        val values = ContentValues().apply {
 | 
			
		||||
            put(CategoriesContract.CategoryEntry.CATEGORY_NAME, category.name)
 | 
			
		||||
        }
 | 
			
		||||
        db.update(CategoriesContract.CategoryEntry.TABLE_NAME, values, "${BaseColumns._ID} = ?", arrayOf(category.id.toString()))
 | 
			
		||||
        db.update(
 | 
			
		||||
            CategoriesContract.CategoryEntry.TABLE_NAME,
 | 
			
		||||
            values,
 | 
			
		||||
            "${BaseColumns._ID} = ?",
 | 
			
		||||
            arrayOf(category.id.toString())
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -13,18 +13,20 @@ class ProductDAO (private val dbHelper: DBStorageController) {
 | 
			
		||||
        db.delete(ProductContract.ProductEntry.TABLE_NAME, BaseColumns._ID + "=" + id, null)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun insertNewProduct(product: Product) {
 | 
			
		||||
    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)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        db.insert(ProductContract.ProductEntry.TABLE_NAME, null, values)
 | 
			
		||||
        val id = db.insert(ProductContract.ProductEntry.TABLE_NAME, null, values)
 | 
			
		||||
        return id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun findAmountOfProductsWithExpiryDate(date: Long): Int {
 | 
			
		||||
@@ -82,6 +84,7 @@ class ProductDAO (private val dbHelper: DBStorageController) {
 | 
			
		||||
        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)
 | 
			
		||||
@@ -157,9 +160,7 @@ class ProductDAO (private val dbHelper: DBStorageController) {
 | 
			
		||||
 | 
			
		||||
                val product = Product(productId, abstractProductId, amount, dateOfProduction, dateOfExpiry)
 | 
			
		||||
 | 
			
		||||
                if (selectedSort == 2) { //freshness
 | 
			
		||||
                    products.add(product)
 | 
			
		||||
                }
 | 
			
		||||
                products.add(product)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package org.foxarmy.barcodescannerforemployees.dataclasses
 | 
			
		||||
 | 
			
		||||
import android.os.Parcel
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.md5
 | 
			
		||||
 | 
			
		||||
class AbstractProduct() : Parcelable {
 | 
			
		||||
    var id: Int = 0
 | 
			
		||||
@@ -32,6 +33,10 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package org.foxarmy.barcodescannerforemployees.dataclasses
 | 
			
		||||
 | 
			
		||||
import android.os.Parcel
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.md5
 | 
			
		||||
 | 
			
		||||
class Category() : Parcelable {
 | 
			
		||||
    var id = 0
 | 
			
		||||
@@ -27,6 +28,10 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import android.os.Parcel
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import androidx.annotation.RequiresApi
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.md5
 | 
			
		||||
 | 
			
		||||
class Product() : Parcelable {
 | 
			
		||||
    var id = 0
 | 
			
		||||
@@ -46,6 +47,10 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -37,13 +37,24 @@ class AbstractProductView: LinearLayout {
 | 
			
		||||
    var isProductSelected = false
 | 
			
		||||
    private var activity: Activity
 | 
			
		||||
 | 
			
		||||
    private lateinit var categoryDAO: CategoryDAO
 | 
			
		||||
    private lateinit var sharedPreferences: SharedPreferences
 | 
			
		||||
    private var categoryDAO: CategoryDAO
 | 
			
		||||
    private var sharedPreferences: SharedPreferences
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ import kotlin.concurrent.thread
 | 
			
		||||
class ProductView: LinearLayout {
 | 
			
		||||
    var product: Product = Product()
 | 
			
		||||
    var isProductSelected = false
 | 
			
		||||
    private lateinit var activity: Activity
 | 
			
		||||
    private var activity: Activity
 | 
			
		||||
 | 
			
		||||
    private lateinit var productImageView: ImageView
 | 
			
		||||
    private lateinit var productNameTextView: TextView
 | 
			
		||||
@@ -56,9 +56,9 @@ class ProductView: LinearLayout {
 | 
			
		||||
    private var strokeColor: Int = 0x000000
 | 
			
		||||
    private lateinit var outline: GradientDrawable
 | 
			
		||||
 | 
			
		||||
    private lateinit var categoryDAO: CategoryDAO
 | 
			
		||||
    private lateinit var abstractProductDAO: AbstractProductDAO
 | 
			
		||||
    private lateinit var sharedPreferences: SharedPreferences
 | 
			
		||||
    private var categoryDAO: CategoryDAO
 | 
			
		||||
    private var abstractProductDAO: AbstractProductDAO
 | 
			
		||||
    private var sharedPreferences: SharedPreferences
 | 
			
		||||
 | 
			
		||||
    constructor(context: Context, a: AttributeSet) : super(context, a) {
 | 
			
		||||
        activity = getActivity(context)!!
 | 
			
		||||
@@ -71,7 +71,7 @@ class ProductView: LinearLayout {
 | 
			
		||||
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
 | 
			
		||||
        val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "offline")!!)
 | 
			
		||||
        abstractProductDAO = AbstractProductDAO(dbHelper)
 | 
			
		||||
        categoryDAO = CategoryDAO(dbHelper)
 | 
			
		||||
 | 
			
		||||
@@ -86,6 +86,18 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user