fixed websockets, synchronization works properly, and a bunch of bugfixes

This commit is contained in:
leca 2024-11-14 03:53:28 +03:00
parent fbf630090c
commit 988272070d
6 changed files with 107 additions and 45 deletions

View File

@ -2,8 +2,10 @@ package org.foxarmy.barcodescannerforemployees
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import okhttp3.* import okhttp3.*
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController import org.foxarmy.barcodescannerforemployees.database.DBStorageController
@ -12,18 +14,30 @@ import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.dataclasses.Category import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.dataclasses.Product import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import org.json.JSONObject import org.json.JSONObject
import java.io.File
import java.util.concurrent.TimeUnit
class WebSocketClient(private val context: Context, private val server: String) { class WebSocketClient(private val context: Context, private val server: String) {
private lateinit var webSocket: WebSocket private lateinit var webSocket: WebSocket
fun connect(token: String, currentGroup: String) { fun connect(token: String, currentGroup: String) {
val client = OkHttpClient() val client = OkHttpClient.Builder()
val request = Request.Builder().url("ws://$server:8282").build() .connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.MINUTES)
.writeTimeout(10, TimeUnit.SECONDS)
.callTimeout(1, TimeUnit.MINUTES)
.build()
val request = Request.Builder()
.url("wss://$server")
.build()
client.newWebSocket(request, object : WebSocketListener() { client.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) { override fun onOpen(webSocket: WebSocket, response: Response) {
this@WebSocketClient.webSocket = webSocket this@WebSocketClient.webSocket = webSocket
sendMessage("\"token\":\"$token\",\"currentGroup\":\"$currentGroup\"") sendMessage("{\"token\":\"$token\",\"currentGroup\":\"$currentGroup\"}")
keepAlive()
} }
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
@ -45,6 +59,19 @@ class WebSocketClient(private val context: Context, private val server: String)
when (item) { when (item) {
"abstractproduct" -> { "abstractproduct" -> {
val newAbstractProduct = AbstractProduct.createFromJSON(data) val newAbstractProduct = AbstractProduct.createFromJSON(data)
val net = Net()
net.server = server
net.token = token
val picturesDir = File(context.filesDir, "pictures")
picturesDir.mkdirs()
val pictureFile =
File(picturesDir, "${data["image_filename"]}.png")
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${data["local_id"]}"
net.downloadImage(url, pictureFile)
abstractProductDAO.addAbstractProduct(newAbstractProduct) abstractProductDAO.addAbstractProduct(newAbstractProduct)
} }
@ -64,6 +91,19 @@ class WebSocketClient(private val context: Context, private val server: String)
when (item) { when (item) {
"abstractproduct" -> { "abstractproduct" -> {
val updatedAbstractProduct = AbstractProduct.createFromJSON(data) val updatedAbstractProduct = AbstractProduct.createFromJSON(data)
val net = Net()
net.server = server
net.token = token
val picturesDir = File(context.filesDir, "pictures")
picturesDir.mkdirs()
val pictureFile =
File(picturesDir, "${data["image_filename"]}.png")
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${data["local_id"]}"
net.downloadImage(url, pictureFile)
abstractProductDAO.updateAbstractProduct(updatedAbstractProduct) abstractProductDAO.updateAbstractProduct(updatedAbstractProduct)
} }
"product" -> { "product" -> {
@ -78,31 +118,44 @@ class WebSocketClient(private val context: Context, private val server: String)
} }
"delete" -> { "delete" -> {
val id = data["local_id"] as Int val id = data["local_id"] as String
when(item) { when(item) {
"abstractproduct" -> { "abstractproduct" -> {
abstractProductDAO.eraseAbstractProduct(id, context) abstractProductDAO.eraseAbstractProduct(id.toInt(), context)
} }
"product" -> { "product" -> {
productDAO.eraseProduct(id) productDAO.eraseProduct(id.toInt())
} }
"category" -> { "category" -> {
categoryDAO.eraseCategory(id, context) categoryDAO.eraseCategory(id.toInt(), context)
} }
} }
} }
} }
(context as MainActivity).updateAll()
} }
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
Log.d("QWERTYUIOP", "Closing ws. Reason: $reason")
} }
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
Log.d("QWERTYUIOP","Connection failed: ${t.message}")
} }
}) })
} }
private fun keepAlive() {
Thread {
while (true) {
Thread.sleep(30000)
webSocket.send("keepalive")
}
}.start()
}
fun sendMessage(message: String) { fun sendMessage(message: String) {
webSocket.send(message) webSocket.send(message)
} }

View File

@ -73,7 +73,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
) )
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!) val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!)
DAO = AbstractProductDAO(dbHelper) DAO = AbstractProductDAO(dbHelper)
picturesPath = File(filesDir, "pictures") picturesPath = File(filesDir, "pictures")
@ -109,24 +109,24 @@ class AddAbstractProductActivity : AppCompatActivity() {
action = extras!!.get("action") as String action = extras!!.get("action") as String
when (action) { when (action) {
"update" -> { "update" -> {
abstractProduct = extras.get("abstractProduct") as AbstractProduct? abstractProduct = extras.get("abstractProduct") as AbstractProduct
} }
"new_from_barcode" -> { "new_from_barcode" -> {
abstractProduct = extras.get("abstractProduct") as AbstractProduct? abstractProduct = extras.get("abstractProduct") as AbstractProduct
barcode = abstractProduct!!.barcode barcode = abstractProduct!!.barcode
performRequest(abstractProduct!!.barcode) performRequest(abstractProduct!!.barcode)
} }
} }
if (abstractProduct != null) { if (abstractProduct != null && action == "update") {
val imageThumbnailUri = getImageUri(this, File(thumbnailsDir, "${abstractProduct!!.imageHash}.webp")) val imageThumbnailUri = getImageUri(this, File(thumbnailsDir, "${abstractProduct!!.imageHash}.webp"))
pictureFile = File(picturesPath, "${abstractProduct!!.imageHash}.png]") pictureFile = File(picturesPath, "${abstractProduct!!.imageHash}.png")
imageView.setImageURI(imageThumbnailUri) imageView.setImageURI(imageThumbnailUri)
barcodeText.setText(abstractProduct!!.barcode) barcodeText.setText(abstractProduct!!.barcode)
productNameText.text = abstractProduct!!.name productNameText.text = abstractProduct!!.name
netWeightText.text = abstractProduct!!.netWeight.toString() netWeightText.text = abstractProduct!!.netWeight.toString()
categorySpinner.setSelection(abstractProduct!!.category) categorySpinner.setSelection(abstractProduct!!.category - 1)
unitTypeSpinner.setSelection(abstractProduct!!.unit) unitTypeSpinner.setSelection(abstractProduct!!.unit)
} }
@ -149,6 +149,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
} }
if (netWeight.toString() == "" || netWeight.toString().toDoubleOrNull() == null) { if (netWeight.toString() == "" || netWeight.toString().toDoubleOrNull() == null) {
Toast.makeText(this, getString(R.string.product_net_weight_request), Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.product_net_weight_request), Toast.LENGTH_SHORT).show()
return@setOnClickListener
} }
val net = Net() val net = Net()
@ -157,7 +158,7 @@ class AddAbstractProductActivity : AppCompatActivity() {
net.server = sharedPreferences.getString("server", "")!! net.server = sharedPreferences.getString("server", "")!!
net.language = sharedPreferences.getString("language", "en-US")!! net.language = sharedPreferences.getString("language", "en-US")!!
val currentGroup = sharedPreferences.getString("currentGroup", "offline")!! val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt()
lateinit var response: Response lateinit var response: Response
@ -174,10 +175,10 @@ class AddAbstractProductActivity : AppCompatActivity() {
if (action == "update") { if (action == "update") {
DAO.updateAbstractProduct(abstractProduct!!) DAO.updateAbstractProduct(abstractProduct!!)
response = net.updateAbstractProduct(currentGroup.toInt(), abstractProduct!!, pictureFile) response = net.updateAbstractProduct(currentGroup, abstractProduct!!, pictureFile)
} else if (action == "new" || action == "new_from_barcode") { } else if (action == "new" || action == "new_from_barcode") {
abstractProduct!!.id = DAO.addAbstractProduct(abstractProduct!!).toInt() abstractProduct!!.id = DAO.addAbstractProduct(abstractProduct!!).toInt()
response = net.uploadAbstractProduct(currentGroup.toInt(), abstractProduct!!, pictureFile); response = net.uploadAbstractProduct(currentGroup, abstractProduct!!, pictureFile);
} }
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show() Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
@ -198,7 +199,6 @@ class AddAbstractProductActivity : AppCompatActivity() {
barcodeText.setText(this.barcode) barcodeText.setText(this.barcode)
val net = Net(); val net = Net();
val result = net.requestProductFromOnlineDB(barcode) val result = net.requestProductFromOnlineDB(barcode)
Log.d("QWERTYUIOP", "Result of request: $result")
var abstractProduct: AbstractProduct var abstractProduct: AbstractProduct
@ -272,18 +272,11 @@ class AddAbstractProductActivity : AppCompatActivity() {
if (success) { if (success) {
//Move picture to a proper directory according to its calculated hash //Move picture to a proper directory according to its calculated hash
val tempfile = File(filesDir, "image.png") val tempfile = File(filesDir, "image.png")
val imageHash = calculateMd5Hash(tempfile)
// val imageContent = tempfile.inputStream().readBytes()
// val imageHash = imageContent.toString(Charsets.UTF_8).md5()
pictureFile = File(picturesPath, "$imageHash.png") val imageContent = tempfile.inputStream().readBytes()
Files.move(tempfile.toPath(), pictureFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
tempfile.delete()
val imageContent = pictureFile.inputStream().readBytes()
var img = BitmapFactory.decodeByteArray(imageContent, 0, imageContent.size) var img = BitmapFactory.decodeByteArray(imageContent, 0, imageContent.size)
val exif = ExifInterface(pictureFile.absoluteFile.toString()) val exif = ExifInterface(tempfile.absoluteFile.toString())
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
val matrix = Matrix() val matrix = Matrix()
@ -298,9 +291,15 @@ class AddAbstractProductActivity : AppCompatActivity() {
rotated.compress( rotated.compress(
Bitmap.CompressFormat.WEBP_LOSSY, Bitmap.CompressFormat.WEBP_LOSSY,
100 * (15 / (16 - compressionFactor)), 100 * (15 / (16 - compressionFactor)),
FileOutputStream(pictureFile) FileOutputStream(tempfile)
) )
val imageHash = calculateMd5Hash(tempfile)
pictureFile = File(picturesPath, "$imageHash.png")
Files.move(tempfile.toPath(), pictureFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
tempfile.delete()
generateThumbnailForImage(this, imageHash) generateThumbnailForImage(this, imageHash)
imageView.setImageURI(getImageUri(this, pictureFile)) imageView.setImageURI(getImageUri(this, pictureFile))

View File

@ -48,7 +48,7 @@ class AddCategoryActivity : Activity() {
net.server = sharedPreferences.getString("server", "")!! net.server = sharedPreferences.getString("server", "")!!
net.language = sharedPreferences.getString("language", "")!! net.language = sharedPreferences.getString("language", "")!!
val currentGroup = sharedPreferences.getString("currentGroup", "offline")!!.toInt() val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt()
findViewById<Button>(R.id.saveButton).setOnClickListener { findViewById<Button>(R.id.saveButton).setOnClickListener {
if (categoryNameTextEdit.text.toString() == "") { if (categoryNameTextEdit.text.toString() == "") {
@ -59,11 +59,11 @@ class AddCategoryActivity : Activity() {
if (category.id == 0) { // Inserting new category if (category.id == 0) { // Inserting new category
val newCategory = Category(0, categoryNameTextEdit.text.toString()) val newCategory = Category(0, categoryNameTextEdit.text.toString())
newCategory.id = DAO.addCategory(newCategory).toInt() newCategory.id = DAO.addCategory(newCategory).toInt()
net.uploadCategory(currentGroup, newCategory) if (currentGroup > 0) net.uploadCategory(currentGroup, newCategory)
} else { // Updating existing category } else { // Updating existing category
category.name = categoryNameTextEdit.text.toString() category.name = categoryNameTextEdit.text.toString()
DAO.updateCategory(category) DAO.updateCategory(category)
net.updateCategory(currentGroup, category) if (currentGroup > 0) net.updateCategory(currentGroup, category)
} }
finish() finish()

View File

@ -177,7 +177,7 @@ class AddProductActivity : AppCompatActivity() {
return@setOnClickListener return@setOnClickListener
} }
val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "")!! == "") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt() val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt()
var response: Response? = null var response: Response? = null
@ -200,8 +200,11 @@ class AddProductActivity : AppCompatActivity() {
val today = SimpleDateFormat("dd.MM.yyyy").format(Calendar.getInstance().time).split(".") val today = SimpleDateFormat("dd.MM.yyyy").format(Calendar.getInstance().time).split(".")
dateOfProductionDatePicker.updateDate(today[2].toInt(), today[1].toInt(), today[0].toInt()) dateOfProductionDatePicker.updateDate(today[2].toInt(), today[1].toInt() - 1, today[0].toInt())
expiryDatePicker.updateDate(today[2].toInt(), today[1].toInt(), today[0].toInt()) expiryDatePicker.updateDate(today[2].toInt(), today[1].toInt() - 1, today[0].toInt())
product!!.dateOfProduction = SimpleDateFormat("dd.MM.yyyy").parse("${today[0]}.${today[1]}.${today[2]}")!!.time / 1000
} }
private val intentLauncher = private val intentLauncher =

View File

@ -4,6 +4,7 @@ import android.app.Activity
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
@ -17,7 +18,6 @@ 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
@ -38,9 +38,10 @@ import java.io.File
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
lateinit var adapter: ViewPagerAdapter private lateinit var adapter: ViewPagerAdapter
var drawerLayout: DrawerLayout? = null private var drawerLayout: DrawerLayout? = null
var actionBarDrawerToggle: ActionBarDrawerToggle? = null private var actionBarDrawerToggle: ActionBarDrawerToggle? = null
private lateinit var ws: WebSocketClient
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -115,7 +116,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
noInternetConnectionAvailableNotification(this) noInternetConnectionAvailableNotification(this)
} else if (currentGroup != "offline" && net.serverIsAvailable(this)) { } else if (currentGroup != "offline" && net.serverIsAvailable(this)) {
synchronize() synchronize()
val ws = WebSocketClient(this, sharedPreferences.getString("server", "")!!) ws = WebSocketClient(this, sharedPreferences.getString("server", "")!!)
val token = sharedPreferences.getString("token", "")!! val token = sharedPreferences.getString("token", "")!!
ws.connect(token, currentGroup) ws.connect(token, currentGroup)
} }
@ -395,13 +396,19 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
fun updateAll() { fun updateAll() {
val storageFragment: StorageFragment = adapter.getItem(0) as StorageFragment runOnUiThread {
val shelfFragment: ShelfFragment = adapter.getItem(1) as ShelfFragment try {
val categoriesFragment: CategoriesFragment = adapter.getItem(2) as CategoriesFragment val storageFragment: StorageFragment = adapter.getItem(0) as StorageFragment
val shelfFragment: ShelfFragment = adapter.getItem(1) as ShelfFragment
val categoriesFragment: CategoriesFragment = adapter.getItem(2) as CategoriesFragment
storageFragment.updateContent() storageFragment.updateContent()
shelfFragment.updateContent() shelfFragment.updateContent()
categoriesFragment.updateContent() categoriesFragment.updateContent()
} catch (e:Exception) {
Log.e("BSFE/MainActivity", e.message!!)
}
}
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {

View File

@ -163,7 +163,7 @@ class AbstractProductDAO(private val dbHelper: DBStorageController) {
with(cursor) { with(cursor) {
while (moveToNext()) { while (moveToNext()) {
val id = getInt(getColumnIndexOrThrow(android.provider.BaseColumns._ID)) val id = getInt(getColumnIndexOrThrow(BaseColumns._ID))
val productName = val productName =
getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME)) getString(getColumnIndexOrThrow(AbstractProductContract.AbstractProductEntry.PRODUCT_NAME))
val imageFilename = val imageFilename =