From 6e772e0198b361654aa18ae325b4f579d167184e Mon Sep 17 00:00:00 2001
From: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id>
Date: Tue, 2 Apr 2024 12:32:11 +0700
Subject: [PATCH] feat: network sensing on scan, map, send

---
 .../nerbos/fragments/scan/ScanFragment.kt     | 70 +++++++++++--------
 .../transaction/TransactionFragment.kt        | 36 +++++++---
 .../example/nerbos/service/Authentication.kt  | 20 ++----
 .../nerbos/service/NetworkManagerService.kt   |  8 +++
 4 files changed, 78 insertions(+), 56 deletions(-)

diff --git a/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt b/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt
index a47dea4..f4c1d9c 100644
--- a/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt
+++ b/app/src/main/java/com/example/nerbos/fragments/scan/ScanFragment.kt
@@ -39,6 +39,7 @@ import com.example.nerbos.R
 import com.example.nerbos.model.Transaction
 import com.example.nerbos.model.TransactionCategory
 import com.example.nerbos.service.Authentication
+import com.example.nerbos.service.NetworkManagerService
 import com.example.nerbos.viewmodel.TransactionViewModel
 import com.google.android.gms.location.FusedLocationProviderClient
 import com.google.android.gms.location.LocationServices
@@ -66,6 +67,7 @@ class ScanFragment : Fragment() {
     private lateinit var imageCapture: ImageCapture
     private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
     private lateinit var geocoder: Geocoder
+    private var networkManagerService: NetworkManagerService = NetworkManagerService()
     private var fragmentContext: Context? = null
 
     private val requestCameraPermissionCode = Manifest.permission.CAMERA
@@ -146,36 +148,40 @@ class ScanFragment : Fragment() {
     }
 
     private fun takePicture() {
-        val imageCapture = imageCapture
-
-        // Create output file to hold the captured image
-        val externalFilesDirs = requireContext().getExternalFilesDirs(null)
-        val photoFile = File(externalFilesDirs.firstOrNull(), "${System.currentTimeMillis()}.jpg")
-
-        // Setup image capture metadata
-        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
-
-        // Capture the image
-        imageCapture.takePicture(
-            outputOptions,
-            ContextCompat.getMainExecutor(requireContext()),
-            object : ImageCapture.OnImageSavedCallback {
-                override fun onError(exc: ImageCaptureException) {
-                    Toast.makeText(requireContext(), "Error capturing image: ${exc.message}", Toast.LENGTH_SHORT).show()
-                }
+        if (!networkManagerService.isNetworkAvailable(requireContext())) {
+            Toast.makeText(requireContext(), "No internet connection, this feature requires internet connection", Toast.LENGTH_SHORT).show()
+        } else {
+            val imageCapture = imageCapture
+
+            // Create output file to hold the captured image
+            val externalFilesDirs = requireContext().getExternalFilesDirs(null)
+            val photoFile = File(externalFilesDirs.firstOrNull(), "${System.currentTimeMillis()}.jpg")
+
+            // Setup image capture metadata
+            val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
+
+            // Capture the image
+            imageCapture.takePicture(
+                outputOptions,
+                ContextCompat.getMainExecutor(requireContext()),
+                object : ImageCapture.OnImageSavedCallback {
+                    override fun onError(exc: ImageCaptureException) {
+                        Toast.makeText(requireContext(), "Error capturing image: ${exc.message}", Toast.LENGTH_SHORT).show()
+                    }
 
-                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
-                    // Image captured successfully, read orientation metadata
-                    val savedUri = photoFile.toUri()
-                    val imageBitmap = BitmapFactory.decodeFile(savedUri.path)
-                    val rotatedBitmap = savedUri.path?.let {
-                        rotateImageIfRequired(imageBitmap,
-                            it
-                        )
+                    override fun onImageSaved(output: ImageCapture.OutputFileResults) {
+                        // Image captured successfully, read orientation metadata
+                        val savedUri = photoFile.toUri()
+                        val imageBitmap = BitmapFactory.decodeFile(savedUri.path)
+                        val rotatedBitmap = savedUri.path?.let {
+                            rotateImageIfRequired(imageBitmap,
+                                it
+                            )
+                        }
+                        showImagePopup(rotatedBitmap)
                     }
-                    showImagePopup(rotatedBitmap)
-                }
-            })
+                })
+        }
     }
 
     private fun rotateImageIfRequired(bitmap: Bitmap, path: String): Bitmap {
@@ -197,8 +203,12 @@ class ScanFragment : Fragment() {
     }
 
     private fun dispatchGalleryIntent() {
-        val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
-        requestGallery.launch(galleryIntent)
+        if (!networkManagerService.isNetworkAvailable(requireContext())) {
+            Toast.makeText(requireContext(), "No internet connection, this feature requires internet connection", Toast.LENGTH_SHORT).show()
+        } else {
+            val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
+            requestGallery.launch(galleryIntent)
+        }
     }
 
     private fun showImagePopup(imageBitmap: Bitmap?) {
diff --git a/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt b/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt
index 0b92fd4..45ef16e 100644
--- a/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt
+++ b/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt
@@ -32,6 +32,7 @@ import com.example.nerbos.R
 import com.example.nerbos.model.Transaction
 import com.example.nerbos.model.TransactionCategory
 import com.example.nerbos.service.Authentication
+import com.example.nerbos.service.NetworkManagerService
 import com.example.nerbos.service.Utils
 import com.example.nerbos.viewmodel.TransactionViewModel
 import com.google.android.gms.location.FusedLocationProviderClient
@@ -51,6 +52,7 @@ class TransactionFragment : Fragment() {
     private  lateinit var transactionViewModel: TransactionViewModel
     private lateinit var authentication: Authentication
     private var currentLocation: Location? = null
+    private var networkManagerService: NetworkManagerService = NetworkManagerService()
     private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
     private lateinit var geocoder: Geocoder
     private val permissionCode = 1
@@ -135,19 +137,27 @@ class TransactionFragment : Fragment() {
         }
 
         xlsSend.setOnClickListener {
-            val subject = getString(R.string.email_subject)
-            val message = getString(R.string.email_message)
-            val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xls")
-            sendWorkbook(authentication.getEmail(), subject, message, workbook, "xls")
+            if (!networkManagerService.isNetworkAvailable(requireContext())) {
+                Toast.makeText(requireContext(), "No internet connection, this feature requires internet connection", Toast.LENGTH_SHORT).show()
+            } else {
+                val subject = getString(R.string.email_subject)
+                val message = getString(R.string.email_message)
+                val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xls")
+                sendWorkbook(authentication.getEmail(), subject, message, workbook, "xls")
+            }
             xlsSend.visibility = View.GONE
             xlsxSend.visibility = View.GONE
         }
 
         xlsxSend.setOnClickListener {
-            val subject = getString(R.string.email_subject)
-            val message = getString(R.string.email_message)
-            val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xlsx")
-            sendWorkbook(authentication.getEmail(), subject, message, workbook, "xlsx")
+            if (!networkManagerService.isNetworkAvailable(requireContext())) {
+                Toast.makeText(requireContext(), "No internet connection, this feature requires internet connection", Toast.LENGTH_SHORT).show()
+            } else {
+                val subject = getString(R.string.email_subject)
+                val message = getString(R.string.email_message)
+                val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xlsx")
+                sendWorkbook(authentication.getEmail(), subject, message, workbook, "xlsx")
+            }
             xlsSend.visibility = View.GONE
             xlsxSend.visibility = View.GONE
         }
@@ -253,9 +263,13 @@ class TransactionFragment : Fragment() {
     }
 
     private fun showLocationOnMap(location: String) {
-        val mapIntent = Intent(requireActivity(), MapsActivity::class.java)
-        mapIntent.putExtra("locationName", location)
-        startActivity(mapIntent)
+        if (!networkManagerService.isNetworkAvailable(requireContext())) {
+            Toast.makeText(requireContext(), "No internet connection, this feature requires internet connection", Toast.LENGTH_SHORT).show()
+        } else {
+            val mapIntent = Intent(requireActivity(), MapsActivity::class.java)
+            mapIntent.putExtra("locationName", location)
+            startActivity(mapIntent)
+        }
     }
 
     private fun showAddTransactionDialog() {
diff --git a/app/src/main/java/com/example/nerbos/service/Authentication.kt b/app/src/main/java/com/example/nerbos/service/Authentication.kt
index 6d99233..d197353 100644
--- a/app/src/main/java/com/example/nerbos/service/Authentication.kt
+++ b/app/src/main/java/com/example/nerbos/service/Authentication.kt
@@ -1,13 +1,11 @@
 package com.example.nerbos.service
 
-import android.annotation.SuppressLint
 import android.content.Context
 import android.content.Intent
 import android.content.SharedPreferences
-import android.net.ConnectivityManager
-import android.net.NetworkCapabilities
 import android.security.keystore.KeyGenParameterSpec
 import android.security.keystore.KeyProperties
+import android.util.Base64
 import com.android.volley.Request
 import com.android.volley.toolbox.JsonObjectRequest
 import com.android.volley.toolbox.Volley
@@ -17,7 +15,6 @@ import org.json.JSONObject
 import java.security.KeyPairGenerator
 import java.security.KeyStore
 import javax.crypto.Cipher
-import android.util.Base64
 
 interface AuthCallback {
     fun onSuccess() {
@@ -32,6 +29,8 @@ interface AuthCallback {
 }
 
 class Authentication(private val context: Context) {
+    private var networkManagerService: NetworkManagerService = NetworkManagerService()
+
     private val keyStore: KeyStore = KeyStore.getInstance(context.getString(R.string.android_key_store))
         .apply {
             load(null)
@@ -64,7 +63,7 @@ class Authentication(private val context: Context) {
     // Service: API Authentication for login and token checking
     fun login(email: String, password: String, callback: AuthCallback) {
         // check network availability
-        if (!isNetworkAvailable()) {
+        if (!networkManagerService.isNetworkAvailable(context)) {
             callback.onError(context.getString(R.string.no_internet))
             return
         }
@@ -152,7 +151,7 @@ class Authentication(private val context: Context) {
 
     fun checkToken(callback: AuthCallback) {
         // check network availability
-        if (!isNetworkAvailable()) {
+        if (!networkManagerService.isNetworkAvailable(context)) {
             callback.onError(context.getString(R.string.no_internet))
             return
         }
@@ -228,13 +227,4 @@ class Authentication(private val context: Context) {
         }
         return email.toInt()
     }
-
-    @SuppressLint("ServiceCast")
-    private fun isNetworkAvailable(): Boolean {
-        val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
-        val network = connectivityManager.activeNetwork
-        val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
-        return networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
-    }
-
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/nerbos/service/NetworkManagerService.kt b/app/src/main/java/com/example/nerbos/service/NetworkManagerService.kt
index efe66c3..685c8ec 100644
--- a/app/src/main/java/com/example/nerbos/service/NetworkManagerService.kt
+++ b/app/src/main/java/com/example/nerbos/service/NetworkManagerService.kt
@@ -6,6 +6,7 @@ import android.content.Intent
 import android.content.res.Configuration
 import android.net.ConnectivityManager
 import android.net.Network
+import android.net.NetworkCapabilities
 import android.os.Binder
 import android.os.Handler
 import android.os.IBinder
@@ -118,4 +119,11 @@ class NetworkManagerService : Service() {
         }
         showActionBarAndNavBar(mainActivity)
     }
+
+    fun isNetworkAvailable(context: Context): Boolean {
+        val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+        val network = connectivityManager.activeNetwork
+        val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
+        return networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
+    }
 }
\ No newline at end of file
-- 
GitLab