real-time sync with websockets (untested)
This commit is contained in:
parent
8c3845a07e
commit
fbf630090c
|
@ -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 {
|
binding.createGroupButton.setOnClickListener {
|
||||||
val groupName = binding.groupNameTextEdit.text.toString()
|
val groupName = binding.groupNameTextEdit.text.toString()
|
||||||
val groupPassword = binding.groupPasswordTextEdit.text.toString()
|
val groupPassword = binding.groupPasswordTextEdit.text.toString()
|
||||||
// group is set to "successful"
|
|
||||||
val net = Net()
|
val net = Net()
|
||||||
net.language = sharedPreferences.getString("language", "en-US")!!
|
net.language = sharedPreferences.getString("language", "en-US")!!
|
||||||
net.server = sharedPreferences.getString("server", "")!!
|
net.server = sharedPreferences.getString("server", "")!!
|
||||||
|
@ -69,7 +68,14 @@ class GroupActivity : AppCompatActivity() {
|
||||||
if (!net.serverIsAvailable(this)) {
|
if (!net.serverIsAvailable(this)) {
|
||||||
noInternetConnectionAvailableNotification(this)
|
noInternetConnectionAvailableNotification(this)
|
||||||
} else {
|
} 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 response = net.joinGroup(groupId, groupPassword)
|
||||||
val responseText = response.body!!.string()
|
val responseText = response.body!!.string()
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
import androidx.security.crypto.MasterKeys
|
import androidx.security.crypto.MasterKeys
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import com.google.android.material.navigation.NavigationView
|
import com.google.android.material.navigation.NavigationView
|
||||||
|
import okhttp3.internal.wait
|
||||||
import org.foxarmy.barcodescannerforemployees.*
|
import org.foxarmy.barcodescannerforemployees.*
|
||||||
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
|
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
|
||||||
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
|
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
|
||||||
|
@ -112,8 +113,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||||
val currentGroup = sharedPreferences.getString("currentGroup", "offline")!!
|
val currentGroup = sharedPreferences.getString("currentGroup", "offline")!!
|
||||||
if ( currentGroup != "offline" && !net.serverIsAvailable(this)) {
|
if ( currentGroup != "offline" && !net.serverIsAvailable(this)) {
|
||||||
noInternetConnectionAvailableNotification(this)
|
noInternetConnectionAvailableNotification(this)
|
||||||
} else {
|
} else if (currentGroup != "offline" && net.serverIsAvailable(this)) {
|
||||||
synchronize()
|
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 != null && categoryInLocalDB != null) {
|
||||||
if (categoryInRemoteDB["hash"] != categoryInLocalDB.calculateHash()) {
|
if (categoryInRemoteDB["hash"] != categoryInLocalDB.calculateHash()) {
|
||||||
|
val updatedData = Category.createFromJSON(categoryInRemoteDB)
|
||||||
var updatedData: Category
|
|
||||||
|
|
||||||
with(categoryInRemoteDB) {
|
|
||||||
updatedData = Category(
|
|
||||||
this["local_id"].toString().toInt(),
|
|
||||||
this["name"].toString()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
categoryDAO.updateCategory(updatedData)
|
categoryDAO.updateCategory(updatedData)
|
||||||
}
|
}
|
||||||
|
@ -251,35 +247,14 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||||
}
|
}
|
||||||
|
|
||||||
if (productInLocalDB == null && remoteProduct != null) {
|
if (productInLocalDB == null && remoteProduct != null) {
|
||||||
var newProduct: Product
|
val newProduct = Product.createFromJSON(remoteProduct)
|
||||||
|
|
||||||
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)
|
productDAO.insertNewProduct(newProduct)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (productInRemoteDB != null && productInLocalDB != null) {
|
if (productInRemoteDB != null && productInLocalDB != null) {
|
||||||
if (productInRemoteDB["hash"] != productInLocalDB.calculateHash()) {
|
if (productInRemoteDB["hash"] != productInLocalDB.calculateHash()) {
|
||||||
|
val updatedData = Product.createFromJSON(productInRemoteDB)
|
||||||
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)
|
productDAO.updateProduct(updatedData)
|
||||||
}
|
}
|
||||||
|
@ -330,19 +305,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||||
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${localId}"
|
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${localId}"
|
||||||
net.downloadImage(url, pictureFile)
|
net.downloadImage(url, pictureFile)
|
||||||
|
|
||||||
var newAbstractProduct: AbstractProduct
|
val newAbstractProduct = AbstractProduct.createFromJSON(remoteAbstractProduct)
|
||||||
|
|
||||||
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)
|
abstractProductDAO.addAbstractProduct(newAbstractProduct)
|
||||||
|
|
||||||
|
@ -357,20 +320,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||||
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${localId}"
|
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${localId}"
|
||||||
net.downloadImage(url, pictureFile)
|
net.downloadImage(url, pictureFile)
|
||||||
|
|
||||||
var updatedData: AbstractProduct
|
val updatedData = AbstractProduct.createFromJSON(abstractProductInRemoteDB)
|
||||||
|
|
||||||
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)
|
abstractProductDAO.updateAbstractProduct(updatedData)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.foxarmy.barcodescannerforemployees.dataclasses
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import org.foxarmy.barcodescannerforemployees.md5
|
import org.foxarmy.barcodescannerforemployees.md5
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
class AbstractProduct() : Parcelable {
|
class AbstractProduct() : Parcelable {
|
||||||
var id: Int = 0
|
var id: Int = 0
|
||||||
|
@ -59,5 +60,17 @@ class AbstractProduct() : Parcelable {
|
||||||
override fun newArray(size: Int): Array<AbstractProduct?> {
|
override fun newArray(size: Int): Array<AbstractProduct?> {
|
||||||
return arrayOfNulls(size)
|
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.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import org.foxarmy.barcodescannerforemployees.md5
|
import org.foxarmy.barcodescannerforemployees.md5
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
class Category() : Parcelable {
|
class Category() : Parcelable {
|
||||||
var id = 0
|
var id = 0
|
||||||
|
@ -40,5 +41,12 @@ class Category() : Parcelable {
|
||||||
override fun newArray(size: Int): Array<Category?> {
|
override fun newArray(size: Int): Array<Category?> {
|
||||||
return arrayOfNulls(size)
|
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 android.os.Parcelable
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness
|
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness
|
||||||
|
import org.foxarmy.barcodescannerforemployees.convertToUnixEpochTimestamp
|
||||||
import org.foxarmy.barcodescannerforemployees.md5
|
import org.foxarmy.barcodescannerforemployees.md5
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
class Product() : Parcelable {
|
class Product() : Parcelable {
|
||||||
var id = 0
|
var id = 0
|
||||||
|
@ -59,5 +61,16 @@ class Product() : Parcelable {
|
||||||
override fun newArray(size: Int): Array<Product?> {
|
override fun newArray(size: Int): Array<Product?> {
|
||||||
return arrayOfNulls(size)
|
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())
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue