From fbf630090c3aa7f586e04b0c88d43114d75bce66 Mon Sep 17 00:00:00 2001 From: leca Date: Wed, 13 Nov 2024 18:51:46 +0300 Subject: [PATCH] real-time sync with websockets (untested) --- .../WebSocketClient.kt | 113 ++++++++++++++++++ .../activities/GroupActivity.kt | 10 +- .../activities/MainActivity.kt | 70 ++--------- .../dataclasses/AbstractProduct.kt | 13 ++ .../dataclasses/Category.kt | 8 ++ .../dataclasses/Product.kt | 13 ++ 6 files changed, 165 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/WebSocketClient.kt diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/WebSocketClient.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/WebSocketClient.kt new file mode 100644 index 0000000..74ca600 --- /dev/null +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/WebSocketClient.kt @@ -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") + } +} \ No newline at end of file diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/GroupActivity.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/GroupActivity.kt index 4dbc9dc..2707136 100644 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/GroupActivity.kt +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/GroupActivity.kt @@ -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() diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/MainActivity.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/MainActivity.kt index e267b4d..9ab80eb 100644 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/MainActivity.kt +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/MainActivity.kt @@ -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) } diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/AbstractProduct.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/AbstractProduct.kt index b907776..20a4fd2 100644 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/AbstractProduct.kt +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/AbstractProduct.kt @@ -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 { 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() + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Category.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Category.kt index 1398681..ff645c7 100644 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Category.kt +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Category.kt @@ -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 { return arrayOfNulls(size) } + + fun createFromJSON(json: JSONObject): Category { + return Category( + json["local_id"].toString().toInt(), + json["name"].toString() + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Product.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Product.kt index c4cfb4e..a64b711 100644 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Product.kt +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/dataclasses/Product.kt @@ -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 { 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()) + ) + } } } \ No newline at end of file