diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6c05b572ac319727dd3510f1b9d8c0bedf5c9278..a165ea8d65565de7fa3ab5a7e18a16b71e06e51f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -108,4 +108,4 @@ dependencies { //DataBinding kapt ("com.android.databinding:compiler:3.2.0-alpha10") -} +} \ No newline at end of file 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 a47dea4cf9aba8010a01534612431e3c9afd6cb6..f4c1d9c548598f2a0ce589070244d120c0c90651 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 0b92fd4b198c06a21bb55736270b47f77989ff65..be27dae07578b6b13268fd7200efd5e887e30e09 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() { @@ -280,9 +294,12 @@ class TransactionFragment : Fragment() { } dialog.findViewById<ImageView>(R.id.autoFillLocationButton).setOnClickListener { - // TODO: Buat percabangan kalau ada internet - val etLocation= dialog.findViewById<EditText>(R.id.locationInput) - etLocation.setText(getAddressName()) + if (!networkManagerService.isNetworkAvailable(requireContext())) { + Toast.makeText(requireContext(), "No internet connection, this feature requires internet connection", Toast.LENGTH_SHORT).show() + } else { + val etLocation= dialog.findViewById<EditText>(R.id.locationInput) + etLocation.setText(getAddressName()) + } } dialog.show() @@ -326,8 +343,11 @@ class TransactionFragment : Fragment() { transactionDate.text = Utils.formatDate(transaction.date.toString()) dialog.findViewById<ImageView>(R.id.autoFillLocationButton).setOnClickListener { - // TODO: Buat percabangan kalau ada internet - etLocation.setText(getAddressName()) + if (!networkManagerService.isNetworkAvailable(requireContext())) { + Toast.makeText(requireContext(), "No internet connection, this feature requires internet connection", Toast.LENGTH_SHORT).show() + } else { + etLocation.setText(getAddressName()) + } } dialog.findViewById<Button>(R.id.saveButton).setOnClickListener{ 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 2e691dd88d10f86095472bdae4e1d6295e44dfaf..d19735372ddb17ae24630310e730e5f496dc6527 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 } @@ -150,10 +149,9 @@ class Authentication(private val context: Context) { return String(decryptedToken, Charsets.UTF_8) } - fun checkToken(callback: AuthCallback) { // check network availability - if (!isNetworkAvailable()) { + if (!networkManagerService.isNetworkAvailable(context)) { callback.onError(context.getString(R.string.no_internet)) return } @@ -229,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 efe66c38c960c6785b01b9462555458bad58f0cd..685c8ec8c6289cb271b7a9d4c4d5f3fd264a3c30 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 diff --git a/app/src/main/res/layout/activity_maps.xml b/app/src/main/res/layout/activity_maps.xml index 0b747601e89149335c41b9ace303b1ca02ab6dfa..ccb06b56041dd0e9000a666b446190f622327549 100644 --- a/app/src/main/res/layout/activity_maps.xml +++ b/app/src/main/res/layout/activity_maps.xml @@ -17,6 +17,7 @@ android:layout_height="match_parent" map:layout_constraintTop_toTopOf="parent" /> + <TextView android:id="@+id/title" android:layout_width="match_parent" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dfc7cc405f38a0a3c3f91d166ae34a8950aad4d4..cc66f87f61f9e12f668d8f4a13303c42204bc2ea 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,7 +21,6 @@ <string name="android_key_store">AndroidKeyStore</string> <string name="authorization">Authorization</string> <string name="bearer">Bearer</string> - <string name="no_internet">No Internet Connection</string> <string name="server_error">Server Error</string> <string name="preferences">NosPreferences</string>