Compare commits

...

11 Commits

Author SHA1 Message Date
leca edd9c666ac bugfixes and translation 2024-11-20 22:00:32 +03:00
leca 6bbf436f45 Мимо собственного кода
Я без шуток не хожу -
То комменты не коммичу,
То весь класс перепишу

Кволити - не кволити,
Хуй меня уволите
2024-11-20 15:42:40 +03:00
leca a860e4d59f added functionality to group rename & leave buttons, added ability to transfer ownership of a group 2024-11-18 14:20:34 +03:00
leca 37e52b3f2f Fixed category filter 2024-11-17 22:37:54 +03:00
leca f18d7ad1e0 performance fixes 2024-11-17 19:26:25 +03:00
leca 1d7050c687 some ui improvements 2024-11-17 17:56:30 +03:00
leca 153506fc83 fixed some layout overflows 2024-11-16 15:40:08 +03:00
leca abe2a850d6 logging out on user data changes 2024-11-15 11:36:44 +03:00
leca 58a7ea7357 added ability to log out 2024-11-14 13:38:33 +03:00
leca 2ebcfff51a fixed using network in offline mode 2024-11-14 13:32:56 +03:00
leca 2676d8083e fixed net weight parsing 2024-11-14 13:11:13 +03:00
38 changed files with 1360 additions and 881 deletions

View File

@ -0,0 +1,14 @@
package org.foxarmy.barcodescannerforemployees
import android.app.Dialog
import android.os.Bundle
import androidx.fragment.app.DialogFragment
class LoadingDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState)
}
}

View File

@ -1,6 +1,9 @@
package org.foxarmy.barcodescannerforemployees package org.foxarmy.barcodescannerforemployees
import android.content.Context import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.* import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -11,19 +14,19 @@ import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import kotlin.concurrent.thread import java.util.concurrent.TimeUnit
class Net { class Net {
var language = "en-US" var language = "en-US"
var server = "bsfe.foxarmy.org" var server = "bsfe.foxarmy.org"
var token = "" var token = ""
fun serverIsAvailable(context: Context): Boolean { fun serverIsAvailable(context: Context, callback: (Boolean) -> Unit) {
if (!isInternetConnectionAvailable(context)) { if (!isInternetConnectionAvailable(context)) {
return false callback(false)
} }
CoroutineScope(Dispatchers.IO).launch {
var flag = false var flag = false
thread {
val client = OkHttpClient(); val client = OkHttpClient();
val request = Request.Builder() val request = Request.Builder()
.url("https://$server/status") .url("https://$server/status")
@ -36,13 +39,13 @@ class Net {
} catch (e: Exception) { } catch (e: Exception) {
flag = false; flag = false;
} }
}.join() callback(flag)
return flag }
} }
fun requestProductFromOnlineDB(barcode: String): String { fun requestProductFromOnlineDB(barcode: String, callback: (Response) -> Unit) {
var response = "" CoroutineScope(Dispatchers.IO).launch {
thread { lateinit var response: Response
val url = "https://ean-online.ru/match.php" val url = "https://ean-online.ru/match.php"
val client = OkHttpClient() val client = OkHttpClient()
@ -58,19 +61,15 @@ class Net {
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
response = client.newCall(request).execute().body!!.string() response = client.newCall(request).execute()
}.join() callback(response)
return if (response == "") {
"Not found 404"
} else {
response
} }
} }
fun registerAccount(username: String, password: String): Response { fun registerAccount(username: String, password: String, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -86,14 +85,13 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun login(username: String, password: String): Response { fun login(username: String, password: String, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -107,15 +105,19 @@ class Net {
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
response = client.newCall(requestLogin).execute() response = client.newCall(requestLogin).execute()
}.join() callback(response)
}
return response
} }
fun uploadAbstractProduct(groupId: Int, abstractProduct: AbstractProduct, imageFile: File): Response { fun uploadAbstractProduct(
groupId: Int,
abstractProduct: AbstractProduct,
imageFile: File,
callback: (Response) -> Unit,
) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = MultipartBody.Builder() val body = MultipartBody.Builder()
@ -138,16 +140,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun changeUsername(newUsername: String): Response { fun changeUsername(newUsername: String, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -162,16 +162,14 @@ class Net {
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun changePassword(newPassword: String): Response { fun changePassword(newPassword: String, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -186,15 +184,12 @@ class Net {
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun createGroup(name: String, password: String): Response { fun createGroup(name: String, password: String, callback: (Response) -> Unit) {
lateinit var response: Response CoroutineScope(Dispatchers.IO).launch {
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -207,19 +202,19 @@ class Net {
.addHeader("Authorization", "Bearer $token") .addHeader("Authorization", "Bearer $token")
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
response = client.newCall(request).execute() val response = client.newCall(request).execute()
}.join()
changeGroupPassword(name, password, { responseFromPassword ->
callback(response)
})
changeGroupPassword(name, password) }
return response
} }
fun joinGroup(id: Int, password: String): Response { fun joinGroup(id: Int, password: String, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -233,18 +228,15 @@ class Net {
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun changeGroupPassword(name: String, password: String): Response { fun changeGroupPassword(name: String, password: String, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread { getGroupId(name, { groupId ->
val groupId = getGroupId(name);
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -259,15 +251,15 @@ class Net {
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
});
return response }
} }
fun getGroupId(name: String): String { fun getGroupId(name: String, callback: (String) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -277,17 +269,14 @@ class Net {
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response.body!!.string())
}
val responseText = response.body!!.string()
return responseText
} }
fun getGroupName(id: Int): String { fun getGroupName(id: Int, callback: (String) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -297,17 +286,14 @@ class Net {
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response.body!!.string())
}
val responseText = response.body!!.string()
return responseText
} }
fun getUsersInGroup(groupId: Int): Response { fun getUsersInGroup(groupId: Int, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -318,15 +304,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun getMyGroups(): Response { fun getMyGroups(callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -337,15 +322,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun getUsernameById(userId: Int): Response { fun getUsernameById(userId: Int, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -356,15 +340,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun getGroupAdminId(groupId: Int): Response { fun getGroupAdminId(groupId: Int, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -375,15 +358,19 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun updateAbstractProduct(groupId: Int, abstractProduct: AbstractProduct, imageFile: File): Response { fun updateAbstractProduct(
groupId: Int,
abstractProduct: AbstractProduct,
imageFile: File,
callback: (Response) -> Unit,
) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = MultipartBody.Builder() val body = MultipartBody.Builder()
@ -406,15 +393,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun uploadCategory(groupId: Int, category: Category): Response { fun uploadCategory(groupId: Int, category: Category, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -431,15 +417,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun updateCategory(groupId: Int, category: Category): Response { fun updateCategory(groupId: Int, category: Category, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -456,15 +441,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun uploadProduct(groupId: Int, product: Product): Response { fun uploadProduct(groupId: Int, product: Product, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -484,15 +468,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun updateProduct(groupId: Int, product: Product): Response { fun updateProduct(groupId: Int, product: Product, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -512,15 +495,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun synchronize(groupId: Int): Response { fun synchronize(groupId: Int, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -531,59 +513,24 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun getProduct(groupId: Int, localId: Int): Response { fun downloadImage(url: String, file: File, callback: () -> Unit) {
lateinit var response: Response CoroutineScope(Dispatchers.IO).launch {
val client = OkHttpClient.Builder()
thread { .connectTimeout(30, TimeUnit.SECONDS)
val client = OkHttpClient() .readTimeout(10, TimeUnit.MINUTES)
val request = Request.Builder()
.url("https://$server/api/product/$groupId/$localId")
.get()
.addHeader("Authorization", "Bearer $token")
.addHeader("accept-language", language)
.build() .build()
response = client.newCall(request).execute()
}.join()
return response
}
fun getAbstractProduct(groupId: Int, localId: Int): Response {
lateinit var response: Response
thread {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://$server/api/abstractproduct/$groupId/$localId")
.get()
.addHeader("Authorization", "Bearer $token")
.addHeader("accept-language", language)
.build()
response = client.newCall(request).execute()
}.join()
return response
}
fun downloadImage(url: String, file: File) {
thread {
val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
.url(url) .url(url)
.addHeader("Authorization", "Bearer $token") .addHeader("Authorization", "Bearer $token")
.addHeader("accept-language", language) .addHeader("accept-language", language)
.build() .build()
client.newCall(request).execute().use { response -> val response = client.newCall(request).execute()
if (!response.isSuccessful) throw IOException("Unexpected code $response") if (!response.isSuccessful) throw IOException("Unexpected code $response")
val fos = FileOutputStream(file) val fos = FileOutputStream(file)
@ -593,14 +540,14 @@ class Net {
inputStream.copyTo(fos) inputStream.copyTo(fos)
} }
} }
callback()
} }
}.join()
} }
fun deleteCategory(groupId: Int, localId: Int): Response { fun deleteCategory(groupId: Int, localId: Int, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -611,15 +558,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun deleteAbstractProduct(groupId: Int, localId: Int): Response { fun deleteAbstractProduct(groupId: Int, localId: Int, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -630,15 +576,14 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
return response
} }
fun deleteProduct(groupId: Int, localId: Int): Response { fun deleteProduct(groupId: Int, localId: Int, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response lateinit var response: Response
thread {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
@ -649,8 +594,69 @@ class Net {
.build() .build()
response = client.newCall(request).execute() response = client.newCall(request).execute()
}.join() callback(response)
}
}
return response fun renameGroup(groupId: Int, newName: String, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response
val client = OkHttpClient()
val body = FormBody.Builder()
.add("name", newName)
.build()
val request = Request.Builder()
.url("https://$server/api/group/rename/$groupId")
.post(body)
.addHeader("Authorization", "Bearer $token")
.addHeader("accept-language", language)
.build()
response = client.newCall(request).execute()
callback(response)
}
}
fun transfer_ownership(groupId: Int, userId: Int, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response
val client = OkHttpClient()
val body = FormBody.Builder()
.add("userId", userId.toString())
.build()
val request = Request.Builder()
.url("https://$server/api/group/transferOwnership/$groupId")
.post(body)
.addHeader("Authorization", "Bearer $token")
.addHeader("accept-language", language)
.build()
response = client.newCall(request).execute()
callback(response)
}
}
fun leaveGroup(groupId: Int, callback: (Response) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
lateinit var response: Response
val client = OkHttpClient()
val request = Request.Builder()
.url("https://$server/api/group/leave/$groupId")
.get()
.addHeader("Authorization", "Bearer $token")
.addHeader("accept-language", language)
.build()
response = client.newCall(request).execute()
callback(response)
}
} }
} }

View File

@ -14,8 +14,6 @@ class Parser constructor() {
text = text.replace(found, "") text = text.replace(found, "")
netWeight = stripNetWeight(found) netWeight = stripNetWeight(found)
return Triple(text, netWeight, found) return Triple(text, netWeight, found)
} else {
return Triple(text, 0.0, "")
} }
} }
return Triple("", 0.0, "") return Triple("", 0.0, "")

View File

@ -3,6 +3,8 @@ package org.foxarmy.barcodescannerforemployees
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Matrix import android.graphics.Matrix
@ -17,6 +19,7 @@ import androidx.core.content.FileProvider
import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys import androidx.security.crypto.MasterKeys
import com.google.firebase.components.BuildConfig import com.google.firebase.components.BuildConfig
import org.foxarmy.barcodescannerforemployees.activities.LoginActivity
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
@ -114,36 +117,28 @@ fun calculateProductFreshness(dateOfProduction: Long, dateOfExpiry: Long): Doubl
fun getUnitNameById(context: Context, id: Int): String { fun getUnitNameById(context: Context, id: Int): String {
return when (id) { return when (id) {
0 -> { 0 -> { context.getString(R.string.kilogram) }
context.getString(R.string.kilogram)
}
1 -> { 1 -> { context.getString(R.string.gram) }
context.getString(R.string.gram)
}
2 -> { 2 -> { context.getString(R.string.liter) }
context.getString(R.string.liter)
}
3 -> { 3 -> { context.getString(R.string.milliliter) }
context.getString(R.string.milliliter)
}
4 -> { 4 -> { context.getString(R.string.pieces) }
context.getString(R.string.pieces)
}
else -> { else -> { "" }
""
}
} }
} }
fun parseArray(input: String): IntArray { fun parseIntArray(input: String): IntArray {
return input.trim('[', ']').split(",").map { it.trim().toInt() }.toIntArray() return input.trim('[', ']').split(",").map { it.trim().toInt() }.toIntArray()
} }
fun parseStringList(input: String): List<String> {
return input.trim('[', ']').split(",").map { it.trim() }.toList()
}
fun calculateMd5Hash(file: File): String { fun calculateMd5Hash(file: File): String {
val digest = MessageDigest.getInstance("MD5") val digest = MessageDigest.getInstance("MD5")
val fis = FileInputStream(file) val fis = FileInputStream(file)
@ -193,23 +188,30 @@ fun noInternetConnectionAvailableNotification(context: Context) {
(context as Activity).runOnUiThread { (context as Activity).runOnUiThread {
AlertDialog.Builder(context) AlertDialog.Builder(context)
.setMessage(context.getString(R.string.no_internet_connection)) .setMessage(context.getString(R.string.no_internet_connection))
.setCancelable(false)
.setPositiveButton(R.string.quit) { _, _ -> .setPositiveButton(R.string.quit) { _, _ ->
exitProcess(0) exitProcess(0)
} }
.setNeutralButton(R.string.logout) { _, _ -> .setNeutralButton(R.string.logout) { _, _ ->
val sharedPreferences = EncryptedSharedPreferences.create( val sharedPreferences = getPreferences(context)
sharedPreferences.edit().putString("token", "").apply()
sharedPreferences.edit().putString("server", "").apply()
sharedPreferences.edit().putStringSet("groups", emptySet()).apply()
sharedPreferences.edit().putString("currentGroup", "").apply()
val intent = Intent(context, LoginActivity::class.java)
context.startActivity(intent)
context.finish()
}.show()
}
}
fun getPreferences(context: Context): SharedPreferences {
return EncryptedSharedPreferences.create(
"sensitive", "sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context, context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
) )
sharedPreferences.edit().putString("token", "").apply()
sharedPreferences.edit().putString("server", "").apply()
sharedPreferences.edit().putStringSet("groups", emptySet()).apply()
sharedPreferences.edit().putString("currentGroup", "").apply()
exitProcess(0)
}.show()
}
} }

View File

@ -20,8 +20,13 @@ 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
private lateinit var token: String
private lateinit var currentGroup: String
fun connect(token: String, currentGroup: String) { fun connect(token: String, currentGroup: String) {
this.token = token
this.currentGroup = currentGroup
val client = OkHttpClient.Builder() val client = OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS) .connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.MINUTES) .readTimeout(1, TimeUnit.MINUTES)
@ -70,19 +75,22 @@ class WebSocketClient(private val context: Context, private val server: String)
val pictureFile = val pictureFile =
File(picturesDir, "${data["image_filename"]}.png") File(picturesDir, "${data["image_filename"]}.png")
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${data["local_id"]}" val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${data["local_id"]}"
net.downloadImage(url, pictureFile) net.downloadImage(url, pictureFile, {
abstractProductDAO.addAbstractProduct(newAbstractProduct) abstractProductDAO.addAbstractProduct(newAbstractProduct)
(context as MainActivity).updateAll()
})
} }
"product" -> { "product" -> {
val newProduct = Product.createFromJSON(data) val newProduct = Product.createFromJSON(data)
productDAO.insertNewProduct(newProduct) productDAO.insertNewProduct(newProduct)
(context as MainActivity).updateAll()
} }
"category" -> { "category" -> {
val newCategory = Category.createFromJSON(data) val newCategory = Category.createFromJSON(data)
categoryDAO.addCategory(newCategory) categoryDAO.addCategory(newCategory)
(context as MainActivity).updateAll()
} }
} }
} }
@ -102,17 +110,21 @@ class WebSocketClient(private val context: Context, private val server: String)
val pictureFile = val pictureFile =
File(picturesDir, "${data["image_filename"]}.png") File(picturesDir, "${data["image_filename"]}.png")
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${data["local_id"]}" val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${data["local_id"]}"
net.downloadImage(url, pictureFile) net.downloadImage(url, pictureFile, {
abstractProductDAO.updateAbstractProduct(updatedAbstractProduct) abstractProductDAO.updateAbstractProduct(updatedAbstractProduct)
(context as MainActivity).updateAll()
})
} }
"product" -> { "product" -> {
val updatedProduct = Product.createFromJSON(data) val updatedProduct = Product.createFromJSON(data)
productDAO.updateProduct(updatedProduct) productDAO.updateProduct(updatedProduct)
(context as MainActivity).updateAll()
} }
"category" -> { "category" -> {
val updatedCategory = Category.createFromJSON(data) val updatedCategory = Category.createFromJSON(data)
categoryDAO.updateCategory(updatedCategory) categoryDAO.updateCategory(updatedCategory)
(context as MainActivity).updateAll()
} }
} }
} }
@ -123,23 +135,25 @@ class WebSocketClient(private val context: Context, private val server: String)
when(item) { when(item) {
"abstractproduct" -> { "abstractproduct" -> {
abstractProductDAO.eraseAbstractProduct(id.toInt(), context) abstractProductDAO.eraseAbstractProduct(id.toInt(), context)
(context as MainActivity).updateAll()
} }
"product" -> { "product" -> {
productDAO.eraseProduct(id.toInt()) productDAO.eraseProduct(id.toInt())
(context as MainActivity).updateAll()
} }
"category" -> { "category" -> {
categoryDAO.eraseCategory(id.toInt(), context) categoryDAO.eraseCategory(id.toInt(), context)
}
}
}
}
(context as MainActivity).updateAll() (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") Log.d("QWERTYUIOP", "Closing ws. Reason: $reason")
noInternetConnectionAvailableNotification(context) // noInternetConnectionAvailableNotification(context)
this@WebSocketClient.connect(token, currentGroup)
} }
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {

View File

@ -1,11 +1,11 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityAccountSettingsBinding import org.foxarmy.barcodescannerforemployees.databinding.ActivityAccountSettingsBinding
class AccountSettingsActivity : AppCompatActivity() { class AccountSettingsActivity : AppCompatActivity() {
@ -19,15 +19,7 @@ class AccountSettingsActivity : AppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
val sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
masterKeyAlias,
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val net = Net() val net = Net()
@ -36,13 +28,39 @@ class AccountSettingsActivity : AppCompatActivity() {
net.token = sharedPreferences.getString("token", "")!! net.token = sharedPreferences.getString("token", "")!!
binding.saveUsernameButton.setOnClickListener { binding.saveUsernameButton.setOnClickListener {
val response = net.changeUsername(binding.newUsernameTextEdit.text.toString()) net.changeUsername(binding.newUsernameTextEdit.text.toString(), { response ->
if (response.code == 200) {
runOnUiThread {
Toast.makeText(this, getString(R.string.username_changed), Toast.LENGTH_LONG).show()
val intent = Intent(this, LoginActivity::class.java)
startActivity(intent)
finish()
}
} else {
runOnUiThread {
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show() Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
} }
}
})
}
binding.savePasswordButton.setOnClickListener { binding.savePasswordButton.setOnClickListener {
val response = net.changePassword(binding.newPasswordTextEdit.text.toString()) net.changePassword(binding.newPasswordTextEdit.text.toString(), { response ->
if (response.code == 200) {
runOnUiThread {
Toast.makeText(this, getString(R.string.password_changed), Toast.LENGTH_LONG).show()
val intent = Intent(this, LoginActivity::class.java)
startActivity(intent)
finish()
}
} else {
runOnUiThread {
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show() Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
} }
} }
})
}
}
} }

View File

@ -1,5 +1,6 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.app.ProgressDialog
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
@ -17,8 +18,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanIntentResult import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions import com.journeyapps.barcodescanner.ScanOptions
@ -32,7 +31,6 @@ import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
import kotlin.concurrent.thread
class AddAbstractProductActivity : AppCompatActivity() { class AddAbstractProductActivity : AppCompatActivity() {
private lateinit var imageView: ImageView private lateinit var imageView: ImageView
@ -60,18 +58,19 @@ class AddAbstractProductActivity : AppCompatActivity() {
private lateinit var DAO: AbstractProductDAO private lateinit var DAO: AbstractProductDAO
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var loadingDialog: ProgressDialog
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_add_abstract_product) setContentView(R.layout.fragment_add_abstract_product)
sharedPreferences = EncryptedSharedPreferences.create( loadingDialog = ProgressDialog(this)
"sensitive", loadingDialog.setMessage(getString(R.string.loading_please_wait))
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), loadingDialog.setCancelable(false)
applicationContext, loadingDialog.setTitle(getString(R.string.loading))
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM sharedPreferences = getPreferences(this)
)
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!) val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!)
DAO = AbstractProductDAO(dbHelper) DAO = AbstractProductDAO(dbHelper)
@ -128,6 +127,9 @@ class AddAbstractProductActivity : AppCompatActivity() {
netWeightText.text = abstractProduct!!.netWeight.toString() netWeightText.text = abstractProduct!!.netWeight.toString()
categorySpinner.setSelection(abstractProduct!!.category - 1) categorySpinner.setSelection(abstractProduct!!.category - 1)
unitTypeSpinner.setSelection(abstractProduct!!.unit) unitTypeSpinner.setSelection(abstractProduct!!.unit)
if (abstractProduct!!.barcode == "" || abstractProduct!!.barcode == " ") {
noBarcodeCheckBox.isChecked = true
}
} }
saveButton.setOnClickListener { saveButton.setOnClickListener {
@ -152,15 +154,23 @@ class AddAbstractProductActivity : AppCompatActivity() {
return@setOnClickListener return@setOnClickListener
} }
loadingDialog.show()
val currentGroup: Int
val currentGroupString = sharedPreferences.getString("currentGroup", "offline")!!
val net = Net() val net = Net()
if (currentGroupString != "offline") {
currentGroup = currentGroupString.toInt()
net.token = sharedPreferences.getString("token", "")!! net.token = sharedPreferences.getString("token", "")!!
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: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt() } else {
currentGroup = 0
lateinit var response: Response }
abstractProduct = AbstractProduct( abstractProduct = AbstractProduct(
if (abstractProduct == null) 0 else abstractProduct!!.id, if (abstractProduct == null) 0 else abstractProduct!!.id,
@ -171,18 +181,17 @@ class AddAbstractProductActivity : AppCompatActivity() {
categorySpinner.selectedItemPosition + 1, categorySpinner.selectedItemPosition + 1,
unitTypeSpinner.selectedItemPosition unitTypeSpinner.selectedItemPosition
) )
val pictureFile = File(File(filesDir, "pictures"), "${abstractProduct!!.imageHash}.png") val pictureFile = File(File(filesDir, "pictures"), "${abstractProduct!!.imageHash}.png")
if (action == "update") { if (action == "update") {
DAO.updateAbstractProduct(abstractProduct!!) DAO.updateAbstractProduct(abstractProduct!!)
response = net.updateAbstractProduct(currentGroup, abstractProduct!!, pictureFile) if (currentGroup > 0) net.updateAbstractProduct(currentGroup, abstractProduct!!, pictureFile, this::notifyUserAndExit)
} 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, abstractProduct!!, pictureFile); if (currentGroup > 0)
net.uploadAbstractProduct(currentGroup, abstractProduct!!, pictureFile, this::notifyUserAndExit)
} }
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
finish()
} }
takePictureButton.setOnClickListener { takePictureButton.setOnClickListener {
@ -195,10 +204,16 @@ class AddAbstractProductActivity : AppCompatActivity() {
} }
} }
fun notifyUserAndExit(response: Response) {
runOnUiThread {
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
loadingDialog.dismiss()
finish()
}
}
fun performRequest(barcode: String) { fun performRequest(barcode: String) {
barcodeText.setText(this.barcode) barcodeText.setText(this.barcode)
val net = Net();
val result = net.requestProductFromOnlineDB(barcode)
var abstractProduct: AbstractProduct var abstractProduct: AbstractProduct
@ -220,24 +235,25 @@ class AddAbstractProductActivity : AppCompatActivity() {
}.show() }.show()
} }
thread { val net = Net();
if (result == "Not found 404") { net.requestProductFromOnlineDB(barcode, {response ->
if (response.code == 404) {
runOnUiThread { runOnUiThread {
Toast.makeText(this, getString(R.string.no_product_in_online_database), Toast.LENGTH_LONG) Toast.makeText(this, getString(R.string.no_product_in_online_database), Toast.LENGTH_LONG)
.show() .show()
productNameText.setText("") productNameText.setText("")
netWeightText.setText("") netWeightText.setText("")
} }
return@thread return@requestProductFromOnlineDB
} }
abstractProduct = Parser().parse(result) abstractProduct = Parser().parse(response.body!!.string())
runOnUiThread { runOnUiThread {
productNameText.text = abstractProduct.name productNameText.text = abstractProduct.name
netWeightText.text = abstractProduct.netWeight.toString() netWeightText.text = abstractProduct.netWeight.toString()
unitTypeSpinner.setSelection(abstractProduct.unit) unitTypeSpinner.setSelection(abstractProduct.unit)
} }
} })
} }
private fun fillupUnitsSpinner() { private fun fillupUnitsSpinner() {
@ -257,7 +273,8 @@ class AddAbstractProductActivity : AppCompatActivity() {
fun fillupCategorySpinner() { fun fillupCategorySpinner() {
val categoriesDAO = CategoryDAO(DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!)) val categoriesDAO =
CategoryDAO(DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!))
val categories = categoriesDAO.getAllCategories().map { category -> category.name } val categories = categoriesDAO.getAllCategories().map { category -> category.name }

View File

@ -1,13 +1,13 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.app.Activity import android.app.Activity
import android.app.ProgressDialog
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.Toast import android.widget.Toast
import androidx.security.crypto.EncryptedSharedPreferences import okhttp3.Response
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
@ -18,19 +18,19 @@ class AddCategoryActivity : Activity() {
private lateinit var DAO: CategoryDAO private lateinit var DAO: CategoryDAO
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var loadingDialog: ProgressDialog
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_category) setContentView(R.layout.activity_add_category)
sharedPreferences = EncryptedSharedPreferences.create( loadingDialog = ProgressDialog(this)
"sensitive", loadingDialog.setMessage(getString(R.string.loading_please_wait))
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), loadingDialog.setCancelable(false)
applicationContext, loadingDialog.setTitle(getString(R.string.loading))
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
)
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!) val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!)
DAO = CategoryDAO(dbHelper) DAO = CategoryDAO(dbHelper)
@ -48,25 +48,35 @@ 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: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.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() == "") {
Toast.makeText(this, getString(R.string.category_name_required), Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.category_name_required), Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
loadingDialog.show()
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()
if (currentGroup > 0) net.uploadCategory(currentGroup, newCategory) if (currentGroup > 0) net.uploadCategory(currentGroup, newCategory, this::notifyUserAndExit)
} else { // Updating existing category } else { // Updating existing category
category.name = categoryNameTextEdit.text.toString() category.name = categoryNameTextEdit.text.toString()
DAO.updateCategory(category) DAO.updateCategory(category)
if (currentGroup > 0) net.updateCategory(currentGroup, category) if (currentGroup > 0) net.updateCategory(currentGroup, category, this::notifyUserAndExit)
} }
}
}
fun notifyUserAndExit(response: Response) {
runOnUiThread {
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_SHORT).show()
}
loadingDialog.dismiss()
finish() finish()
} }
} }
}

View File

@ -1,6 +1,7 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.app.Activity import android.app.Activity
import android.app.ProgressDialog
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
@ -13,8 +14,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanIntentResult import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions import com.journeyapps.barcodescanner.ScanOptions
@ -56,18 +55,19 @@ class AddProductActivity : AppCompatActivity() {
private lateinit var abstractProductDAO: AbstractProductDAO private lateinit var abstractProductDAO: AbstractProductDAO
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var loadingDialog: ProgressDialog
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_add_product) setContentView(R.layout.fragment_add_product)
sharedPreferences = EncryptedSharedPreferences.create( loadingDialog = ProgressDialog(this)
"sensitive", loadingDialog.setMessage(getString(R.string.loading_please_wait))
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), loadingDialog.setCancelable(false)
applicationContext, loadingDialog.setTitle(getString(R.string.loading))
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
)
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!) val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "offline")!!)
productDAO = ProductDAO(dbHelper) productDAO = ProductDAO(dbHelper)
@ -177,23 +177,17 @@ class AddProductActivity : AppCompatActivity() {
return@setOnClickListener return@setOnClickListener
} }
val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt() loadingDialog.show()
var response: Response? = null val currentGroup: Int = if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") 0 else sharedPreferences.getString("currentGroup", "")!!.toInt()
if (updatingExistentProduct) { if (updatingExistentProduct) {
productDAO.updateProduct(product!!) productDAO.updateProduct(product!!)
if (currentGroup > 0) response = net.updateProduct(currentGroup, product!!) if (currentGroup > 0) net.updateProduct(currentGroup, product!!, this::notifyUserAndExit)
} else { } else {
product!!.id = productDAO.insertNewProduct(product!!).toInt() product!!.id = productDAO.insertNewProduct(product!!).toInt()
if (currentGroup > 0) response = net.uploadProduct(currentGroup, product!!) if (currentGroup > 0) net.uploadProduct(currentGroup, product!!, this::notifyUserAndExit)
} }
if (response != null) {
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
}
finish()
} }
update() update()
@ -207,6 +201,14 @@ class AddProductActivity : AppCompatActivity() {
} }
fun notifyUserAndExit(response: Response) {
runOnUiThread {
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
loadingDialog.dismiss()
finish()
}
}
private val intentLauncher = private val intentLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) { if (result.resultCode == Activity.RESULT_OK) {

View File

@ -7,8 +7,6 @@ import android.os.Bundle
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.database.DBStorageController import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.database.ProductDAO import org.foxarmy.barcodescannerforemployees.database.ProductDAO
@ -26,13 +24,7 @@ class ExpiryCalendarActivity : AppCompatActivity() {
setContentView(R.layout.fragment_expiry_dates) setContentView(R.layout.fragment_expiry_dates)
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!) val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "database")!!)
productDAO = ProductDAO(dbHelper) productDAO = ProductDAO(dbHelper)

View File

@ -18,7 +18,7 @@ class FindBarcodelessAbstractProduct() : AppCompatActivity() {
binding = ActivityFindBarcodelessAbstractProductBinding.inflate(layoutInflater) binding = ActivityFindBarcodelessAbstractProductBinding.inflate(layoutInflater)
val ft = supportFragmentManager.beginTransaction() val ft = supportFragmentManager.beginTransaction()
val fragment = StorageFragment.newInstance("barcodeless", arrayOf("", " ")) val fragment = StorageFragment.newInstance("barcodeless", arrayOf("", " ", " "))
ft.replace(R.id.content, fragment) ft.replace(R.id.content, fragment)
ft.commit() ft.commit()

View File

@ -15,10 +15,7 @@ import java.io.File
class FullscreenActivity : Activity() { class FullscreenActivity : Activity() {
private lateinit var binding: ActivityFullscreenBinding
private lateinit var fullscreenImageView: ImageView private lateinit var fullscreenImageView: ImageView
private lateinit var fullscreenContentControls: LinearLayout
private val hideHandler = Handler(Looper.myLooper()!!)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@ -1,12 +1,12 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.app.ProgressDialog
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityGroupBinding import org.foxarmy.barcodescannerforemployees.databinding.ActivityGroupBinding
import org.foxarmy.barcodescannerforemployees.noInternetConnectionAvailableNotification import org.foxarmy.barcodescannerforemployees.noInternetConnectionAvailableNotification
@ -20,13 +20,12 @@ class GroupActivity : AppCompatActivity() {
binding = ActivityGroupBinding.inflate(layoutInflater) binding = ActivityGroupBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
val sharedPreferences = EncryptedSharedPreferences.create( val loadingDialog = ProgressDialog(this)
"sensitive", loadingDialog.setMessage(getString(R.string.loading_please_wait))
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), loadingDialog.setCancelable(false)
applicationContext, loadingDialog.setTitle(getString(R.string.loading))
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM val sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
)
binding.createGroupButton.setOnClickListener { binding.createGroupButton.setOnClickListener {
val groupName = binding.groupNameTextEdit.text.toString() val groupName = binding.groupNameTextEdit.text.toString()
@ -36,10 +35,14 @@ class GroupActivity : AppCompatActivity() {
net.server = sharedPreferences.getString("server", "")!! net.server = sharedPreferences.getString("server", "")!!
net.token = sharedPreferences.getString("token", "")!! net.token = sharedPreferences.getString("token", "")!!
if (!net.serverIsAvailable(this)) { net.serverIsAvailable(this, { isServerAvailable ->
if (!isServerAvailable) {
noInternetConnectionAvailableNotification(this) noInternetConnectionAvailableNotification(this)
} else { } else {
val response = net.createGroup(groupName, groupPassword) runOnUiThread {
loadingDialog.show()
}
net.createGroup(groupName, groupPassword, { response ->
val responseText = response.body!!.string() val responseText = response.body!!.string()
if (response.code == 200) { if (response.code == 200) {
@ -47,13 +50,21 @@ class GroupActivity : AppCompatActivity() {
currentGroups!!.add(responseText) currentGroups!!.add(responseText)
sharedPreferences.edit().putStringSet("groups", currentGroups).apply() sharedPreferences.edit().putStringSet("groups", currentGroups).apply()
sharedPreferences.edit().putString("currentGroup", responseText).apply() sharedPreferences.edit().putString("currentGroup", responseText).apply()
runOnUiThread {
val intent = Intent(this, MainActivity::class.java) val intent = Intent(this, MainActivity::class.java)
startActivity(intent) startActivity(intent)
loadingDialog.dismiss()
finish() finish()
}
} else { } else {
runOnUiThread {
loadingDialog.dismiss()
Toast.makeText(this, responseText, Toast.LENGTH_LONG).show() Toast.makeText(this, responseText, Toast.LENGTH_LONG).show()
} }
} }
})
}
})
} }
binding.joinGroupButton.setOnClickListener { binding.joinGroupButton.setOnClickListener {
@ -65,18 +76,25 @@ class GroupActivity : AppCompatActivity() {
net.server = sharedPreferences.getString("server", "")!! net.server = sharedPreferences.getString("server", "")!!
net.token = sharedPreferences.getString("token", "")!! net.token = sharedPreferences.getString("token", "")!!
if (!net.serverIsAvailable(this)) { net.serverIsAvailable(this) { isServerAvailable ->
if (!isServerAvailable) {
noInternetConnectionAvailableNotification(this) noInternetConnectionAvailableNotification(this)
} else { } else {
val requestGroupIdResponse = net.getGroupId(groupName) runOnUiThread {
loadingDialog.show()
}
net.getGroupId(groupName, { requestGroupIdResponse ->
var groupId: Int var groupId: Int
try { try {
groupId = requestGroupIdResponse.toInt() groupId = requestGroupIdResponse.toInt()
} catch (e: Exception) { } catch (e: Exception) {
runOnUiThread {
loadingDialog.dismiss()
Toast.makeText(this, requestGroupIdResponse, Toast.LENGTH_SHORT).show() Toast.makeText(this, requestGroupIdResponse, Toast.LENGTH_SHORT).show()
return@setOnClickListener
} }
val response = net.joinGroup(groupId, groupPassword) return@getGroupId
}
net.joinGroup(groupId, groupPassword, { response ->
val responseText = response.body!!.string() val responseText = response.body!!.string()
if (response.code == 200) { if (response.code == 200) {
@ -84,13 +102,22 @@ class GroupActivity : AppCompatActivity() {
currentGroups!!.add(groupId.toString()) currentGroups!!.add(groupId.toString())
sharedPreferences.edit().putStringSet("groups", currentGroups).apply() sharedPreferences.edit().putStringSet("groups", currentGroups).apply()
sharedPreferences.edit().putString("currentGroup", groupId.toString()).apply() sharedPreferences.edit().putString("currentGroup", groupId.toString()).apply()
runOnUiThread {
val intent = Intent(this, MainActivity::class.java) val intent = Intent(this, MainActivity::class.java)
startActivity(intent) startActivity(intent)
loadingDialog.dismiss()
finish() finish()
}
} else { } else {
runOnUiThread {
loadingDialog.dismiss()
Toast.makeText(this, responseText, Toast.LENGTH_LONG).show() Toast.makeText(this, responseText, Toast.LENGTH_LONG).show()
} }
} }
})
})
}
}
} }
} }
} }

View File

@ -1,17 +1,16 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.app.ProgressDialog
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityLoginBinding import org.foxarmy.barcodescannerforemployees.databinding.ActivityLoginBinding
import org.foxarmy.barcodescannerforemployees.noInternetConnectionAvailableNotification import org.foxarmy.barcodescannerforemployees.noInternetConnectionAvailableNotification
import org.foxarmy.barcodescannerforemployees.parseArray import org.foxarmy.barcodescannerforemployees.parseIntArray
import org.json.JSONObject import org.json.JSONObject
class LoginActivity : AppCompatActivity() { class LoginActivity : AppCompatActivity() {
@ -24,15 +23,14 @@ class LoginActivity : AppCompatActivity() {
binding = ActivityLoginBinding.inflate(layoutInflater); binding = ActivityLoginBinding.inflate(layoutInflater);
setContentView(binding.root) setContentView(binding.root)
val loadingDialog = ProgressDialog(this)
loadingDialog.setMessage(getString(R.string.loading_please_wait))
loadingDialog.setCancelable(false)
loadingDialog.setTitle(getString(R.string.loading))
fillUpLanguagesSpinner() fillUpLanguagesSpinner()
val sharedPreferences = EncryptedSharedPreferences.create( val sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
binding.loginButton.setOnClickListener { binding.loginButton.setOnClickListener {
val server = binding.serverTextEdit.text.toString() val server = binding.serverTextEdit.text.toString()
@ -45,13 +43,19 @@ class LoginActivity : AppCompatActivity() {
net.language = sharedPreferences.getString("language", "en-US")!! net.language = sharedPreferences.getString("language", "en-US")!!
net.server = server net.server = server
if (!net.serverIsAvailable(this)) { net.serverIsAvailable(this, { isServerAvailable ->
if (!isServerAvailable) {
noInternetConnectionAvailableNotification(this) noInternetConnectionAvailableNotification(this)
} else { } else {
val response = net.login(username, password) runOnUiThread {
loadingDialog.show()
}
net.login(username, password, { response ->
val responseText = response.body!!.string() val responseText = response.body!!.string()
if (response.code != 200) { if (response.code != 200) {
runOnUiThread {
Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show() Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show()
}
} else { } else {
val json = JSONObject(responseText) val json = JSONObject(responseText)
sharedPreferences.edit().putString("token", json["token"].toString()).apply() sharedPreferences.edit().putString("token", json["token"].toString()).apply()
@ -59,18 +63,25 @@ class LoginActivity : AppCompatActivity() {
sharedPreferences.edit().putInt("userId", json["id"].toString().toInt()).apply() sharedPreferences.edit().putInt("userId", json["id"].toString().toInt()).apply()
sharedPreferences.edit().putString("server", server).apply() sharedPreferences.edit().putString("server", server).apply()
net.getMyGroups({ response ->
val r = net.getMyGroups().body!!.string() runOnUiThread {
val myGroups = parseArray(r).map { a -> a.toString()} loadingDialog.dismiss()
}
val r = response.body!!.string()
if (r == "" || r == "[]") {
goToActivity("GroupActivity")
}
val myGroups = parseIntArray(r).map { a -> a.toString() }
sharedPreferences.edit().putStringSet("groups", myGroups.toSet()).apply() sharedPreferences.edit().putStringSet("groups", myGroups.toSet()).apply()
sharedPreferences.edit().putString("currentGroup", myGroups[0]).apply() sharedPreferences.edit().putString("currentGroup", myGroups[0]).apply()
val intent = Intent(this, MainActivity::class.java) goToActivity("MainActivity")
startActivity(intent) })
finish()
} }
})
} }
})
} }
binding.registerButton.setOnClickListener { binding.registerButton.setOnClickListener {
@ -85,24 +96,30 @@ class LoginActivity : AppCompatActivity() {
net.language = language net.language = language
net.server = server net.server = server
if (!net.serverIsAvailable(this)) { net.serverIsAvailable(this, {isServerAvailable ->
if (!isServerAvailable) {
noInternetConnectionAvailableNotification(this) noInternetConnectionAvailableNotification(this)
} else { } else {
val response = net.registerAccount(username, password); net.registerAccount(username, password, {response ->
val responseText = response.body!!.string() val responseText = response.body!!.string()
if (response.code != 200) { if (response.code != 200) {
Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show(); Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show();
} else { } else {
sharedPreferences.edit().putInt("userId", responseText.toInt()).apply() sharedPreferences.edit().putInt("userId", responseText.toInt()).apply()
val token = JSONObject(net.login(username, password).body!!.string())["token"].toString() net.login(username, password, {response ->
val token = JSONObject(response.body!!.string())["token"].toString()
sharedPreferences.edit().putString("token", token).apply() sharedPreferences.edit().putString("token", token).apply()
sharedPreferences.edit().putString("server", server).apply() sharedPreferences.edit().putString("server", server).apply()
val intent = Intent(this, GroupActivity::class.java) runOnUiThread {
startActivity(intent) loadingDialog.dismiss()
finish()
} }
goToActivity("GroupActivity")
})
} }
})
}
})
} }
binding.offlineButton.setOnClickListener { binding.offlineButton.setOnClickListener {
@ -117,9 +134,22 @@ class LoginActivity : AppCompatActivity() {
private fun fillUpLanguagesSpinner() { private fun fillUpLanguagesSpinner() {
val languages = resources.getStringArray(R.array.languages) val languages = resources.getStringArray(R.array.languages)
val arrayAdapter = ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, languages) val arrayAdapter =
ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, languages)
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item) arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
binding.languageSpinner.adapter = arrayAdapter binding.languageSpinner.adapter = arrayAdapter
} }
private fun goToActivity(activityName: String) {
runOnUiThread {
val intent = Intent(this, when(activityName) {
"MainActivity" -> MainActivity::class.java
"GroupActivity" -> GroupActivity::class.java
"LoginActivity" -> LoginActivity::class.java
else -> LoginActivity::class.java
})
startActivity(intent)
finish()
}
}
} }

View File

@ -1,8 +1,10 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.app.Activity import android.app.Activity
import android.app.ProgressDialog
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.Menu import android.view.Menu
@ -14,8 +16,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.security.crypto.EncryptedSharedPreferences
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 org.foxarmy.barcodescannerforemployees.* import org.foxarmy.barcodescannerforemployees.*
@ -43,16 +43,36 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
private var actionBarDrawerToggle: ActionBarDrawerToggle? = null private var actionBarDrawerToggle: ActionBarDrawerToggle? = null
private lateinit var ws: WebSocketClient private lateinit var ws: WebSocketClient
var selectionMode = false
private var selectedAmount = 0
private lateinit var sharedPreferences: SharedPreferences
private lateinit var loadingDialog: ProgressDialog
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
loadingDialog = ProgressDialog(this)
loadingDialog.setMessage(getString(R.string.loading_please_wait))
loadingDialog.setCancelable(false)
loadingDialog.setTitle(getString(R.string.loading))
sharedPreferences = getPreferences(this)
setSupportActionBar(binding.toolbar) setSupportActionBar(binding.toolbar)
setupViewPager(binding.tabViewpager) setupViewPager(binding.tabViewpager)
binding.tabTablayout.setupWithViewPager(binding.tabViewpager) binding.tabTablayout.setupWithViewPager(binding.tabViewpager)
binding.tabTablayout.setOnDragListener { _, _ ->
selectionMode = false
selectedAmount = 0
true
}
drawerLayout = binding.drawerLayout drawerLayout = binding.drawerLayout
actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, R.string.nav_open, R.string.nav_close) actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, R.string.nav_open, R.string.nav_close)
@ -62,14 +82,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
supportActionBar!!.setDisplayHomeAsUpEnabled(true) supportActionBar!!.setDisplayHomeAsUpEnabled(true)
val sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
binding.expiryCalendarFab.setOnClickListener { _ -> binding.expiryCalendarFab.setOnClickListener { _ ->
val expiryCalendarIntent = Intent(this, ExpiryCalendarActivity::class.java) val expiryCalendarIntent = Intent(this, ExpiryCalendarActivity::class.java)
val extras = Bundle() val extras = Bundle()
@ -77,7 +89,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
ContextCompat.startActivity(this, expiryCalendarIntent, extras) ContextCompat.startActivity(this, expiryCalendarIntent, extras)
} }
binding.newElementFab.setOnClickListener { view -> binding.newElementFab.setOnClickListener { _ ->
val currentPosition = binding.tabTablayout.selectedTabPosition val currentPosition = binding.tabTablayout.selectedTabPosition
val fragment = adapter.getItem(currentPosition) val fragment = adapter.getItem(currentPosition)
@ -112,26 +124,21 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val net = Net() val net = Net()
net.server = sharedPreferences.getString("server", "")!! net.server = sharedPreferences.getString("server", "")!!
val currentGroup = sharedPreferences.getString("currentGroup", "offline")!! val currentGroup = sharedPreferences.getString("currentGroup", "offline")!!
if ( currentGroup != "offline" && !net.serverIsAvailable(this)) { net.serverIsAvailable(this, {isServerAvailable ->
if ( currentGroup != "offline" && !isServerAvailable) {
runOnUiThread {
noInternetConnectionAvailableNotification(this) noInternetConnectionAvailableNotification(this)
} else if (currentGroup != "offline" && net.serverIsAvailable(this)) { }
} else if (currentGroup != "offline" && isServerAvailable) {
synchronize() synchronize()
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)
} }
})
} }
private fun synchronize() { private fun synchronize() {
val sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") return if (sharedPreferences.getString("currentGroup", "offline")!! == "offline") return
val net = Net() val net = Net()
@ -142,7 +149,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt() val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt()
val data = JSONObject(net.synchronize(currentGroup).body!!.string()) net.synchronize(currentGroup, {response ->
val data = JSONObject(response.body!!.string())
val remoteAbstractProducts = data["abstract_products"] as JSONArray val remoteAbstractProducts = data["abstract_products"] as JSONArray
val remoteProducts = data["products"] as JSONArray val remoteProducts = data["products"] as JSONArray
@ -161,7 +169,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
syncAbstractProducts(net, currentGroup, abstractProductDAO, remoteAbstractProducts, localAbstractProducts) syncAbstractProducts(net, currentGroup, abstractProductDAO, remoteAbstractProducts, localAbstractProducts)
syncProducts(productDAO, remoteProducts, localProducts) syncProducts(productDAO, remoteProducts, localProducts)
syncCategories(categoryDAO, remoteCategories, localCategories) syncCategories(categoryDAO, remoteCategories, localCategories)
})
} }
private fun syncCategories( private fun syncCategories(
@ -304,12 +312,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val pictureFile = val pictureFile =
File(picturesDir, "${remoteAbstractProduct["image_filename"]}.png") File(picturesDir, "${remoteAbstractProduct["image_filename"]}.png")
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, {
val newAbstractProduct = AbstractProduct.createFromJSON(remoteAbstractProduct) val newAbstractProduct = AbstractProduct.createFromJSON(remoteAbstractProduct)
abstractProductDAO.addAbstractProduct(newAbstractProduct) abstractProductDAO.addAbstractProduct(newAbstractProduct)
})
} }
if (abstractProductInRemoteDB != null && abstractProductInLocalDB != null) { if (abstractProductInRemoteDB != null && abstractProductInLocalDB != null) {
@ -319,11 +326,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val pictureFile = val pictureFile =
File(File(filesDir, "pictures"), "${abstractProductInRemoteDB["image_filename"]}.png") File(File(filesDir, "pictures"), "${abstractProductInRemoteDB["image_filename"]}.png")
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, {
val updatedData = AbstractProduct.createFromJSON(abstractProductInRemoteDB) val updatedData = AbstractProduct.createFromJSON(abstractProductInRemoteDB)
abstractProductDAO.updateAbstractProduct(updatedData) abstractProductDAO.updateAbstractProduct(updatedData)
})
} }
} }
} }
@ -337,6 +344,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
adapter.addFragment(CategoriesFragment(), getString(R.string.categories_title)) adapter.addFragment(CategoriesFragment(), getString(R.string.categories_title))
viewpager.adapter = adapter viewpager.adapter = adapter
viewpager.offscreenPageLimit = 3
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -346,13 +354,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
private fun isOffline(): Boolean { private fun isOffline(): Boolean {
val sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
return sharedPreferences.getString("currentGroup", "") == "offline" return sharedPreferences.getString("currentGroup", "") == "offline"
} }
@ -380,6 +381,15 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
R.id.nav_settings -> { R.id.nav_settings -> {
intent = Intent(this, SettingsActivity::class.java) intent = Intent(this, SettingsActivity::class.java)
} }
R.id.nav_logout -> {
sharedPreferences.edit().putString("currentGroup", "").apply()
sharedPreferences.edit().putStringSet("groups", emptySet()).apply()
sharedPreferences.edit().putString("token", "").apply()
sharedPreferences.edit().putString("server", "").apply()
intent = Intent(this, LoginActivity::class.java)
}
} }
startActivity(intent) startActivity(intent)
@ -495,4 +505,17 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val storageFragment = fragment as StorageFragment val storageFragment = fragment as StorageFragment
storageFragment.filterByCategory(id) storageFragment.filterByCategory(id)
} }
fun addSelection() {
selectionMode = true
selectedAmount ++
}
fun removeSelection() {
selectedAmount --
if (selectedAmount <= 0) {
selectedAmount = 0
selectionMode = false
}
}
} }

View File

@ -1,19 +1,27 @@
package org.foxarmy.barcodescannerforemployees.activities package org.foxarmy.barcodescannerforemployees.activities
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.EditText
import android.widget.PopupMenu
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityManageGroupBinding import org.foxarmy.barcodescannerforemployees.databinding.ActivityManageGroupBinding
import org.foxarmy.barcodescannerforemployees.parseArray import org.foxarmy.barcodescannerforemployees.parseIntArray
import org.foxarmy.barcodescannerforemployees.views.GroupMemberView import org.foxarmy.barcodescannerforemployees.views.GroupMemberView
class ManageGroupActivity : AppCompatActivity() { class ManageGroupActivity : AppCompatActivity() {
private lateinit var binding: ActivityManageGroupBinding private lateinit var binding: ActivityManageGroupBinding
private var isAdmin: Boolean = true private var isAdmin: Boolean = false
private lateinit var sharedPreferences: SharedPreferences
private lateinit var net: Net
private var groupId: Int = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -21,56 +29,150 @@ class ManageGroupActivity : AppCompatActivity(){
binding = ActivityManageGroupBinding.inflate(layoutInflater) binding = ActivityManageGroupBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
val groupId = intent.extras!!.getInt("groupId") groupId = intent.extras!!.getInt("groupId")
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
val sharedPreferences = EncryptedSharedPreferences.create( net = Net()
"sensitive",
masterKeyAlias,
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val net = Net()
net.token = sharedPreferences.getString("token", "")!! net.token = sharedPreferences.getString("token", "")!!
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 users = parseArray(net.getUsersInGroup(groupId).body!!.string()) setUpMembers()
for (user in users) { binding.renameButton.setOnClickListener {
val groupMemberView = GroupMemberView(this, this as Context, net.getUsernameById(user).body!!.string(), user) val input = EditText(this)
AlertDialog.Builder(this)
.setTitle(getString(R.string.new_group_name))
.setView(input)
.setPositiveButton(getString(R.string.ok)) { _, _ ->
val newName = input.text.toString()
net.renameGroup(groupId, newName, { response ->
runOnUiThread {
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_LONG).show()
}
})
binding.groupsContent.addView(groupMemberView) }
.setNegativeButton(getString(R.string.cancel)) { _, _ ->
}.show()
} }
isAdmin = amIAnAdminIn(groupId) binding.leaveButton.setOnClickListener {
net.leaveGroup(groupId, { response ->
runOnUiThread {
Toast.makeText(this, response.body!!.string(), Toast.LENGTH_SHORT).show()
}
if (response.code == 200) {
val groups = sharedPreferences.getStringSet("groups", emptySet())!!
groups.remove(groupId.toString())
sharedPreferences.edit().putStringSet("groups", groups).apply()
sharedPreferences.edit().putString("currentGroup", "").apply()
if (groups.isEmpty()) {
runOnUiThread {
val intent = Intent(this, GroupActivity::class.java)
startActivity(intent)
finish()
}
}
sharedPreferences.edit().putString("currentGroup", groups.toList()[0]).apply()
runOnUiThread {
finish()
}
}
})
}
setUpAdminRelatedButtons()
}
private fun setUpMembers() {
net.getUsersInGroup(groupId, { response ->
val users = parseIntArray(response.body!!.string())
for (user in users) {
net.getUsernameById(user, { response ->
val groupMemberView = GroupMemberView(this, this as Context, response.body!!.string(), user)
groupMemberView.setOnLongClickListener { view ->
amIAnAdminIn(groupId, { amIAnAdmin ->
if (!amIAnAdmin) return@amIAnAdminIn
runOnUiThread {
val popupMenu = PopupMenu(this, groupMemberView)
popupMenu.inflate(R.menu.user_pop_menu)
popupMenu.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.kick -> {
setUpMembers()
false
}
R.id.transfer_ownership -> {
AlertDialog.Builder(this)
.setMessage(getString(R.string.transfer_ownership_confirmation))
.setPositiveButton(R.string.yes) { _, _ ->
net.transfer_ownership(
groupId,
(view as GroupMemberView).userId,
{ response ->
runOnUiThread {
Toast.makeText(
this,
response.body!!.string(),
Toast.LENGTH_SHORT
).show()
setUpAdminRelatedButtons()
}
})
}
.setNegativeButton(R.string.no) { _, _ -> }
.show()
true
}
else -> false
}
}
runOnUiThread {
popupMenu.show()
}
}
})
true
}
runOnUiThread {
binding.groupsContent.addView(groupMemberView)
}
})
}
})
}
fun amIAnAdminIn(groupId: Int, callback: (Boolean) -> Unit) {
val net = Net()
net.token = sharedPreferences.getString("token", "")!!
net.server = sharedPreferences.getString("server", "")!!
net.language = sharedPreferences.getString("language", "en-US")!!
net.getGroupAdminId(groupId, { response ->
val result = sharedPreferences.getInt("userId", 0) == response.body!!.string().toInt()
callback(result)
})
}
fun setUpAdminRelatedButtons() {
amIAnAdminIn(groupId, { isAdmin ->
runOnUiThread {
if (!isAdmin) { if (!isAdmin) {
binding.renameButton.visibility = View.GONE binding.renameButton.visibility = View.GONE
} }
} }
})
fun amIAnAdminIn(groupId: Int): Boolean {
val sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val net = Net()
net.token = sharedPreferences.getString("token", "")!!
net.server = sharedPreferences.getString("server", "")!!
net.language = sharedPreferences.getString("language", "en-US")!!
val result = sharedPreferences.getInt("userId", 0) == net.getGroupAdminId(groupId).body!!.string().toInt()
return result
} }
} }

View File

@ -5,12 +5,10 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityMyGroupsBinding import org.foxarmy.barcodescannerforemployees.databinding.ActivityMyGroupsBinding
import org.foxarmy.barcodescannerforemployees.parseArray import org.foxarmy.barcodescannerforemployees.parseIntArray
import org.foxarmy.barcodescannerforemployees.views.GroupView import org.foxarmy.barcodescannerforemployees.views.GroupView
class MyGroupsActivity : AppCompatActivity(){ class MyGroupsActivity : AppCompatActivity(){
@ -22,13 +20,7 @@ class MyGroupsActivity : AppCompatActivity(){
binding = ActivityMyGroupsBinding.inflate(layoutInflater) binding = ActivityMyGroupsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
val sharedPreferences = EncryptedSharedPreferences.create( val sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val net = Net() val net = Net()
@ -36,20 +28,28 @@ class MyGroupsActivity : 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 groups = parseArray(net.getMyGroups().body!!.string()) net.getMyGroups { response ->
val groups = parseIntArray(response.body!!.string())
val container = findViewById<LinearLayout>(R.id.groupsLayout) val container = findViewById<LinearLayout>(R.id.groupsLayout)
for (group in groups) { for (group in groups) {
val groupView = GroupView(this, this as Context, net.getGroupName(group), group) net.getGroupName(group, {response ->
val groupView = GroupView(this, this as Context, response, group)
runOnUiThread {
container.addView(groupView) container.addView(groupView)
} }
})
}
binding.newGroup.setOnClickListener { binding.newGroup.setOnClickListener {
runOnUiThread {
val intent = Intent(this, GroupActivity::class.java) val intent = Intent(this, GroupActivity::class.java)
startActivity(intent) startActivity(intent)
} }
} }
} }
}
}

View File

@ -4,8 +4,8 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import androidx.security.crypto.EncryptedSharedPreferences import org.foxarmy.barcodescannerforemployees.Net
import androidx.security.crypto.MasterKeys import org.foxarmy.barcodescannerforemployees.parseStringList
class NavigatorActivity : Activity() { class NavigatorActivity : Activity() {
@ -14,27 +14,24 @@ class NavigatorActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
var intent = Intent(this, MainActivity::class.java) var intent = Intent(this, MainActivity::class.java)
if (!isInGroup() && !isOffline()) { isInGroup { flag ->
if (!flag && !isOffline()) {
intent = Intent(this, GroupActivity::class.java) intent = Intent(this, GroupActivity::class.java)
} }
if (!isAuthenticated() && !isOffline()) { if (!isAuthenticated() && !isOffline()) {
intent = Intent(this, LoginActivity::class.java); intent = Intent(this, LoginActivity::class.java);
} }
runOnUiThread {
startActivity(intent); startActivity(intent);
finish(); finish();
} }
}
}
private fun isOffline(): Boolean { private fun isOffline(): Boolean {
return sharedPreferences.getString("currentGroup", "")!! == "offline" return sharedPreferences.getString("currentGroup", "")!! == "offline"
@ -44,7 +41,28 @@ class NavigatorActivity : Activity() {
return sharedPreferences.getString("token", "") != "" return sharedPreferences.getString("token", "") != ""
} }
private fun isInGroup(): Boolean { private fun isInGroup(callback: (Boolean) -> Unit) {
return sharedPreferences.getStringSet("groups", emptySet())!!.isNotEmpty() val groups = sharedPreferences.getStringSet("groups", emptySet())!!
if (groups.isEmpty()) {
if (sharedPreferences.getString("token", "")!! == "") callback(false)
val net = Net()
net.language = sharedPreferences.getString("language", "")!!
net.server = sharedPreferences.getString("server", "")!!
net.token = sharedPreferences.getString("token", "")!!
net.getMyGroups { response ->
val responseBody = response.body!!.string()
if (responseBody == "" || responseBody == "[]") callback(false)
val groupsFromServer = parseStringList(responseBody)
if (groupsFromServer.isNotEmpty()) {
sharedPreferences.edit().putStringSet("groups", groupsFromServer.toSet()).apply()
sharedPreferences.edit().putString("currentGroup", groupsFromServer[0]).apply()
callback(true)
} else {
callback(false)
}
}
}
callback(true)
} }
} }

View File

@ -6,8 +6,6 @@ import android.os.Bundle
import android.view.View import android.view.View
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.databinding.ActivitySettingsBinding import org.foxarmy.barcodescannerforemployees.databinding.ActivitySettingsBinding
@ -17,17 +15,12 @@ class SettingsActivity : AppCompatActivity() {
lateinit var myGroups: List<String> lateinit var myGroups: List<String>
lateinit var currentGroup: String lateinit var currentGroup: String
lateinit var groupsNames: MutableList<String> lateinit var groupsNames: MutableList<String>
lateinit var namesMap: MutableMap<Int, String>
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
myGroups = sharedPreferences.getStringSet("groups", emptySet())!!.toList() myGroups = sharedPreferences.getStringSet("groups", emptySet())!!.toList()
currentGroup = sharedPreferences.getString("currentGroup", "offline")!! currentGroup = sharedPreferences.getString("currentGroup", "offline")!!
@ -46,21 +39,27 @@ class SettingsActivity : AppCompatActivity() {
if (isOffline()) { if (isOffline()) {
binding.currentGroupSetting.visibility = View.GONE binding.currentGroupSetting.visibility = View.GONE
} else { } else {
groupsNames = mutableListOf() namesMap = mutableMapOf()
for (myGroup in myGroups) { for (myGroup in myGroups) {
groupsNames.add(net.getGroupName(myGroup.toInt())) net.getGroupName(myGroup.toInt(), { name ->
} namesMap[myGroup.toInt()] = name
fillUpCurrentGroupSpinner() fillUpCurrentGroupSpinner()
})
}
} }
binding.saveButton.setOnClickListener { binding.saveButton.setOnClickListener {
sharedPreferences.edit().putString("currentGroup", net.getGroupId(binding.currentGroupSpinner.selectedItem.toString())).apply() net.getGroupId(binding.currentGroupSpinner.selectedItem.toString(), { groupId ->
sharedPreferences.edit().putInt("imageCompression", binding.imageCompressionFactorSeekBar.progress + 1).apply() sharedPreferences.edit().putString("currentGroup", groupId).apply()
sharedPreferences.edit().putInt("imageCompression", binding.imageCompressionFactorSeekBar.progress + 1)
.apply()
runOnUiThread {
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)
finish() finish()
} }
})
}
binding.cancelButton.setOnClickListener { binding.cancelButton.setOnClickListener {
setResult(Activity.RESULT_CANCELED) setResult(Activity.RESULT_CANCELED)
@ -78,14 +77,27 @@ class SettingsActivity : AppCompatActivity() {
} }
private fun fillUpCurrentGroupSpinner() { private fun fillUpCurrentGroupSpinner() {
runOnUiThread {
if (currentGroup == "offline") { if (currentGroup == "offline") {
binding.currentGroupSetting.visibility = View.GONE binding.currentGroupSetting.visibility = View.GONE
} }
groupsNames = mutableListOf()
groupsNames.clear()
val sortedMap = namesMap.entries.sortedBy { it.key }.associate { it.toPair() }
for ((id, name) in sortedMap) {
groupsNames.add(name)
}
val arrayAdapter = val arrayAdapter =
ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, groupsNames) ArrayAdapter(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, groupsNames)
arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item) arrayAdapter.setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
binding.currentGroupSpinner.adapter = arrayAdapter binding.currentGroupSpinner.adapter = arrayAdapter
binding.currentGroupSpinner.setSelection(myGroups.indexOf(currentGroup)) if (myGroups.indexOf(currentGroup) < groupsNames.size) binding.currentGroupSpinner.setSelection(
myGroups.indexOf(
currentGroup
)
)
}
} }
} }

View File

@ -308,7 +308,7 @@ class AbstractProductDAO(private val dbHelper: DBStorageController) {
} }
"barcodeless" -> { "barcodeless" -> {
selection = "${AbstractProductContract.AbstractProductEntry.BARCODE} = ? OR ${AbstractProductContract.AbstractProductEntry.BARCODE} = ?" selection = "${AbstractProductContract.AbstractProductEntry.BARCODE} = ? OR ${AbstractProductContract.AbstractProductEntry.BARCODE} = ? OR ${AbstractProductContract.AbstractProductEntry.BARCODE} = ?"
selectionArgs = filter selectionArgs = filter
} }
} }

View File

@ -11,36 +11,31 @@ import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.AddCategoryActivity import org.foxarmy.barcodescannerforemployees.activities.AddCategoryActivity
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.getPreferences
import org.foxarmy.barcodescannerforemployees.views.CategoryView import org.foxarmy.barcodescannerforemployees.views.CategoryView
import kotlin.concurrent.thread
class CategoriesFragment : Fragment() { class CategoriesFragment : Fragment() {
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var categoryDAO: CategoryDAO private lateinit var categoryDAO: CategoryDAO
private var updateInProgress = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = getPreferences(requireContext())
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
requireContext(),
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
prepareDatabaseConnection() prepareDatabaseConnection()
} }
private fun prepareDatabaseConnection() { private fun prepareDatabaseConnection() {
val dbHelper = DBStorageController(requireContext(), sharedPreferences.getString("currentGroup", "database")!!) val dbHelper = DBStorageController(requireContext(), sharedPreferences.getString("currentGroup", "offline")!!)
categoryDAO = CategoryDAO(dbHelper) categoryDAO = CategoryDAO(dbHelper)
} }
@ -58,6 +53,7 @@ class CategoriesFragment : Fragment() {
} }
fun removeSelected() { fun removeSelected() {
thread {
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout) val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
var deleted = false var deleted = false
@ -70,22 +66,27 @@ class CategoriesFragment : Fragment() {
val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt() val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt()
categoryDAO.eraseCategory(view.category.id, requireContext()) categoryDAO.eraseCategory(view.category.id, requireContext())
val response = net.deleteCategory(currentGroup, view.category.id) net.deleteCategory(currentGroup, view.category.id, {response ->
activity!!.runOnUiThread{ activity!!.runOnUiThread{
Toast.makeText(context, response.body!!.string(), Toast.LENGTH_SHORT).show() Toast.makeText(context, response.body!!.string(), Toast.LENGTH_SHORT).show()
} }
})
deleted = true deleted = true
} }
} }
if (!deleted) { if (!deleted) {
activity!!.runOnUiThread {
Toast.makeText(requireContext(), getString(R.string.nothing_to_delete), Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), getString(R.string.nothing_to_delete), Toast.LENGTH_SHORT).show()
} }
}
updateContent() updateContent()
} }
}
fun updateSelected() { fun updateSelected() {
thread {
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout) val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
for (view: CategoryView in layout?.children!!.iterator() as Iterator<CategoryView>) { for (view: CategoryView in layout?.children!!.iterator() as Iterator<CategoryView>) {
if (view.isCategorySelected) { if (view.isCategorySelected) {
@ -93,24 +94,37 @@ class CategoriesFragment : Fragment() {
val extras = Bundle() val extras = Bundle()
extras.putParcelable("category", view.category) extras.putParcelable("category", view.category)
addCategoryIntent.putExtras(extras) addCategoryIntent.putExtras(extras)
activity!!.runOnUiThread {
ContextCompat.startActivity(context!!, addCategoryIntent, extras) ContextCompat.startActivity(context!!, addCategoryIntent, extras)
} }
} }
} }
}
}
fun updateContent() { fun updateContent() {
thread {
if (updateInProgress) return@thread
updateInProgress = true
prepareDatabaseConnection() prepareDatabaseConnection()
val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout) val layout = view?.findViewById<LinearLayout>(R.id.categoriesLayout)
activity!!.runOnUiThread {
layout?.removeAllViews() layout?.removeAllViews()
}
val categories = categoryDAO.getAllCategories() val categories = categoryDAO.getAllCategories()
for (category in categories) { for (category in categories) {
val categoryView = CategoryView(requireActivity(), requireContext(), category) val categoryView = CategoryView(requireActivity(), requireContext(), category)
activity!!.runOnUiThread {
layout?.addView(categoryView) layout?.addView(categoryView)
} }
} }
updateInProgress = false
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View File

@ -14,14 +14,13 @@ import androidx.core.content.ContextCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.gridlayout.widget.GridLayout import androidx.gridlayout.widget.GridLayout
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.AddProductActivity import org.foxarmy.barcodescannerforemployees.activities.AddProductActivity
import org.foxarmy.barcodescannerforemployees.database.DBStorageController import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.database.ProductDAO import org.foxarmy.barcodescannerforemployees.database.ProductDAO
import org.foxarmy.barcodescannerforemployees.databinding.FragmentShelfBinding import org.foxarmy.barcodescannerforemployees.databinding.FragmentShelfBinding
import org.foxarmy.barcodescannerforemployees.getPreferences
import org.foxarmy.barcodescannerforemployees.views.ProductView import org.foxarmy.barcodescannerforemployees.views.ProductView
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -38,16 +37,9 @@ class ShelfFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = getPreferences(requireContext())
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
requireContext(),
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
prepareDatabaseConnection() prepareDatabaseConnection()
} }
override fun onCreateView( override fun onCreateView(
@ -122,11 +114,12 @@ class ShelfFragment : Fragment() {
} }
fun updateContent() { fun updateContent() {
prepareDatabaseConnection()
thread { thread {
if (updateInProgress) return@thread if (updateInProgress) return@thread
updateInProgress = true updateInProgress = true
prepareDatabaseConnection()
val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout) val grv = view?.findViewById<GridLayout>(R.id.contentGridLayout)
activity!!.runOnUiThread { activity!!.runOnUiThread {
grv?.removeAllViews() grv?.removeAllViews()
} }
@ -144,7 +137,6 @@ class ShelfFragment : Fragment() {
} }
} }
updateInProgress = false updateInProgress = false
} }
} }
@ -165,10 +157,11 @@ class ShelfFragment : Fragment() {
val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt() val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt()
productDAO.eraseProduct(view.product.id) productDAO.eraseProduct(view.product.id)
val response = net.deleteProduct(currentGroup, view.product.id) net.deleteProduct(currentGroup, view.product.id, {response ->
activity!!.runOnUiThread{ activity!!.runOnUiThread{
Toast.makeText(context, response.body!!.string(), Toast.LENGTH_SHORT).show() Toast.makeText(context, response.body!!.string(), Toast.LENGTH_SHORT).show()
} }
})
deleted = true deleted = true
} }

View File

@ -7,11 +7,10 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.*
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.AddAbstractProductActivity import org.foxarmy.barcodescannerforemployees.activities.AddAbstractProductActivity
@ -20,6 +19,7 @@ import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.databinding.FragmentStorageBinding import org.foxarmy.barcodescannerforemployees.databinding.FragmentStorageBinding
import org.foxarmy.barcodescannerforemployees.generateThumbnailForImage import org.foxarmy.barcodescannerforemployees.generateThumbnailForImage
import org.foxarmy.barcodescannerforemployees.getPreferences
import org.foxarmy.barcodescannerforemployees.views.AbstractProductView import org.foxarmy.barcodescannerforemployees.views.AbstractProductView
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -32,23 +32,18 @@ class StorageFragment : Fragment() {
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var abstractProductDAO: AbstractProductDAO private lateinit var abstractProductDAO: AbstractProductDAO
private var updateInProcess = false
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
sharedPreferences = EncryptedSharedPreferences.create( binding = FragmentStorageBinding.inflate(inflater)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), sharedPreferences = getPreferences(requireContext())
requireContext(),
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
prepareDatabaseConnection() prepareDatabaseConnection()
binding = FragmentStorageBinding.inflate(inflater)
fillUpSortBySpinner() fillUpSortBySpinner()
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
@ -107,10 +102,12 @@ class StorageFragment : Fragment() {
val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt() val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt()
abstractProductDAO.eraseAbstractProduct(view.abstractProduct.id, requireContext()) abstractProductDAO.eraseAbstractProduct(view.abstractProduct.id, requireContext())
val response = net.deleteAbstractProduct(currentGroup, view.abstractProduct.id) net.deleteAbstractProduct(currentGroup, view.abstractProduct.id, {response ->
activity!!.runOnUiThread{ activity!!.runOnUiThread{
Toast.makeText(context, response.body!!.string(), Toast.LENGTH_SHORT).show() Toast.makeText(context, response.body!!.string(), Toast.LENGTH_SHORT).show()
} }
})
deleted = true deleted = true
} }
@ -141,9 +138,13 @@ class StorageFragment : Fragment() {
} }
fun updateContent() { fun updateContent() {
prepareDatabaseConnection()
thread { thread {
if (updateInProcess) return@thread
updateInProcess = true
prepareDatabaseConnection()
val grv = binding.contentGridLayout val grv = binding.contentGridLayout
activity!!.runOnUiThread { activity!!.runOnUiThread {
grv.removeAllViews() grv.removeAllViews()
} }
@ -160,26 +161,36 @@ class StorageFragment : Fragment() {
) )
if (filterBy == "barcodeless") { if (filterBy == "barcodeless") {
abstractProductView.setOnClickListener { abstractProductView.also {
it.setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct) (activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
} }
abstractProductView.findViewById<TextView>(R.id.productNameView).setOnClickListener { it.findViewById<TextView>(R.id.productNameView).setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct) (activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
} }
it.findViewById<ConstraintLayout>(R.id.productLayout).setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
}
it.findViewById<TextView>(R.id.categoryView).setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
}
it.findViewById<TextView>(R.id.unitView).setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
}
it.findViewById<TextView>(R.id.productNetWeightView).setOnClickListener {
(activity as FindBarcodelessAbstractProduct).selected(abstractProductView.abstractProduct)
}
}
} }
activity!!.runOnUiThread{ activity!!.runOnUiThread{
grv.addView(abstractProductView) grv.addView(abstractProductView)
} }
} }
}.join() updateInProcess = false
}
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateContent()
}
fun filterByCategory(id: Int) { fun filterByCategory(id: Int) {
filterBy = "category" filterBy = "category"
@ -194,7 +205,7 @@ class StorageFragment : Fragment() {
val args = Bundle() val args = Bundle()
args.putString("filterBy", filterBy) args.putString("filterBy", filterBy)
args.putStringArray("filter", filter) args.putStringArray("filter", filter)
fragment.setArguments(args) fragment.arguments = args
return fragment return fragment
} }

View File

@ -14,16 +14,12 @@ import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import androidx.security.crypto.EncryptedSharedPreferences import org.foxarmy.barcodescannerforemployees.*
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.getActivity
import org.foxarmy.barcodescannerforemployees.getImageUri
import org.foxarmy.barcodescannerforemployees.getUnitNameById
import java.io.File import java.io.File
class AbstractProductView : LinearLayout { class AbstractProductView : LinearLayout {
@ -45,13 +41,7 @@ class AbstractProductView: LinearLayout {
val inflater: LayoutInflater = activity.layoutInflater val inflater: LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.abstract_product_view, this) inflater.inflate(R.layout.abstract_product_view, this)
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = getPreferences(context)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!) val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
categoryDAO = CategoryDAO(dbHelper) categoryDAO = CategoryDAO(dbHelper)
@ -63,20 +53,14 @@ class AbstractProductView: LinearLayout {
val inflater: LayoutInflater = activity.layoutInflater val inflater: LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.abstract_product_view, this) inflater.inflate(R.layout.abstract_product_view, this)
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = getPreferences(context)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!) val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
categoryDAO = CategoryDAO(dbHelper) categoryDAO = CategoryDAO(dbHelper)
update() update()
} }
fun init () { private fun init() {
productLayout = findViewById(R.id.productLayout) productLayout = findViewById(R.id.productLayout)
productPicture = findViewById(R.id.productPicture) productPicture = findViewById(R.id.productPicture)
productNameField = findViewById(R.id.productNameView) productNameField = findViewById(R.id.productNameView)
@ -84,30 +68,8 @@ class AbstractProductView: LinearLayout {
categoryField = findViewById(R.id.categoryView) categoryField = findViewById(R.id.categoryView)
unitField = findViewById(R.id.unitView) unitField = findViewById(R.id.unitView)
setUpClicks()
productPicture!!.setOnClickListener {
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
val extras = Bundle()
extras.putString("imagehash", abstractProduct.imageHash)
fullscreenIntent.putExtras(extras)
startActivity(context, fullscreenIntent, extras)
}
productLayout!!.setOnClickListener {
}
unitField?.text = getUnitNameById(context, abstractProduct.unit) unitField?.text = getUnitNameById(context, abstractProduct.unit)
productLayout!!.setOnLongClickListener {
isProductSelected = !isProductSelected
this.background = ContextCompat.getDrawable(context, if (isProductSelected) R.drawable.outline_selected else R.drawable.outline)
true
}
productNameField!!.setOnClickListener {
Toast.makeText(activity, productNameField!!.text, Toast.LENGTH_SHORT).show()
}
} }
fun update() { fun update() {
@ -117,10 +79,126 @@ class AbstractProductView: LinearLayout {
thumbnailsDir.mkdirs() thumbnailsDir.mkdirs()
val imageUri = getImageUri(activity, File(thumbnailsDir, "${abstractProduct.imageHash}.webp")) val imageUri = getImageUri(activity, File(thumbnailsDir, "${abstractProduct.imageHash}.webp"))
productPicture!!.setImageURI(imageUri) productPicture!!.setImageURI(imageUri)
// productPicture!!.rotation = 90f
productNameField!!.text = abstractProduct.name productNameField!!.text = abstractProduct.name
netWeightField!!.text = abstractProduct.netWeight.toString() netWeightField!!.text = abstractProduct.netWeight.toString()
categoryField!!.text = categoryDAO.getCategoryNameById(abstractProduct.category) categoryField!!.text = categoryDAO.getCategoryNameById(abstractProduct.category)
} }
private fun setUpClicks() {
productLayout!!.setOnLongClickListener { onAnyLongClick() }
productNameField!!.setOnLongClickListener { onNameLongClick() }
productPicture!!.setOnLongClickListener { onImageLongClick() }
netWeightField!!.setOnLongClickListener { onAnyLongClick() }
categoryField!!.setOnLongClickListener { onAnyLongClick() }
unitField!!.setOnLongClickListener { onAnyLongClick() }
productLayout!!.setOnClickListener { onAnyClick() }
netWeightField!!.setOnClickListener { onAnyClick() }
categoryField!!.setOnClickListener { onAnyClick() }
unitField!!.setOnClickListener { onAnyClick() }
productPicture!!.setOnClickListener { onImageLongClick() }
productNameField!!.setOnClickListener { onNameLongClick() }
}
private fun onAnyClick(): Boolean {
if (activity !is MainActivity) return false
val mainActivity = activity as MainActivity
if (mainActivity.selectionMode) {
isProductSelected = !isProductSelected
if (isProductSelected) {
mainActivity.addSelection()
} else {
mainActivity.removeSelection()
}
activity.runOnUiThread {
this.background = ContextCompat.getDrawable(
context,
if (isProductSelected) R.drawable.outline_selected else R.drawable.outline
)
}
return true
} else {
return false
}
}
private fun onAnyLongClick(): Boolean {
if (activity !is MainActivity) return false
val mainActivity = activity as MainActivity
if (mainActivity.selectionMode) {
isProductSelected = !isProductSelected
if (isProductSelected) {
mainActivity.addSelection()
} else {
mainActivity.removeSelection()
}
activity.runOnUiThread {
this.background = ContextCompat.getDrawable(
context,
if (isProductSelected) R.drawable.outline_selected else R.drawable.outline
)
}
return true
} else {
mainActivity.addSelection()
isProductSelected = !isProductSelected
activity.runOnUiThread {
this.background = ContextCompat.getDrawable(
context,
if (isProductSelected) R.drawable.outline_selected else R.drawable.outline
)
}
return true
}
}
private fun onImageLongClick(): Boolean {
if (activity !is MainActivity) {
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
val extras = Bundle()
extras.putString("imagehash", abstractProduct.imageHash)
fullscreenIntent.putExtras(extras)
startActivity(context, fullscreenIntent, extras)
return true
}
val mainActivity = activity as MainActivity
if (mainActivity.selectionMode) {
return onAnyLongClick()
} else {
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
val extras = Bundle()
extras.putString("imagehash", abstractProduct.imageHash)
fullscreenIntent.putExtras(extras)
startActivity(context, fullscreenIntent, extras)
}
return true
}
private fun onNameLongClick(): Boolean {
if (activity !is MainActivity) {
activity.runOnUiThread {
Toast.makeText(activity, productNameField!!.text, Toast.LENGTH_SHORT).show()
}
return true
}
val mainActivity = activity as MainActivity
if (mainActivity.selectionMode) {
return onAnyLongClick()
} else {
activity.runOnUiThread {
Toast.makeText(activity, productNameField!!.text, Toast.LENGTH_SHORT).show()
}
}
return true
}
} }

View File

@ -7,16 +7,16 @@ import android.view.LayoutInflater
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.MainActivity import org.foxarmy.barcodescannerforemployees.activities.MainActivity
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.dataclasses.Category import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.getPreferences
class CategoryView : LinearLayout { class CategoryView : LinearLayout {
var category: Category var category: Category
val categoryName: TextView val categoryName: TextView
val amountOfProducts: TextView val amountOfProducts: TextView
var isCategorySelected = false var isCategorySelected = false
@ -27,13 +27,7 @@ class CategoryView : LinearLayout {
constructor(activity: Activity, context: Context, category: Category) : super(context) { constructor(activity: Activity, context: Context, category: Category) : super(context) {
this.category = category this.category = category
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = getPreferences(context)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!) val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
abstractProductDAO = AbstractProductDAO(dbHelper) abstractProductDAO = AbstractProductDAO(dbHelper)
@ -41,7 +35,7 @@ class CategoryView : LinearLayout {
val inflater: LayoutInflater = activity.layoutInflater val inflater: LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.category_view, this) inflater.inflate(R.layout.category_view, this)
this.background = ContextCompat.getDrawable(context, if (isCategorySelected) R.drawable.outline_selected else R.drawable.outline) updateStroke()
categoryName = findViewById(R.id.categoryNameTextView) categoryName = findViewById(R.id.categoryNameTextView)
amountOfProducts = findViewById(R.id.amountOfProducts) amountOfProducts = findViewById(R.id.amountOfProducts)
@ -49,13 +43,47 @@ class CategoryView : LinearLayout {
categoryName.text = category.name categoryName.text = category.name
amountOfProducts.text = abstractProductDAO.getAmountOfAbstractProductsInCategory(category.id).toString() amountOfProducts.text = abstractProductDAO.getAmountOfAbstractProductsInCategory(category.id).toString()
setOnLongClickListener { setUpClicks()
}
private fun setUpClicks() {
categoryName.setOnClickListener { onCategoryClick() }
amountOfProducts.setOnClickListener { onCategoryClick() }
categoryName.setOnLongClickListener { onAnyLongClick() }
amountOfProducts.setOnLongClickListener { onAnyLongClick() }
}
private fun onCategoryClick(): Boolean {
if (context !is MainActivity) return false
val mainActivity = context as MainActivity
if (mainActivity.selectionMode) return onAnyLongClick()
else (context as MainActivity).filterAbstractProductsByCategory(category.id)
return true
}
private fun onAnyLongClick(): Boolean {
if (context !is MainActivity) return false
val mainActivity = context as MainActivity
isCategorySelected = !isCategorySelected isCategorySelected = !isCategorySelected
updateStroke()
if (mainActivity.selectionMode) {
if (isCategorySelected) mainActivity.addSelection()
else mainActivity.removeSelection()
} else {
mainActivity.addSelection()
}
return true
}
private fun updateStroke() {
(context as Activity).runOnUiThread {
this.background = ContextCompat.getDrawable(context, if (isCategorySelected) R.drawable.outline_selected else R.drawable.outline) this.background = ContextCompat.getDrawable(context, if (isCategorySelected) R.drawable.outline_selected else R.drawable.outline)
true
}
setOnClickListener {
(activity as MainActivity).filterAbstractProductsByCategory(category.id)
} }
} }
} }

View File

@ -7,12 +7,11 @@ import android.view.LayoutInflater
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.ExpiryCalendarActivity import org.foxarmy.barcodescannerforemployees.activities.ExpiryCalendarActivity
import org.foxarmy.barcodescannerforemployees.database.DBStorageController import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.database.ProductDAO import org.foxarmy.barcodescannerforemployees.database.ProductDAO
import org.foxarmy.barcodescannerforemployees.getPreferences
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
class ExpiryGroupView : LinearLayout { class ExpiryGroupView : LinearLayout {
@ -24,13 +23,7 @@ class ExpiryGroupView : LinearLayout {
constructor(activity: Activity, context: Context, representingDate: Long) : super(context) { constructor(activity: Activity, context: Context, representingDate: Long) : super(context) {
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = getPreferences(context)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!) val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
productDAO = ProductDAO(dbHelper) productDAO = ProductDAO(dbHelper)

View File

@ -10,7 +10,7 @@ import org.foxarmy.barcodescannerforemployees.R
class GroupMemberView : LinearLayout { class GroupMemberView : LinearLayout {
private var username: String private var username: String
private var userId: Int var userId: Int
constructor(activity: Activity, context: Context, username: String, userId: Int) : super(context) { constructor(activity: Activity, context: Context, username: String, userId: Int) : super(context) {

View File

@ -13,6 +13,7 @@ import android.view.LayoutInflater
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
@ -21,18 +22,14 @@ import androidx.core.graphics.blue
import androidx.core.graphics.green import androidx.core.graphics.green
import androidx.core.graphics.red import androidx.core.graphics.red
import androidx.core.math.MathUtils.clamp import androidx.core.math.MathUtils.clamp
import androidx.security.crypto.EncryptedSharedPreferences import org.foxarmy.barcodescannerforemployees.*
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness 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
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.dataclasses.Product import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import org.foxarmy.barcodescannerforemployees.getActivity
import org.foxarmy.barcodescannerforemployees.getImageUri
import java.io.File import java.io.File
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -44,6 +41,7 @@ class ProductView: LinearLayout {
var isProductSelected = false var isProductSelected = false
private var activity: Activity private var activity: Activity
private lateinit var productLayout: ConstraintLayout
private lateinit var productImageView: ImageView private lateinit var productImageView: ImageView
private lateinit var productNameTextView: TextView private lateinit var productNameTextView: TextView
private lateinit var productNetWeightTextView: TextView private lateinit var productNetWeightTextView: TextView
@ -63,13 +61,7 @@ class ProductView: LinearLayout {
constructor(context: Context, a: AttributeSet) : super(context, a) { constructor(context: Context, a: AttributeSet) : super(context, a) {
activity = getActivity(context)!! activity = getActivity(context)!!
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = getPreferences(context)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "offline")!!) val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "offline")!!)
abstractProductDAO = AbstractProductDAO(dbHelper) abstractProductDAO = AbstractProductDAO(dbHelper)
@ -86,18 +78,13 @@ class ProductView: LinearLayout {
val inflater: LayoutInflater = activity.layoutInflater val inflater: LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.product_view, this) inflater.inflate(R.layout.product_view, this)
sharedPreferences = EncryptedSharedPreferences.create( sharedPreferences = getPreferences(context)
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "offline")!!) val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "offline")!!)
abstractProductDAO = AbstractProductDAO(dbHelper) abstractProductDAO = AbstractProductDAO(dbHelper)
categoryDAO = CategoryDAO(dbHelper) categoryDAO = CategoryDAO(dbHelper)
productLayout = findViewById(R.id.productLayout)
productImageView = findViewById(R.id.productPicture) productImageView = findViewById(R.id.productPicture)
productNameTextView = findViewById(R.id.productNameView) productNameTextView = findViewById(R.id.productNameView)
productNetWeightTextView = findViewById(R.id.productNetWeightView) productNetWeightTextView = findViewById(R.id.productNetWeightView)
@ -106,40 +93,124 @@ class ProductView: LinearLayout {
productLifeSpan = findViewById(R.id.dateSpan) productLifeSpan = findViewById(R.id.dateSpan)
productFreshnessTextView = findViewById(R.id.freshnessPercentTextView) productFreshnessTextView = findViewById(R.id.freshnessPercentTextView)
findViewById<ConstraintLayout>(R.id.productLayout).setOnLongClickListener { setUpClicks()
isProductSelected = !isProductSelected
updateStroke()
true
}
productImageView.setOnClickListener {
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
val extras = Bundle()
val abstractProduct = abstractProductDAO.findAbstractProductById(product.abstractProductId)
extras.putString("imagehash", abstractProduct!!.imageHash)
fullscreenIntent.putExtras(extras)
startActivity(context, fullscreenIntent, extras)
}
update() update()
} }
@RequiresApi(Build.VERSION_CODES.O)
private fun setUpClicks() {
productLayout.setOnLongClickListener { onAnyLongClick() }
productImageView.setOnLongClickListener { onImageLongClick() }
productNameTextView.setOnLongClickListener { onNameLongClick() }
productNetWeightTextView.setOnLongClickListener { onAnyLongClick() }
productAmountTextView.setOnLongClickListener { onAnyLongClick() }
productCategoryView.setOnLongClickListener { onAnyLongClick() }
productLifeSpan.setOnLongClickListener { onAnyLongClick() }
productFreshnessTextView.setOnLongClickListener { onAnyLongClick() }
productLayout.setOnClickListener { onAnyClick() }
productImageView.setOnClickListener { onImageLongClick() }
productNameTextView.setOnClickListener { onNameLongClick() }
productNetWeightTextView.setOnClickListener { onAnyClick() }
productAmountTextView.setOnClickListener { onAnyClick() }
productCategoryView.setOnClickListener { onAnyClick() }
productLifeSpan.setOnClickListener { onAnyClick() }
productFreshnessTextView.setOnClickListener { onAnyClick() }
}
@RequiresApi(Build.VERSION_CODES.O)
private fun onAnyClick(): Boolean {
if (activity !is MainActivity) return false
val mainActivity = activity as MainActivity
if (mainActivity.selectionMode) {
isProductSelected = !isProductSelected
if (isProductSelected) mainActivity.addSelection()
else mainActivity.removeSelection()
updateStroke()
return true
} else {
return false
}
}
@RequiresApi(Build.VERSION_CODES.O)
private fun onAnyLongClick(): Boolean {
if (activity !is MainActivity) return false
val mainActivity = activity as MainActivity
isProductSelected = !isProductSelected
updateStroke()
if (mainActivity.selectionMode) {
if (isProductSelected) mainActivity.addSelection()
else mainActivity.removeSelection()
} else {
mainActivity.addSelection()
}
return true
}
private fun onNameLongClick(): Boolean {
if (activity !is MainActivity) {
Toast.makeText(activity, productNameTextView.text, Toast.LENGTH_SHORT).show()
return true
}
val mainActivity = activity as MainActivity
if (mainActivity.selectionMode) return onAnyLongClick()
else Toast.makeText(activity, productNameTextView.text, Toast.LENGTH_SHORT).show()
return true
}
private fun onImageLongClick(): Boolean {
val abstractProduct = abstractProductDAO.findAbstractProductById(product.abstractProductId)
if (activity !is MainActivity) {
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
val extras = Bundle()
extras.putString("imagehash", abstractProduct!!.imageHash)
fullscreenIntent.putExtras(extras)
startActivity(context, fullscreenIntent, extras)
return true
}
val mainActivity = activity as MainActivity
if (mainActivity.selectionMode) {
return onAnyLongClick()
} else {
val fullscreenIntent = Intent(activity, FullscreenActivity::class.java)
val extras = Bundle()
extras.putString("imagehash", abstractProduct!!.imageHash)
fullscreenIntent.putExtras(extras)
startActivity(context, fullscreenIntent, extras)
}
return true
}
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
fun update() { fun update() {
val linkedAbstractProduct: AbstractProduct = abstractProductDAO.findAbstractProductById(product.abstractProductId) ?: return val linkedAbstractProduct: AbstractProduct =
abstractProductDAO.findAbstractProductById(product.abstractProductId) ?: return
val thumbnailsDir = File(activity.cacheDir, "thumbnails") val thumbnailsDir = File(activity.cacheDir, "thumbnails")
thumbnailsDir.mkdirs() thumbnailsDir.mkdirs()
val pictureFile = File(thumbnailsDir, "${linkedAbstractProduct.imageHash}.webp") val pictureFile = File(thumbnailsDir, "${linkedAbstractProduct.imageHash}.webp")
val imageUri = getImageUri(activity, pictureFile) val imageUri = getImageUri(activity, pictureFile)
productImageView.setImageURI(imageUri) productImageView.setImageURI(imageUri)
// productImageView.rotation = 90f
productNameTextView.text = linkedAbstractProduct.name productNameTextView.text = linkedAbstractProduct.name
productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString() productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString()
productAmountTextView.text = product.amount.toString() productAmountTextView.text = product.amount.toString()
productCategoryView.text = categoryDAO.getCategoryNameById(linkedAbstractProduct.category) productCategoryView.text = categoryDAO.getCategoryNameById(linkedAbstractProduct.category)
productLifeSpan.text = "${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfProduction*1000))}-${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfExpiry*1000))}" productLifeSpan.text = "${SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfProduction * 1000))}-${
SimpleDateFormat("dd.MM.yyyy").format(Date(product.dateOfExpiry * 1000))
}"
productFreshnessTextView.text = productFreshnessTextView.text =
if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY || product.freshness < 0) { if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY || product.freshness < 0) {
context.getString(R.string.expired) context.getString(R.string.expired)
@ -152,11 +223,11 @@ class ProductView: LinearLayout {
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
fun updateStroke() { fun updateStroke() {
activity.runOnUiThread {
if (isProductSelected) { if (isProductSelected) {
this.background = ContextCompat.getDrawable(context, R.drawable.outline_selected) this.background = ContextCompat.getDrawable(context, R.drawable.outline_selected)
} else { } else {
if (product.id != 0) { if (product.id != 0) {
thread {
this.outline = GradientDrawable() this.outline = GradientDrawable()
backgroundColor = evaluateColor() backgroundColor = evaluateColor()
strokeColor = darkenColor(backgroundColor, 0.25) // (backgroundColor and 0xfefefe ) shr 1 strokeColor = darkenColor(backgroundColor, 0.25) // (backgroundColor and 0xfefefe ) shr 1
@ -164,12 +235,12 @@ class ProductView: LinearLayout {
this.outline.setStroke(4, strokeColor) this.outline.setStroke(4, strokeColor)
this.outline.alpha = 84 this.outline.alpha = 84
this.background = outline this.background = outline
}
} else { } else {
this.background = ContextCompat.getDrawable(context, R.drawable.outline) this.background = ContextCompat.getDrawable(context, R.drawable.outline)
} }
} }
} }
}
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
fun evaluateColor(): Int { fun evaluateColor(): Int {
@ -178,8 +249,10 @@ class ProductView: LinearLayout {
} }
fun calculateFreshnessGradient(percentage: Double): Int { fun calculateFreshnessGradient(percentage: Double): Int {
val startColor = ContextCompat.getColor(context, if (percentage > 0.5) R.color.full_freshness else R.color.half_freshness) val startColor =
val endColor = ContextCompat.getColor(context, if (percentage > 0.5) R.color.half_freshness else R.color.expired_freshness) ContextCompat.getColor(context, if (percentage > 0.5) R.color.full_freshness else R.color.half_freshness)
val endColor =
ContextCompat.getColor(context, if (percentage > 0.5) R.color.half_freshness else R.color.expired_freshness)
val gradientPosition = 1 - if (percentage > 0.5) (percentage - 0.5) * 2 else percentage * 2 val gradientPosition = 1 - if (percentage > 0.5) (percentage - 0.5) * 2 else percentage * 2

View File

@ -8,7 +8,7 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:id="@+id/productLayout" android:outlineProvider="bounds" android:layout_height="match_parent" android:id="@+id/productLayout" android:outlineProvider="bounds"
android:background="#00FFFEFE" android:clickable="true"> android:background="#00FFFEFE" android:clickable="true" android:translationZ="0sp">
<ImageView <ImageView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" app:srcCompat="@android:drawable/ic_menu_gallery" android:layout_height="200dp" app:srcCompat="@android:drawable/ic_menu_gallery"

View File

@ -1,10 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="match_parent">
android:layout_height="match_parent"
<androidx.core.widget.NestedScrollView
android:layout_height="0dp"
tools:context=".fragments.ShelfFragment" tools:context=".fragments.ShelfFragment"
android:id="@+id/content"> android:id="@+id/content" android:layout_width="0dp" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -39,12 +39,13 @@
<androidx.viewpager.widget.ViewPager <androidx.viewpager.widget.ViewPager
android:id="@+id/tab_viewpager" android:id="@+id/tab_viewpager"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="777dp" android:layout_height="0dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior" android:layout_gravity="bottom" app:layout_behavior="@string/appbar_scrolling_view_behavior" android:layout_gravity="bottom"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" app:layout_constraintTop_toBottomOf="@+id/appBarLayout"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="6dp" app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="6dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="6dp"/> app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="6dp"
app:layout_constraintBottom_toBottomOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/new_element_fab" android:id="@+id/new_element_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" android:id="@+id/storageLayout">
<fragment
android:id="@+id/nav_host_fragment_content_storage"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph_main"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -26,7 +26,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" app:layout_constraintTop_toBottomOf="@+id/spinner" android:layout_height="match_parent" app:layout_constraintTop_toBottomOf="@+id/spinner"
android:layout_marginTop="32dp" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="32dp" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" android:id="@+id/scrollView"> app:layout_constraintEnd_toEndOf="parent" android:id="@+id/scrollView"
app:layout_constraintBottom_toBottomOf="parent">
<androidx.gridlayout.widget.GridLayout <androidx.gridlayout.widget.GridLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2" android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2"

View File

@ -1,13 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment_storage"
tools:context=".fragments.StorageFragment">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView <TextView
android:text="@string/sort_by" android:text="@string/sort_by"
@ -16,7 +12,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
android:textAlignment="center" android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="@+id/scrollView2"/> app:layout_constraintBottom_toTopOf="@+id/scrollView2"/>
<Spinner <Spinner
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="32dp" android:id="@+id/spinner" android:layout_height="32dp" android:id="@+id/spinner"
@ -34,15 +30,16 @@
android:layout_marginEnd="10dp" app:layout_constraintTop_toTopOf="parent"/> android:layout_marginEnd="10dp" app:layout_constraintTop_toTopOf="parent"/>
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="0dp"
android:id="@+id/scrollView2" android:id="@+id/scrollView2"
app:layout_constraintTop_toBottomOf="@+id/dropFiltersButton"> app:layout_constraintTop_toBottomOf="@+id/dropFiltersButton"
android:layout_marginTop="4dp" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.gridlayout.widget.GridLayout <androidx.gridlayout.widget.GridLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/contentGridLayout" app:columnCount="2" android:layout_height="match_parent" android:id="@+id/contentGridLayout" app:columnCount="2"
app:rowCount="100" android:nestedScrollingEnabled="true"> app:rowCount="100" android:nestedScrollingEnabled="true">
</androidx.gridlayout.widget.GridLayout> </androidx.gridlayout.widget.GridLayout>
</ScrollView> </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -12,4 +12,5 @@
<item <item
android:id="@+id/nav_settings" android:id="@+id/nav_settings"
android:title="@string/settings"/> android:title="@string/settings"/>
<item android:title="@string/logout" android:id="@+id/nav_logout"/>
</menu> </menu>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/kick" android:id="@+id/kick"/>
<item android:title="@string/transfer_ownership" android:id="@+id/transfer_ownership"/>
</menu>

View File

@ -118,6 +118,14 @@
<string name="ok">Ок</string> <string name="ok">Ок</string>
<string name="logout">Выйти</string> <string name="logout">Выйти</string>
<string name="online_only_feature">Эта возможность доступна только с онлайн аккаунтом</string> <string name="online_only_feature">Эта возможность доступна только с онлайн аккаунтом</string>
<string name="username_changed">Имя пользователя изменено. Пожалуйста, перезайдите</string>
<string name="password_changed">Пароль изменён. Пожалуйста, перезайдите</string>
<string name="new_group_name">Введите новое имя группы</string>
<string name="kick">Выгнать</string>
<string name="transfer_ownership">Передать владение группой</string>
<string name="transfer_ownership_confirmation">Вы увереных, что хотите передать владение группой этому пользователю?</string>
<string name="loading_please_wait">Загрузка, пожалуйста ждите</string>
<string name="loading">Загрузка</string>
<string-array name="languages"> <string-array name="languages">
<item>en-US</item> <item>en-US</item>
<item>ru-RU</item> <item>ru-RU</item>

View File

@ -116,6 +116,14 @@
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="logout">Log out</string> <string name="logout">Log out</string>
<string name="online_only_feature">This feature is online only</string> <string name="online_only_feature">This feature is online only</string>
<string name="username_changed">Your username has been changed. Please, relogin</string>
<string name="password_changed">You password was changed. Please, relogin</string>
<string name="new_group_name">Enter new group name</string>
<string name="kick">Kick</string>
<string name="transfer_ownership">Transfer ownership</string>
<string name="transfer_ownership_confirmation">Are you sure you want to transfer ownership on current group to that user?</string>
<string name="loading_please_wait">Loading, please wait</string>
<string name="loading">Loading</string>
<string-array name="languages"> <string-array name="languages">
<item>en-US</item> <item>en-US</item>
<item>ru-RU</item> <item>ru-RU</item>