real-time sync with websockets (untested)
This commit is contained in:
		@@ -0,0 +1,113 @@
 | 
			
		||||
package org.foxarmy.barcodescannerforemployees
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import androidx.annotation.RequiresApi
 | 
			
		||||
import okhttp3.*
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
class WebSocketClient(private val context: Context, private val server: String) {
 | 
			
		||||
 | 
			
		||||
    private lateinit var webSocket: WebSocket
 | 
			
		||||
 | 
			
		||||
    fun connect(token: String, currentGroup: String) {
 | 
			
		||||
        val client = OkHttpClient()
 | 
			
		||||
        val request = Request.Builder().url("ws://$server:8282").build()
 | 
			
		||||
        client.newWebSocket(request, object : WebSocketListener() {
 | 
			
		||||
            override fun onOpen(webSocket: WebSocket, response: Response) {
 | 
			
		||||
                this@WebSocketClient.webSocket = webSocket
 | 
			
		||||
                sendMessage("\"token\":\"$token\",\"currentGroup\":\"$currentGroup\"")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @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)
 | 
			
		||||
                                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)
 | 
			
		||||
                                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 Int
 | 
			
		||||
 | 
			
		||||
                        when(item) {
 | 
			
		||||
                            "abstractproduct" -> {
 | 
			
		||||
                                abstractProductDAO.eraseAbstractProduct(id, context)
 | 
			
		||||
                            }
 | 
			
		||||
                            "product" -> {
 | 
			
		||||
                                productDAO.eraseProduct(id)
 | 
			
		||||
                            }
 | 
			
		||||
                            "category" -> {
 | 
			
		||||
                                categoryDAO.eraseCategory(id, context)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun sendMessage(message: String) {
 | 
			
		||||
        webSocket.send(message)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun close() {
 | 
			
		||||
        webSocket.close(1000, "Closing WebSocket connection")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -31,7 +31,6 @@ class GroupActivity : AppCompatActivity() {
 | 
			
		||||
        binding.createGroupButton.setOnClickListener {
 | 
			
		||||
            val groupName = binding.groupNameTextEdit.text.toString()
 | 
			
		||||
            val groupPassword = binding.groupPasswordTextEdit.text.toString()
 | 
			
		||||
            // group is set to "successful"
 | 
			
		||||
            val net = Net()
 | 
			
		||||
            net.language = sharedPreferences.getString("language", "en-US")!!
 | 
			
		||||
            net.server = sharedPreferences.getString("server", "")!!
 | 
			
		||||
@@ -69,7 +68,14 @@ class GroupActivity : AppCompatActivity() {
 | 
			
		||||
            if (!net.serverIsAvailable(this)) {
 | 
			
		||||
                noInternetConnectionAvailableNotification(this)
 | 
			
		||||
            } else {
 | 
			
		||||
                val groupId = net.getGroupId(groupName).toInt()
 | 
			
		||||
                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()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ import androidx.security.crypto.EncryptedSharedPreferences
 | 
			
		||||
import androidx.security.crypto.MasterKeys
 | 
			
		||||
import androidx.viewpager.widget.ViewPager
 | 
			
		||||
import com.google.android.material.navigation.NavigationView
 | 
			
		||||
import okhttp3.internal.wait
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.*
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
 | 
			
		||||
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
 | 
			
		||||
@@ -112,8 +113,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
        val currentGroup = sharedPreferences.getString("currentGroup", "offline")!!
 | 
			
		||||
        if ( currentGroup != "offline" && !net.serverIsAvailable(this)) {
 | 
			
		||||
            noInternetConnectionAvailableNotification(this)
 | 
			
		||||
        } else {
 | 
			
		||||
        } else if (currentGroup != "offline" && net.serverIsAvailable(this)) {
 | 
			
		||||
            synchronize()
 | 
			
		||||
            val ws = WebSocketClient(this, sharedPreferences.getString("server", "")!!)
 | 
			
		||||
            val token = sharedPreferences.getString("token", "")!!
 | 
			
		||||
            ws.connect(token, currentGroup)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -204,15 +208,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
 | 
			
		||||
            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()
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                    val updatedData = Category.createFromJSON(categoryInRemoteDB)
 | 
			
		||||
 | 
			
		||||
                    categoryDAO.updateCategory(updatedData)
 | 
			
		||||
                }
 | 
			
		||||
@@ -251,35 +247,14 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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())
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                val newProduct = Product.createFromJSON(remoteProduct)
 | 
			
		||||
 | 
			
		||||
                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())
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                    val updatedData = Product.createFromJSON(productInRemoteDB)
 | 
			
		||||
 | 
			
		||||
                    productDAO.updateProduct(updatedData)
 | 
			
		||||
                }
 | 
			
		||||
@@ -330,19 +305,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
                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()
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                val newAbstractProduct = AbstractProduct.createFromJSON(remoteAbstractProduct)
 | 
			
		||||
 | 
			
		||||
                abstractProductDAO.addAbstractProduct(newAbstractProduct)
 | 
			
		||||
 | 
			
		||||
@@ -357,20 +320,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
                    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()
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    val updatedData = AbstractProduct.createFromJSON(abstractProductInRemoteDB)
 | 
			
		||||
 | 
			
		||||
                    abstractProductDAO.updateAbstractProduct(updatedData)
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ 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
 | 
			
		||||
@@ -59,5 +60,17 @@ 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()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,7 @@ 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
 | 
			
		||||
@@ -40,5 +41,12 @@ 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()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,9 @@ 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
 | 
			
		||||
@@ -59,5 +61,16 @@ 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())
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user