From a0d96da9e410c15aa0b6853da44205aaa02d31e8 Mon Sep 17 00:00:00 2001 From: leca Date: Tue, 29 Oct 2024 03:28:26 +0300 Subject: [PATCH] working on network and UI --- app/build.gradle.kts | 5 +- app/src/main/AndroidManifest.xml | 10 +- .../foxarmy/barcodescannerforemployees/Net.kt | 127 ++++++++++++++++++ .../barcodescannerforemployees/Requester.kt | 40 ------ .../activities/AddAbstractProductActivity.kt | 34 +++-- .../activities/LoginActivity.kt | 76 +++++++++++ .../activities/MainActivity.kt | 1 + .../activities/NavigatorActivity.kt | 38 ++++++ app/src/main/res/layout/activity_login.xml | 56 ++++++++ app/src/main/res/layout/activity_main.xml | 105 ++++++++------- app/src/main/res/menu/drawer_menu.xml | 16 +++ app/src/main/res/menu/menu_main.xml | 2 +- app/src/main/res/values-ru/strings.xml | 16 ++- app/src/main/res/values/strings.xml | 12 +- gradle/libs.versions.toml | 6 + 15 files changed, 442 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/Net.kt delete mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/Requester.kt create mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/LoginActivity.kt create mode 100644 app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/NavigatorActivity.kt create mode 100644 app/src/main/res/layout/activity_login.xml create mode 100644 app/src/main/res/menu/drawer_menu.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4ee9fda..d84d506 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -50,13 +50,16 @@ dependencies { implementation(libs.androidx.activity) implementation(libs.androidx.legacy.support.v4) implementation(libs.androidx.fragment) + implementation(libs.androidx.material3.android) testImplementation(libs.junit) implementation(libs.volley) androidTestImplementation(libs.androidx.junit) implementation(libs.zxing.android.embedded) implementation("com.google.zxing:core:3.4.1") androidTestImplementation(libs.androidx.espresso.core) - + implementation("com.google.android.material:material:1.3.0-alpha03") + implementation(libs.androidx.security.crypto) + implementation(libs.okhttp) // Barcode scanning API implementation (libs.barcode.scanning) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9bd8fd7..cbd0d10 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,6 +48,14 @@ android:name=".activities.FindBarcodelessAbstractProduct" android:exported="false" android:theme="@style/Theme.BarcodeScannerForEmployees"/> + + diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/Net.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/Net.kt new file mode 100644 index 0000000..b14b377 --- /dev/null +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/Net.kt @@ -0,0 +1,127 @@ +package org.foxarmy.barcodescannerforemployees + +import android.util.Log +import okhttp3.* +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.asRequestBody +import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct +import java.io.File +import kotlin.concurrent.thread + +class Net { + + fun requestProductFromOnlineDB(barcode: String): String { + var response = "" + thread { + val url = "https://ean-online.ru/match.php" + + val client = OkHttpClient() + + val formBody = FormBody.Builder() + formBody.add("barcode", barcode) + val body = formBody.build() + val request = Request.Builder() + .url(url) + .post(body) + .addHeader("referer", "https://ean-online.ru") + .build() + + response = client.newCall(request).execute().body!!.string() + }.join() + + return if (response == "") { + "Not found 404" + } else { + response + } + } + + fun registerAccount(server: String, username: String, password: String): String { + var token = "" + lateinit var response: Response + thread { + val client = OkHttpClient() + + val formBody = FormBody.Builder() + formBody.add("username", username) + formBody.add("password", password) + val body = formBody.build() + + val request = Request.Builder() + .url("https://$server/api/user/register") + .post(body) + .addHeader("content-type", "application/x-www-form-urlencoded") + .build() + + response = client.newCall(request).execute() + }.join() + + return when (response.code) { + 200 -> { + login(server, username, password) + } + 400 -> { + "Such username exists" + } + else -> { + "Unknown error" + } + } + } + + fun login(server: String, username: String, password: String): String { + lateinit var response: Response + thread { + val client = OkHttpClient() + + val formBody = FormBody.Builder() + formBody.add("username", username) + formBody.add("password", password) + val body = formBody.build() + + val requestLogin = Request.Builder() + .url("https://$server/api/user/login") + .post(body) + .addHeader("content-type", "application/x-www-form-urlencoded") + .build() + response = client.newCall(requestLogin).execute() + }.join() + + return response.body!!.string() + } + + fun uploadAbstractProduct(server: String, groupId: Int, abstractProduct: AbstractProduct, imageFile: File, token: String): String { + lateinit var response: Response + + thread { + val client = OkHttpClient() + + val body = MultipartBody.Builder() + body.setType("multipart/form-data".toMediaType()) + body.addFormDataPart("file", imageFile.name, imageFile.asRequestBody("image/png".toMediaTypeOrNull())) + body.addFormDataPart("groupId", groupId.toString()) + body.addFormDataPart("localId", abstractProduct.id.toString()) + body.addFormDataPart("barcode", abstractProduct.barcode) + body.addFormDataPart("name", abstractProduct.name) + body.addFormDataPart("net_weight", abstractProduct.netWeight.toString()) + body.addFormDataPart("image_filename", abstractProduct.imageHash) + body.addFormDataPart("category", abstractProduct.category.toString()) + body.addFormDataPart("unit", abstractProduct.unit.toString()) + + val requestBody = body.build() + + val request = Request.Builder() + .url("https://$server/api/abstractproduct/create") + .post(requestBody) + .addHeader("Authorization", "Bearer $token") + .build() + + response = client.newCall(request).execute() + }.join() + + val responseText = response.body!!.string() + + return responseText + } +} \ No newline at end of file diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/Requester.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/Requester.kt deleted file mode 100644 index 1acace2..0000000 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/Requester.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.foxarmy.barcodescannerforemployees - -import android.content.Context -import android.widget.Toast -import com.android.volley.toolbox.StringRequest -import com.android.volley.toolbox.Volley - -class Requester constructor(var siteName: String, var endpoint: String) { - var response = "" - - fun request(context: Context, barcode: String) { - val url = "${siteName}/${endpoint}" - - val volleyQueue = Volley.newRequestQueue(context) - val stringRequest = object: StringRequest( - Method.POST, url, { resp -> - run { - response = - if (resp == "") { - "Not found 404" - } else { - resp - } - } - }, - { - Toast.makeText(context, "Cannot make request", Toast.LENGTH_LONG).show() - } - ) { - override fun getHeaders(): Map { - return mapOf("referer" to "$siteName/") - } - - public override fun getParams(): MutableMap { - return mutableMapOf("barcode" to barcode) - } - } - volleyQueue.add(stringRequest) - } -} diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddAbstractProductActivity.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddAbstractProductActivity.kt index c27e90f..28924b9 100644 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddAbstractProductActivity.kt +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/AddAbstractProductActivity.kt @@ -14,15 +14,19 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.widget.addTextChangedListener +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKeys import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanIntentResult import com.journeyapps.barcodescanner.ScanOptions +import okhttp3.internal.http.hasBody import org.foxarmy.barcodescannerforemployees.* import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct import java.io.File import java.nio.file.Files import java.nio.file.StandardCopyOption import kotlin.concurrent.thread +import kotlin.math.abs class AddAbstractProductActivity : AppCompatActivity() { private lateinit var imageView: ImageView @@ -147,7 +151,21 @@ class AddAbstractProductActivity : AppCompatActivity() { arrayOf(abstractProduct!!.id.toString()) ) } else if (action == "new" || action == "new_from_barcode"){ - db.insert(AbstractProductContract.AbstractProductEntry.TABLE_NAME, null, values) + val id = db.insert(AbstractProductContract.AbstractProductEntry.TABLE_NAME, null, values) + val n = Net() + val abstractProduct = AbstractProduct(id.toInt(), barcode, productName, netWeight.toString().toDouble(), pictureFile.nameWithoutExtension, categorySpinner.selectedItemPosition, unitTypeSpinner.selectedItemPosition) + val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) + + val sharedPreferences = EncryptedSharedPreferences.create( + // passing a file name to share a preferences + "sensitive", + masterKeyAlias, + applicationContext, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + val token = sharedPreferences.getString("token", "") + val response = n.uploadAbstractProduct("bsfe.foxarmy.org", 1, abstractProduct, File(pictureFile.absolutePath), token!!); } finish() @@ -165,8 +183,10 @@ class AddAbstractProductActivity : AppCompatActivity() { fun performRequest(barcode: String) { barcodeText.setText(this.barcode) - val requester = Requester("https://ean-online.ru", "match.php") - requester.request(this, barcode) + val net = Net(); + val result = net.requestProductFromOnlineDB(barcode) + Log.d("QWERTYUIOP", "Result of request: $result") + var abstractProduct: AbstractProduct if (DBStorageController(this).findAbstractProductByBarcode( @@ -194,10 +214,7 @@ class AddAbstractProductActivity : AppCompatActivity() { } thread { - // Я сам в ахуях какой это костыль, пока хз как фиксить, потом придумаю :)) - while (requester.response == "") { - } - if (requester.response == "Not found 404") { + if (result == "Not found 404") { runOnUiThread { Toast.makeText(this, getString(R.string.no_product_in_online_database), Toast.LENGTH_LONG) .show() @@ -207,8 +224,7 @@ class AddAbstractProductActivity : AppCompatActivity() { return@thread } - abstractProduct = Parser().parse(requester.response) - requester.response = "" + abstractProduct = Parser().parse(result) runOnUiThread { productNameText.text = abstractProduct.name netWeightText.text = abstractProduct.netWeight.toString() diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/LoginActivity.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/LoginActivity.kt new file mode 100644 index 0000000..37f9d43 --- /dev/null +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/LoginActivity.kt @@ -0,0 +1,76 @@ +package org.foxarmy.barcodescannerforemployees.activities + +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKeys +import org.foxarmy.barcodescannerforemployees.Net +import org.foxarmy.barcodescannerforemployees.R +import org.foxarmy.barcodescannerforemployees.databinding.ActivityLoginBinding + +class LoginActivity : AppCompatActivity() { + + private lateinit var binding: ActivityLoginBinding; + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityLoginBinding.inflate(layoutInflater); + setContentView(binding.root) + + val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) + + val sharedPreferences = EncryptedSharedPreferences.create( + // passing a file name to share a preferences + "sensitive", + masterKeyAlias, + applicationContext, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + binding.loginButton.setOnClickListener { + val server = binding.serverTextEdit.text.toString() + val username = binding.usernameTextEdit.text.toString() + val password = binding.passwordTextEdit.text.toString() + + val n = Net() + + val response = n.login(server, username, password) + + if (response == "Wrong password") { + Toast.makeText(this, getString(R.string.wrong_password), Toast.LENGTH_SHORT).show() + } else { + sharedPreferences.edit().putString("token", response).apply() + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + finish() + } + } + + binding.registerButton.setOnClickListener { + val server = binding.serverTextEdit.text.toString() + val username = binding.usernameTextEdit.text.toString() + val password = binding.passwordTextEdit.text.toString() + + val n = Net() + + val response = n.registerAccount(server, username, password); + + if (response == "Such username exists") { + Toast.makeText(this, getString(R.string.username_already_exists), Toast.LENGTH_SHORT).show(); + } else { + sharedPreferences.edit().putString("token", response).apply() + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + finish() + } + + } + + + } + +} \ No newline at end of file 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 900a1b9..0f47b05 100644 --- a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/MainActivity.kt +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/MainActivity.kt @@ -69,6 +69,7 @@ class MainActivity : AppCompatActivity() { extras.putParcelable("product", null) addProductIntent.putExtras(extras) ContextCompat.startActivity(this, addProductIntent, extras) + } } } diff --git a/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/NavigatorActivity.kt b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/NavigatorActivity.kt new file mode 100644 index 0000000..871244f --- /dev/null +++ b/app/src/main/java/org/foxarmy/barcodescannerforemployees/activities/NavigatorActivity.kt @@ -0,0 +1,38 @@ +package org.foxarmy.barcodescannerforemployees.activities + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKeys + +class NavigatorActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + var intent = Intent(this, LoginActivity::class.java) + + if (isAuthenticated()) { + intent = Intent(this, MainActivity::class.java); + } + + startActivity(intent); + finish(); + } + + fun isAuthenticated(): Boolean { + val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) + + val sharedPreferences = EncryptedSharedPreferences.create( + // passing a file name to share a preferences + "sensitive", + masterKeyAlias, + applicationContext, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + val jwt = sharedPreferences.getString("token", "") + return jwt != "" + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..48f8829 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,56 @@ + + + +