Compare commits

..

No commits in common. "master" and "alpha-0.0.6" have entirely different histories.

38 changed files with 894 additions and 1373 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,17 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.app.ProgressDialog
import android.content.Intent
import android.os.Bundle
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityLoginBinding
import org.foxarmy.barcodescannerforemployees.noInternetConnectionAvailableNotification
import org.foxarmy.barcodescannerforemployees.parseIntArray
import org.foxarmy.barcodescannerforemployees.parseArray
import org.json.JSONObject
class LoginActivity : AppCompatActivity() {
@ -23,14 +24,15 @@ class LoginActivity : AppCompatActivity() {
binding = ActivityLoginBinding.inflate(layoutInflater);
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()
val sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
val sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
binding.loginButton.setOnClickListener {
val server = binding.serverTextEdit.text.toString()
@ -43,45 +45,32 @@ class LoginActivity : AppCompatActivity() {
net.language = sharedPreferences.getString("language", "en-US")!!
net.server = server
net.serverIsAvailable(this, { isServerAvailable ->
if (!isServerAvailable) {
noInternetConnectionAvailableNotification(this)
if (!net.serverIsAvailable(this)) {
noInternetConnectionAvailableNotification(this)
} else {
val response = net.login(username, password)
val responseText = response.body!!.string()
if (response.code != 200) {
Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show()
} else {
runOnUiThread {
loadingDialog.show()
}
net.login(username, password, { response ->
val responseText = response.body!!.string()
if (response.code != 200) {
runOnUiThread {
Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show()
}
} else {
val json = JSONObject(responseText)
sharedPreferences.edit().putString("token", json["token"].toString()).apply()
net.token = json["token"].toString()
sharedPreferences.edit().putInt("userId", json["id"].toString().toInt()).apply()
sharedPreferences.edit().putString("server", server).apply()
val json = JSONObject(responseText)
sharedPreferences.edit().putString("token", json["token"].toString()).apply()
net.token = json["token"].toString()
sharedPreferences.edit().putInt("userId", json["id"].toString().toInt()).apply()
sharedPreferences.edit().putString("server", server).apply()
net.getMyGroups({ response ->
runOnUiThread {
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().putString("currentGroup", myGroups[0]).apply()
val r = net.getMyGroups().body!!.string()
val myGroups = parseArray(r).map { a -> a.toString()}
goToActivity("MainActivity")
})
}
})
sharedPreferences.edit().putStringSet("groups", myGroups.toSet()).apply()
sharedPreferences.edit().putString("currentGroup", myGroups[0]).apply()
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
}
})
}
}
binding.registerButton.setOnClickListener {
@ -96,30 +85,24 @@ class LoginActivity : AppCompatActivity() {
net.language = language
net.server = server
net.serverIsAvailable(this, {isServerAvailable ->
if (!isServerAvailable) {
noInternetConnectionAvailableNotification(this)
if (!net.serverIsAvailable(this)) {
noInternetConnectionAvailableNotification(this)
} else {
val response = net.registerAccount(username, password);
val responseText = response.body!!.string()
if (response.code != 200) {
Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show();
} else {
net.registerAccount(username, password, {response ->
val responseText = response.body!!.string()
if (response.code != 200) {
Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show();
} else {
sharedPreferences.edit().putInt("userId", responseText.toInt()).apply()
net.login(username, password, {response ->
val token = JSONObject(response.body!!.string())["token"].toString()
sharedPreferences.edit().putString("token", token).apply()
sharedPreferences.edit().putInt("userId", responseText.toInt()).apply()
val token = JSONObject(net.login(username, password).body!!.string())["token"].toString()
sharedPreferences.edit().putString("token", token).apply()
sharedPreferences.edit().putString("server", server).apply()
runOnUiThread {
loadingDialog.dismiss()
}
goToActivity("GroupActivity")
})
}
})
sharedPreferences.edit().putString("server", server).apply()
val intent = Intent(this, GroupActivity::class.java)
startActivity(intent)
finish()
}
})
}
}
binding.offlineButton.setOnClickListener {
@ -134,22 +117,9 @@ class LoginActivity : AppCompatActivity() {
private fun fillUpLanguagesSpinner() {
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)
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,10 +1,8 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.app.Activity
import android.app.ProgressDialog
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.view.Menu
@ -16,6 +14,8 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import androidx.viewpager.widget.ViewPager
import com.google.android.material.navigation.NavigationView
import org.foxarmy.barcodescannerforemployees.*
@ -43,36 +43,16 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
private var actionBarDrawerToggle: ActionBarDrawerToggle? = null
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?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
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)
setupViewPager(binding.tabViewpager)
binding.tabTablayout.setupWithViewPager(binding.tabViewpager)
binding.tabTablayout.setOnDragListener { _, _ ->
selectionMode = false
selectedAmount = 0
true
}
drawerLayout = binding.drawerLayout
actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, R.string.nav_open, R.string.nav_close)
@ -82,6 +62,14 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
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 { _ ->
val expiryCalendarIntent = Intent(this, ExpiryCalendarActivity::class.java)
val extras = Bundle()
@ -89,7 +77,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
ContextCompat.startActivity(this, expiryCalendarIntent, extras)
}
binding.newElementFab.setOnClickListener { _ ->
binding.newElementFab.setOnClickListener { view ->
val currentPosition = binding.tabTablayout.selectedTabPosition
val fragment = adapter.getItem(currentPosition)
@ -124,21 +112,26 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val net = Net()
net.server = sharedPreferences.getString("server", "")!!
val currentGroup = sharedPreferences.getString("currentGroup", "offline")!!
net.serverIsAvailable(this, {isServerAvailable ->
if ( currentGroup != "offline" && !isServerAvailable) {
runOnUiThread {
noInternetConnectionAvailableNotification(this)
}
} else if (currentGroup != "offline" && isServerAvailable) {
synchronize()
ws = WebSocketClient(this, sharedPreferences.getString("server", "")!!)
val token = sharedPreferences.getString("token", "")!!
ws.connect(token, currentGroup)
}
})
if ( currentGroup != "offline" && !net.serverIsAvailable(this)) {
noInternetConnectionAvailableNotification(this)
} else if (currentGroup != "offline" && net.serverIsAvailable(this)) {
synchronize()
ws = WebSocketClient(this, sharedPreferences.getString("server", "")!!)
val token = sharedPreferences.getString("token", "")!!
ws.connect(token, currentGroup)
}
}
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
val net = Net()
@ -149,27 +142,26 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val currentGroup = sharedPreferences.getString("currentGroup", "")!!.toInt()
net.synchronize(currentGroup, {response ->
val data = JSONObject(response.body!!.string())
val data = JSONObject(net.synchronize(currentGroup).body!!.string())
val remoteAbstractProducts = data["abstract_products"] as JSONArray
val remoteProducts = data["products"] as JSONArray
val remoteCategories = data["categories"] as JSONArray
val remoteAbstractProducts = data["abstract_products"] as JSONArray
val remoteProducts = data["products"] as JSONArray
val remoteCategories = data["categories"] as JSONArray
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "")!!)
val dbHelper = DBStorageController(this, sharedPreferences.getString("currentGroup", "")!!)
val abstractProductDAO = AbstractProductDAO(dbHelper)
val productDAO = ProductDAO(dbHelper)
val categoryDAO = CategoryDAO(dbHelper)
val abstractProductDAO = AbstractProductDAO(dbHelper)
val productDAO = ProductDAO(dbHelper)
val categoryDAO = CategoryDAO(dbHelper)
val localAbstractProducts = abstractProductDAO.getSortedListOfAbstractProducts(0, "", arrayOf(""))
val localProducts = productDAO.getSortedListOfProducts(0, "", "")
val localCategories = categoryDAO.getAllCategories()
val localAbstractProducts = abstractProductDAO.getSortedListOfAbstractProducts(0, "", arrayOf(""))
val localProducts = productDAO.getSortedListOfProducts(0, "", "")
val localCategories = categoryDAO.getAllCategories()
syncAbstractProducts(net, currentGroup, abstractProductDAO, remoteAbstractProducts, localAbstractProducts)
syncProducts(productDAO, remoteProducts, localProducts)
syncCategories(categoryDAO, remoteCategories, localCategories)
syncAbstractProducts(net, currentGroup, abstractProductDAO, remoteAbstractProducts, localAbstractProducts)
syncProducts(productDAO, remoteProducts, localProducts)
syncCategories(categoryDAO, remoteCategories, localCategories)
})
}
private fun syncCategories(
@ -312,11 +304,12 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val pictureFile =
File(picturesDir, "${remoteAbstractProduct["image_filename"]}.png")
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${localId}"
net.downloadImage(url, pictureFile, {
val newAbstractProduct = AbstractProduct.createFromJSON(remoteAbstractProduct)
net.downloadImage(url, pictureFile)
val newAbstractProduct = AbstractProduct.createFromJSON(remoteAbstractProduct)
abstractProductDAO.addAbstractProduct(newAbstractProduct)
abstractProductDAO.addAbstractProduct(newAbstractProduct)
})
}
if (abstractProductInRemoteDB != null && abstractProductInLocalDB != null) {
@ -326,11 +319,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val pictureFile =
File(File(filesDir, "pictures"), "${abstractProductInRemoteDB["image_filename"]}.png")
val url = "https://${net.server}/api/abstractproduct/getImage/${currentGroup}/${localId}"
net.downloadImage(url, pictureFile, {
val updatedData = AbstractProduct.createFromJSON(abstractProductInRemoteDB)
net.downloadImage(url, pictureFile)
abstractProductDAO.updateAbstractProduct(updatedData)
})
val updatedData = AbstractProduct.createFromJSON(abstractProductInRemoteDB)
abstractProductDAO.updateAbstractProduct(updatedData)
}
}
}
@ -344,7 +337,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
adapter.addFragment(CategoriesFragment(), getString(R.string.categories_title))
viewpager.adapter = adapter
viewpager.offscreenPageLimit = 3
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -354,6 +346,13 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
}
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"
}
@ -381,15 +380,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
R.id.nav_settings -> {
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)
@ -505,17 +495,4 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val storageFragment = fragment as StorageFragment
storageFragment.filterByCategory(id)
}
fun addSelection() {
selectionMode = true
selectedAmount ++
}
fun removeSelection() {
selectedAmount --
if (selectedAmount <= 0) {
selectedAmount = 0
selectionMode = false
}
}
}

View File

@ -1,27 +1,19 @@
package org.foxarmy.barcodescannerforemployees.activities
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
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.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityManageGroupBinding
import org.foxarmy.barcodescannerforemployees.parseIntArray
import org.foxarmy.barcodescannerforemployees.parseArray
import org.foxarmy.barcodescannerforemployees.views.GroupMemberView
class ManageGroupActivity : AppCompatActivity() {
class ManageGroupActivity : AppCompatActivity(){
private lateinit var binding: ActivityManageGroupBinding
private var isAdmin: Boolean = false
private lateinit var sharedPreferences: SharedPreferences
private lateinit var net: Net
private var groupId: Int = 0
private var isAdmin: Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -29,150 +21,56 @@ class ManageGroupActivity : AppCompatActivity() {
binding = ActivityManageGroupBinding.inflate(layoutInflater)
setContentView(binding.root)
groupId = intent.extras!!.getInt("groupId")
val groupId = intent.extras!!.getInt("groupId")
sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
net = Net()
val sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
masterKeyAlias,
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
net.token = sharedPreferences.getString("token", "")!!
net.server = sharedPreferences.getString("server", "")!!
net.language = sharedPreferences.getString("language", "en-US")!!
setUpMembers()
binding.renameButton.setOnClickListener {
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()
}
})
}
.setNegativeButton(getString(R.string.cancel)) { _, _ ->
}.show()
}
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)
})
val users = parseArray(net.getUsersInGroup(groupId).body!!.string())
for (user in users) {
val groupMemberView = GroupMemberView(this, this as Context, net.getUsernameById(user).body!!.string(), user)
binding.groupsContent.addView(groupMemberView)
}
isAdmin = amIAnAdminIn(groupId)
if (!isAdmin) {
binding.renameButton.visibility = View.GONE
}
}
fun setUpAdminRelatedButtons() {
amIAnAdminIn(groupId, { isAdmin ->
runOnUiThread {
if (!isAdmin) {
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,10 +5,12 @@ import android.content.Intent
import android.os.Bundle
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityMyGroupsBinding
import org.foxarmy.barcodescannerforemployees.parseIntArray
import org.foxarmy.barcodescannerforemployees.parseArray
import org.foxarmy.barcodescannerforemployees.views.GroupView
class MyGroupsActivity : AppCompatActivity(){
@ -20,7 +22,13 @@ class MyGroupsActivity : AppCompatActivity(){
binding = ActivityMyGroupsBinding.inflate(layoutInflater)
setContentView(binding.root)
val sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
val sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val net = Net()
@ -28,28 +36,20 @@ class MyGroupsActivity : AppCompatActivity(){
net.server = sharedPreferences.getString("server", "")!!
net.language = sharedPreferences.getString("language", "en-US")!!
net.getMyGroups { response ->
val groups = parseIntArray(response.body!!.string())
val groups = parseArray(net.getMyGroups().body!!.string())
val container = findViewById<LinearLayout>(R.id.groupsLayout)
for (group in groups) {
val groupView = GroupView(this, this as Context, net.getGroupName(group), group)
val container = findViewById<LinearLayout>(R.id.groupsLayout)
container.addView(groupView)
}
for (group in groups) {
net.getGroupName(group, {response ->
val groupView = GroupView(this, this as Context, response, group)
runOnUiThread {
container.addView(groupView)
}
})
}
binding.newGroup.setOnClickListener {
runOnUiThread {
val intent = Intent(this, GroupActivity::class.java)
startActivity(intent)
}
}
binding.newGroup.setOnClickListener {
val intent = Intent(this, GroupActivity::class.java)
startActivity(intent)
}
}
}

View File

@ -4,8 +4,8 @@ import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.parseStringList
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
class NavigatorActivity : Activity() {
@ -14,23 +14,26 @@ class NavigatorActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedPreferences = org.foxarmy.barcodescannerforemployees.getPreferences(this)
sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
var intent = Intent(this, MainActivity::class.java)
isInGroup { flag ->
if (!flag && !isOffline()) {
intent = Intent(this, GroupActivity::class.java)
}
if (!isAuthenticated() && !isOffline()) {
intent = Intent(this, LoginActivity::class.java);
}
runOnUiThread {
startActivity(intent);
finish();
}
if (!isInGroup() && !isOffline()) {
intent = Intent(this, GroupActivity::class.java)
}
if (!isAuthenticated() && !isOffline()) {
intent = Intent(this, LoginActivity::class.java);
}
startActivity(intent);
finish();
}
private fun isOffline(): Boolean {
@ -41,28 +44,7 @@ class NavigatorActivity : Activity() {
return sharedPreferences.getString("token", "") != ""
}
private fun isInGroup(callback: (Boolean) -> Unit) {
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)
private fun isInGroup(): Boolean {
return sharedPreferences.getStringSet("groups", emptySet())!!.isNotEmpty()
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,15 +14,19 @@ import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.startActivity
import org.foxarmy.barcodescannerforemployees.*
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
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
class AbstractProductView : LinearLayout {
class AbstractProductView: LinearLayout {
private var productLayout: ConstraintLayout? = null
private var productPicture: ImageView? = null
private var productNameField: TextView? = null
@ -38,10 +42,16 @@ class AbstractProductView : LinearLayout {
constructor(context: Context, a: AttributeSet) : super(context, a) {
activity = getActivity(context)!!
val inflater: LayoutInflater = activity.layoutInflater
val inflater:LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.abstract_product_view, this)
sharedPreferences = getPreferences(context)
sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
categoryDAO = CategoryDAO(dbHelper)
@ -50,17 +60,23 @@ class AbstractProductView : LinearLayout {
constructor(activity: Activity, context: Context, abstractProduct: AbstractProduct) : super(context) {
this.abstractProduct = abstractProduct
this.activity = activity
val inflater: LayoutInflater = activity.layoutInflater
val inflater:LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.abstract_product_view, this)
sharedPreferences = getPreferences(context)
sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
categoryDAO = CategoryDAO(dbHelper)
update()
}
private fun init() {
fun init () {
productLayout = findViewById(R.id.productLayout)
productPicture = findViewById(R.id.productPicture)
productNameField = findViewById(R.id.productNameView)
@ -68,8 +84,30 @@ class AbstractProductView : LinearLayout {
categoryField = findViewById(R.id.categoryView)
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)
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() {
@ -79,126 +117,10 @@ class AbstractProductView : LinearLayout {
thumbnailsDir.mkdirs()
val imageUri = getImageUri(activity, File(thumbnailsDir, "${abstractProduct.imageHash}.webp"))
productPicture!!.setImageURI(imageUri)
// productPicture!!.rotation = 90f
productNameField!!.text = abstractProduct.name
netWeightField!!.text = abstractProduct.netWeight.toString()
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.TextView
import androidx.core.content.ContextCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.dataclasses.Category
import org.foxarmy.barcodescannerforemployees.getPreferences
class CategoryView : LinearLayout {
var category: Category
val categoryName: TextView
val amountOfProducts: TextView
var isCategorySelected = false
@ -27,7 +27,13 @@ class CategoryView : LinearLayout {
constructor(activity: Activity, context: Context, category: Category) : super(context) {
this.category = category
sharedPreferences = getPreferences(context)
sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
abstractProductDAO = AbstractProductDAO(dbHelper)
@ -35,7 +41,7 @@ class CategoryView : LinearLayout {
val inflater: LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.category_view, this)
updateStroke()
this.background = ContextCompat.getDrawable(context, if (isCategorySelected) R.drawable.outline_selected else R.drawable.outline)
categoryName = findViewById(R.id.categoryNameTextView)
amountOfProducts = findViewById(R.id.amountOfProducts)
@ -43,47 +49,13 @@ class CategoryView : LinearLayout {
categoryName.text = category.name
amountOfProducts.text = abstractProductDAO.getAmountOfAbstractProductsInCategory(category.id).toString()
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
updateStroke()
if (mainActivity.selectionMode) {
if (isCategorySelected) mainActivity.addSelection()
else mainActivity.removeSelection()
} else {
mainActivity.addSelection()
}
return true
}
private fun updateStroke() {
(context as Activity).runOnUiThread {
setOnLongClickListener {
isCategorySelected = !isCategorySelected
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,11 +7,12 @@ import android.view.LayoutInflater
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.ExpiryCalendarActivity
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.database.ProductDAO
import org.foxarmy.barcodescannerforemployees.getPreferences
import java.text.SimpleDateFormat
class ExpiryGroupView : LinearLayout {
@ -23,7 +24,13 @@ class ExpiryGroupView : LinearLayout {
constructor(activity: Activity, context: Context, representingDate: Long) : super(context) {
sharedPreferences = getPreferences(context)
sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "database")!!)
productDAO = ProductDAO(dbHelper)

View File

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

View File

@ -13,7 +13,6 @@ import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
@ -22,26 +21,29 @@ import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
import androidx.core.math.MathUtils.clamp
import org.foxarmy.barcodescannerforemployees.*
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.activities.FullscreenActivity
import org.foxarmy.barcodescannerforemployees.activities.MainActivity
import org.foxarmy.barcodescannerforemployees.calculateProductFreshness
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController
import org.foxarmy.barcodescannerforemployees.dataclasses.AbstractProduct
import org.foxarmy.barcodescannerforemployees.dataclasses.Product
import org.foxarmy.barcodescannerforemployees.getActivity
import org.foxarmy.barcodescannerforemployees.getImageUri
import java.io.File
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.*
import kotlin.concurrent.thread
class ProductView : LinearLayout {
class ProductView: LinearLayout {
var product: Product = Product()
var isProductSelected = false
private var activity: Activity
private lateinit var productLayout: ConstraintLayout
private lateinit var productImageView: ImageView
private lateinit var productNameTextView: TextView
private lateinit var productNetWeightTextView: TextView
@ -61,7 +63,13 @@ class ProductView : LinearLayout {
constructor(context: Context, a: AttributeSet) : super(context, a) {
activity = getActivity(context)!!
sharedPreferences = getPreferences(context)
sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "offline")!!)
abstractProductDAO = AbstractProductDAO(dbHelper)
@ -78,13 +86,18 @@ class ProductView : LinearLayout {
val inflater: LayoutInflater = activity.layoutInflater
inflater.inflate(R.layout.product_view, this)
sharedPreferences = getPreferences(context)
sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val dbHelper = DBStorageController(context, sharedPreferences.getString("currentGroup", "offline")!!)
abstractProductDAO = AbstractProductDAO(dbHelper)
categoryDAO = CategoryDAO(dbHelper)
productLayout = findViewById(R.id.productLayout)
productImageView = findViewById(R.id.productPicture)
productNameTextView = findViewById(R.id.productNameView)
productNetWeightTextView = findViewById(R.id.productNetWeightView)
@ -93,152 +106,68 @@ class ProductView : LinearLayout {
productLifeSpan = findViewById(R.id.dateSpan)
productFreshnessTextView = findViewById(R.id.freshnessPercentTextView)
setUpClicks()
findViewById<ConstraintLayout>(R.id.productLayout).setOnLongClickListener {
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()
}
@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)
fun update() {
val linkedAbstractProduct: AbstractProduct =
abstractProductDAO.findAbstractProductById(product.abstractProductId) ?: return
fun update () {
val linkedAbstractProduct: AbstractProduct = abstractProductDAO.findAbstractProductById(product.abstractProductId) ?: return
val thumbnailsDir = File(activity.cacheDir, "thumbnails")
thumbnailsDir.mkdirs()
val pictureFile = File(thumbnailsDir, "${linkedAbstractProduct.imageHash}.webp")
val imageUri = getImageUri(activity, pictureFile)
productImageView.setImageURI(imageUri)
// productImageView.rotation = 90f
productNameTextView.text = linkedAbstractProduct.name
productNetWeightTextView.text = linkedAbstractProduct.netWeight.toString()
productAmountTextView.text = product.amount.toString()
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 =
if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY || product.freshness < 0) {
context.getString(R.string.expired)
} else {
"${DecimalFormat("#.#").format(product.freshness * 100)}%"
}
if (product.freshness == Double.NEGATIVE_INFINITY || product.freshness == Double.POSITIVE_INFINITY || product.freshness < 0) {
context.getString(R.string.expired)
} else {
"${DecimalFormat("#.#").format(product.freshness*100)}%"
}
updateStroke()
}
@RequiresApi(Build.VERSION_CODES.O)
fun updateStroke() {
activity.runOnUiThread {
if (isProductSelected) {
this.background = ContextCompat.getDrawable(context, R.drawable.outline_selected)
} else {
if (product.id != 0) {
this.outline = GradientDrawable()
backgroundColor = evaluateColor()
strokeColor = darkenColor(backgroundColor, 0.25) // (backgroundColor and 0xfefefe ) shr 1
this.outline.setColor(backgroundColor)
this.outline.setStroke(4, strokeColor)
this.outline.alpha = 84
this.background = outline
} else {
this.background = ContextCompat.getDrawable(context, R.drawable.outline)
}
}
if (isProductSelected) {
this.background = ContextCompat.getDrawable(context, R.drawable.outline_selected)
} else {
if (product.id != 0) {
thread {
this.outline = GradientDrawable()
backgroundColor = evaluateColor()
strokeColor = darkenColor(backgroundColor, 0.25) // (backgroundColor and 0xfefefe ) shr 1
this.outline.setColor(backgroundColor)
this.outline.setStroke(4, strokeColor)
this.outline.alpha = 84
this.background = outline
}
} else {
this.background = ContextCompat.getDrawable(context, R.drawable.outline)
}
}
}
@ -249,15 +178,13 @@ class ProductView : LinearLayout {
}
fun calculateFreshnessGradient(percentage: Double): Int {
val startColor =
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 startColor = 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 red = clamp((startColor.red + gradientPosition * (endColor.red - startColor.red)).toInt(), 0, 255)
val green = clamp((startColor.green + gradientPosition * (endColor.green - startColor.green)).toInt(), 0, 255)
val red = clamp((startColor.red + gradientPosition * (endColor.red - startColor.red )).toInt(), 0, 255)
val green = clamp((startColor.green + gradientPosition * (endColor.green - startColor.green)).toInt(), 0, 255)
val blue = clamp((startColor.blue + gradientPosition * (endColor.blue - startColor.blue)).toInt(), 0, 255)
var redHex = Integer.toHexString(red)
@ -267,7 +194,7 @@ class ProductView : LinearLayout {
if (greenHex.length == 1) greenHex = "0$greenHex"
var blueHex = Integer.toHexString(blue)
if (blueHex.length == 1) blueHex = "0$blueHex"
if (blueHex.length == 1 ) blueHex = "0$blueHex"
val colorString = "#$redHex$greenHex$blueHex"
@ -275,7 +202,7 @@ class ProductView : LinearLayout {
}
@RequiresApi(Build.VERSION_CODES.O)
fun darkenColor(color: Int, darkPercent: Double): Int {
fun darkenColor(color: Int, darkPercent: Double) : Int {
val c = Color.valueOf(color)
val red = c.red() * (1 - darkPercent)

View File

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

View File

@ -1,15 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
<androidx.core.widget.NestedScrollView
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">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.ShelfFragment"
android:id="@+id/content">
<androidx.core.widget.NestedScrollView
android:layout_height="0dp"
tools:context=".fragments.ShelfFragment"
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.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +0,0 @@
<?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,14 +118,6 @@
<string name="ok">Ок</string>
<string name="logout">Выйти</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">
<item>en-US</item>
<item>ru-RU</item>

View File

@ -116,14 +116,6 @@
<string name="ok">OK</string>
<string name="logout">Log out</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">
<item>en-US</item>
<item>ru-RU</item>