real-time sync with websockets (untested)

This commit is contained in:
leca 2024-11-13 18:51:46 +03:00
parent 8c3845a07e
commit fbf630090c
6 changed files with 165 additions and 62 deletions

View File

@ -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")
}
}

View File

@ -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()

View File

@ -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)
}

View File

@ -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()
)
}
}
}

View File

@ -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()
)
}
}
}

View File

@ -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())
)
}
}
}