handling no internet situations

This commit is contained in:
leca 2024-11-12 17:57:45 +03:00
parent ec7062602d
commit d08a79e981
9 changed files with 220 additions and 90 deletions

View File

@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application <application
android:allowBackup="true" android:allowBackup="true"

View File

@ -1,5 +1,6 @@
package org.foxarmy.barcodescannerforemployees package org.foxarmy.barcodescannerforemployees
import android.content.Context
import okhttp3.* import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -17,6 +18,28 @@ class Net {
var server = "bsfe.foxarmy.org" var server = "bsfe.foxarmy.org"
var token = "" var token = ""
fun serverIsAvailable(context: Context): Boolean {
if (!isInternetConnectionAvailable(context)) {
return false
}
var flag = false
thread {
val client = OkHttpClient();
val request = Request.Builder()
.url("https://$server/status")
.get()
.build();
try {
val response = client.newCall(request).execute();
flag = response.code == 200;
} catch (e: Exception) {
flag = false;
}
}.join()
return flag
}
fun requestProductFromOnlineDB(barcode: String): String { fun requestProductFromOnlineDB(barcode: String): String {
var response = "" var response = ""
thread { thread {

View File

@ -7,10 +7,15 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Matrix import android.graphics.Matrix
import android.media.ExifInterface import android.media.ExifInterface
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import com.google.firebase.components.BuildConfig import com.google.firebase.components.BuildConfig
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
@ -18,6 +23,7 @@ import java.io.FileOutputStream
import java.security.MessageDigest import java.security.MessageDigest
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.system.exitProcess
fun convertToUnixEpochTimestamp(dateString: String): Long { fun convertToUnixEpochTimestamp(dateString: String): Long {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
@ -27,7 +33,11 @@ fun convertToUnixEpochTimestamp(dateString: String): Long {
} }
fun getImageUri(activity: Activity, imageFile: File): Uri? { fun getImageUri(activity: Activity, imageFile: File): Uri? {
return FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + "." + activity.localClassName + ".provider", imageFile) return FileProvider.getUriForFile(
activity,
BuildConfig.APPLICATION_ID + "." + activity.localClassName + ".provider",
imageFile
)
} }
@RequiresApi(Build.VERSION_CODES.R) @RequiresApi(Build.VERSION_CODES.R)
@ -47,13 +57,13 @@ fun generateThumbnailForImage(context: Context, imageHash: String) {
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
val matrix = Matrix() val matrix = Matrix()
when(orientation){ when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F) ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F) ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F) ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
} }
val rotated = Bitmap.createBitmap(img, 0, 0, img.width, img.height, matrix, true) val rotated = Bitmap.createBitmap(img, 0, 0, img.width, img.height, matrix, true)
val scaled = Bitmap.createScaledBitmap(rotated, rotated.width/4, rotated.height/4, true) val scaled = Bitmap.createScaledBitmap(rotated, rotated.width / 4, rotated.height / 4, true)
scaled.compress(Bitmap.CompressFormat.WEBP_LOSSY, 25, FileOutputStream(thumbnailFile)) scaled.compress(Bitmap.CompressFormat.WEBP_LOSSY, 25, FileOutputStream(thumbnailFile))
} }
@ -64,8 +74,11 @@ fun String.md5(): String {
return digest.toHexString() return digest.toHexString()
} }
fun stripNetWeight (netWeight: String): Double { fun stripNetWeight(netWeight: String): Double {
return removeSubstringsFromString(netWeight, arrayOf("Л", "л", "мл", "Мл", "г", "Г", "кг", "Кг", "шт", "Шт", ",", " ", ".")).toDouble() return removeSubstringsFromString(
netWeight,
arrayOf("Л", "л", "мл", "Мл", "г", "Г", "кг", "Кг", "шт", "Шт", ",", " ", ".")
).toDouble()
} }
fun removeSubstringsFromString(text: String, toRemove: Array<String>): String { fun removeSubstringsFromString(text: String, toRemove: Array<String>): String {
@ -99,14 +112,31 @@ fun calculateProductFreshness(dateOfProduction: Long, dateOfExpiry: Long): Doubl
return lifeSpanLeft / productLifeSpan.toDouble() return lifeSpanLeft / productLifeSpan.toDouble()
} }
fun getUnitNameById (context: Context, id: Int): String { fun getUnitNameById(context: Context, id: Int): String {
return when(id) { return when (id) {
0 -> { context.getString(R.string.kilogram) } 0 -> {
1 -> { context.getString(R.string.gram) } context.getString(R.string.kilogram)
2 -> { context.getString(R.string.liter) } }
3 -> { context.getString(R.string.milliliter) }
4 -> { context.getString(R.string.pieces) } 1 -> {
else -> { "" } context.getString(R.string.gram)
}
2 -> {
context.getString(R.string.liter)
}
3 -> {
context.getString(R.string.milliliter)
}
4 -> {
context.getString(R.string.pieces)
}
else -> {
""
}
} }
} }
@ -137,4 +167,47 @@ fun bytesToHex(bytes: ByteArray): String {
hexString.append(hex) hexString.append(hex)
} }
return hexString.toString() return hexString.toString()
}
fun isInternetConnectionAvailable(context: Context): Boolean {
if (context.getSystemService(Context.CONNECTIVITY_SERVICE) == null) return false
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (connectivityManager != null) {
val capabilities =
connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
if (capabilities != null) {
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return true
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
return true
}
}
}
return false
}
fun noInternetConnectionAvailableNotification(context: Context) {
AlertDialog.Builder(context)
.setMessage(context.getString(R.string.no_internet_connection))
.setPositiveButton(R.string.quit) { _, _ ->
exitProcess(0)
}
.setNeutralButton(R.string.logout) { _, _ ->
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()
exitProcess(0)
}.show()
} }

View File

@ -8,6 +8,7 @@ import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.databinding.ActivityGroupBinding import org.foxarmy.barcodescannerforemployees.databinding.ActivityGroupBinding
import org.foxarmy.barcodescannerforemployees.noInternetConnectionAvailableNotification
class GroupActivity : AppCompatActivity() { class GroupActivity : AppCompatActivity() {
@ -31,24 +32,28 @@ class GroupActivity : AppCompatActivity() {
val groupName = binding.groupNameTextEdit.text.toString() val groupName = binding.groupNameTextEdit.text.toString()
val groupPassword = binding.groupPasswordTextEdit.text.toString() val groupPassword = binding.groupPasswordTextEdit.text.toString()
// group is set to "successful" // group is set to "successful"
val n = Net() val net = Net()
n.language = sharedPreferences.getString("language", "en-US")!! net.language = sharedPreferences.getString("language", "en-US")!!
n.server = sharedPreferences.getString("server", "")!! net.server = sharedPreferences.getString("server", "")!!
n.token = sharedPreferences.getString("token", "")!! net.token = sharedPreferences.getString("token", "")!!
val response = n.createGroup(groupName, groupPassword) if (!net.serverIsAvailable(this)) {
val responseText = response.body!!.string() noInternetConnectionAvailableNotification(this)
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 { } else {
Toast.makeText(this, responseText, Toast.LENGTH_LONG).show() 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()
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
} else {
Toast.makeText(this, responseText, Toast.LENGTH_LONG).show()
}
} }
} }
@ -56,25 +61,29 @@ class GroupActivity : AppCompatActivity() {
val groupName = binding.groupNameTextEdit.text.toString() val groupName = binding.groupNameTextEdit.text.toString()
val groupPassword = binding.groupPasswordTextEdit.text.toString() val groupPassword = binding.groupPasswordTextEdit.text.toString()
val n = Net() val net = Net()
n.language = sharedPreferences.getString("language", "en-US")!! net.language = sharedPreferences.getString("language", "en-US")!!
n.server = sharedPreferences.getString("server", "")!! net.server = sharedPreferences.getString("server", "")!!
n.token = sharedPreferences.getString("token", "")!! net.token = sharedPreferences.getString("token", "")!!
val groupId = n.getGroupId(groupName).toInt() if (!net.serverIsAvailable(this)) {
val response = n.joinGroup(groupId, groupPassword) noInternetConnectionAvailableNotification(this)
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()
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
} else { } else {
Toast.makeText(this, responseText, Toast.LENGTH_LONG).show() val groupId = net.getGroupId(groupName).toInt()
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()
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
} else {
Toast.makeText(this, responseText, Toast.LENGTH_LONG).show()
}
} }
} }
} }

View File

@ -10,6 +10,7 @@ import androidx.security.crypto.MasterKeys
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.Net
import org.foxarmy.barcodescannerforemployees.R import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.databinding.ActivityLoginBinding import org.foxarmy.barcodescannerforemployees.databinding.ActivityLoginBinding
import org.foxarmy.barcodescannerforemployees.noInternetConnectionAvailableNotification
import org.foxarmy.barcodescannerforemployees.parseArray import org.foxarmy.barcodescannerforemployees.parseArray
import org.json.JSONObject import org.json.JSONObject
@ -40,31 +41,35 @@ class LoginActivity : AppCompatActivity() {
val language = resources.getStringArray(R.array.languages)[binding.languageSpinner.selectedItemPosition] val language = resources.getStringArray(R.array.languages)[binding.languageSpinner.selectedItemPosition]
sharedPreferences.edit().putString("language", language).apply() sharedPreferences.edit().putString("language", language).apply()
val n = Net() val net = Net()
n.language = sharedPreferences.getString("language", "en-US")!! net.language = sharedPreferences.getString("language", "en-US")!!
n.server = server net.server = server
val response = n.login(username, password) if (!net.serverIsAvailable(this)) {
val responseText = response.body!!.string() noInternetConnectionAvailableNotification(this)
if (response.code != 200) {
Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show()
} else { } else {
val json = JSONObject(responseText) val response = net.login(username, password)
sharedPreferences.edit().putString("token", json["token"].toString()).apply() val responseText = response.body!!.string()
n.token = json["token"].toString() if (response.code != 200) {
sharedPreferences.edit().putInt("userId", json["id"].toString().toInt()).apply() Toast.makeText(this, responseText, Toast.LENGTH_SHORT).show()
sharedPreferences.edit().putString("server", server).apply() } 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 r = n.getMyGroups().body!!.string() val r = net.getMyGroups().body!!.string()
val myGroups = parseArray(r).map { a -> a.toString()} val myGroups = parseArray(r).map { a -> a.toString()}
sharedPreferences.edit().putStringSet("groups", myGroups.toSet()).apply() sharedPreferences.edit().putStringSet("groups", myGroups.toSet()).apply()
sharedPreferences.edit().putString("currentGroup", myGroups[0]).apply() sharedPreferences.edit().putString("currentGroup", myGroups[0]).apply()
val intent = Intent(this, MainActivity::class.java) val intent = Intent(this, MainActivity::class.java)
startActivity(intent) startActivity(intent)
finish() finish()
}
} }
} }
@ -76,22 +81,27 @@ class LoginActivity : AppCompatActivity() {
sharedPreferences.edit().putString("language", language).apply() sharedPreferences.edit().putString("language", language).apply()
sharedPreferences.edit().putString("server", server).apply() sharedPreferences.edit().putString("server", server).apply()
val n = Net() val net = Net()
n.language = language net.language = language
n.server = server net.server = server
val response = n.registerAccount(username, password);
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()
val token = JSONObject(n.login(username, password).body!!.string())["token"].toString()
sharedPreferences.edit().putString("token", token).apply()
sharedPreferences.edit().putString("server", server).apply() if (!net.serverIsAvailable(this)) {
val intent = Intent(this, GroupActivity::class.java) noInternetConnectionAvailableNotification(this)
startActivity(intent) } else {
finish() val response = net.registerAccount(username, password);
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()
val token = JSONObject(net.login(username, password).body!!.string())["token"].toString()
sharedPreferences.edit().putString("token", token).apply()
sharedPreferences.edit().putString("server", server).apply()
val intent = Intent(this, GroupActivity::class.java)
startActivity(intent)
finish()
}
} }
} }
} }

View File

@ -16,10 +16,7 @@ import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys import androidx.security.crypto.MasterKeys
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import org.foxarmy.barcodescannerforemployees.Net import org.foxarmy.barcodescannerforemployees.*
import org.foxarmy.barcodescannerforemployees.R
import org.foxarmy.barcodescannerforemployees.ViewPagerAdapter
import org.foxarmy.barcodescannerforemployees.convertToUnixEpochTimestamp
import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO import org.foxarmy.barcodescannerforemployees.database.AbstractProductDAO
import org.foxarmy.barcodescannerforemployees.database.CategoryDAO import org.foxarmy.barcodescannerforemployees.database.CategoryDAO
import org.foxarmy.barcodescannerforemployees.database.DBStorageController import org.foxarmy.barcodescannerforemployees.database.DBStorageController
@ -60,9 +57,16 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
actionBarDrawerToggle!!.syncState() actionBarDrawerToggle!!.syncState()
binding.navView.setNavigationItemSelectedListener(this) binding.navView.setNavigationItemSelectedListener(this)
// to make the Navigation drawer icon always appear on the action bar
supportActionBar!!.setDisplayHomeAsUpEnabled(true) supportActionBar!!.setDisplayHomeAsUpEnabled(true)
val sharedPreferences = EncryptedSharedPreferences.create(
"sensitive",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
binding.expiryCalendarFab.setOnClickListener { _ -> binding.expiryCalendarFab.setOnClickListener { _ ->
val expiryCalendarIntent = Intent(this, ExpiryCalendarActivity::class.java) val expiryCalendarIntent = Intent(this, ExpiryCalendarActivity::class.java)
val extras = Bundle() val extras = Bundle()
@ -78,8 +82,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
"StorageFragment" -> { "StorageFragment" -> {
val addAbstractProductIntent = Intent(this, AddAbstractProductActivity::class.java) val addAbstractProductIntent = Intent(this, AddAbstractProductActivity::class.java)
val extras = Bundle() val extras = Bundle()
// I reuse the same stuff for editing and adding new product.
// if abstractProduct == null, it means that we need to create new object
extras.putParcelable("abstractProduct", null) extras.putParcelable("abstractProduct", null)
extras.putString("action", "new") extras.putString("action", "new")
addAbstractProductIntent.putExtras(extras) addAbstractProductIntent.putExtras(extras)
@ -104,7 +106,14 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
} }
} }
} }
synchronize() val net = Net()
net.server = sharedPreferences.getString("server", "")!!
if (!net.serverIsAvailable(this)) {
noInternetConnectionAvailableNotification(this)
} else {
synchronize()
}
} }
private fun synchronize() { private fun synchronize() {

View File

@ -31,8 +31,7 @@ class NavigatorActivity : Activity() {
if (!isAuthenticated()) { if (!isAuthenticated()) {
intent = Intent(this, LoginActivity::class.java); intent = Intent(this, LoginActivity::class.java);
} }
startActivity(intent); startActivity(intent);
finish(); finish();
} }

View File

@ -114,6 +114,9 @@
<string name="image_compress_factor">Степень сжатия изображения</string> <string name="image_compress_factor">Степень сжатия изображения</string>
<string name="current_group">Текущая группа</string> <string name="current_group">Текущая группа</string>
<string name="cancel">Отмена</string> <string name="cancel">Отмена</string>
<string name="no_internet_connection">No internet connection available. Please, connect to a network.</string>
<string name="ok">Ok</string>
<string name="logout">Log out</string>
<string-array name="languages"> <string-array name="languages">
<item>en-US</item> <item>en-US</item>
<item>ru-RU</item> <item>ru-RU</item>

View File

@ -112,6 +112,9 @@
<string name="image_compress_factor">Image compression factor</string> <string name="image_compress_factor">Image compression factor</string>
<string name="current_group">Current group</string> <string name="current_group">Current group</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="no_internet_connection">No internet connection available. Please, connect to a network.</string>
<string name="ok">Ok</string>
<string name="logout">Log out</string>
<string-array name="languages"> <string-array name="languages">
<item>en-US</item> <item>en-US</item>
<item>ru-RU</item> <item>ru-RU</item>