diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f063f1ceb837032872c0b40aa8d1b09c07596ea..bfacbd1d614984c5ebe50aa134ff479140f54db6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,14 +2,16 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> - <uses-feature android:name="android.hardware.camera" android:required="false" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> @@ -53,8 +55,7 @@ android:name=".service.jwt.JwtService" android:enabled="true" android:exported="true" - android:permission="android.permission.INTERNET"> - </service> + android:permission="android.permission.INTERNET"></service> <receiver android:name=".ui.transactions.TransactionsBroadcastReceiver" @@ -64,8 +65,8 @@ <activity android:name=".ui.login.LoginActivity" - android:screenOrientation="portrait" - android:exported="true"> + android:exported="true" + android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -75,8 +76,7 @@ <activity android:name=".MainActivity" - android:exported="true"> - </activity> + android:exported="true"></activity> </application> diff --git a/app/src/main/java/com/example/bondoyap/MainActivity.kt b/app/src/main/java/com/example/bondoyap/MainActivity.kt index f99a955ca06223a0f5dc78aba72f6cc75a909984..5ac8d1fb73b41a0e5ace944a96de93b533fa7169 100644 --- a/app/src/main/java/com/example/bondoyap/MainActivity.kt +++ b/app/src/main/java/com/example/bondoyap/MainActivity.kt @@ -5,30 +5,31 @@ import android.content.Intent import android.content.IntentFilter import android.content.SharedPreferences import android.os.Bundle +import android.widget.Toast import androidx.activity.viewModels -import com.google.android.material.bottomnavigation.BottomNavigationView import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.ContentProviderCompat.requireContext -import androidx.fragment.app.viewModels import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.navigation.findNavController import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupWithNavController -import com.example.bondoyap.service.api.Constants.SHARED_PREFS_NAME import com.example.bondoyap.databinding.ActivityMainBinding +import com.example.bondoyap.service.NetworkObserver import com.example.bondoyap.service.api.Constants +import com.example.bondoyap.service.api.Constants.SHARED_PREFS_NAME import com.example.bondoyap.service.jwt.JwtService import com.example.bondoyap.ui.login.LoginActivity import com.example.bondoyap.ui.transactions.TransactionsApplication import com.example.bondoyap.ui.transactions.TransactionsBroadcastReceiver import com.example.bondoyap.ui.transactions.TransactionsViewModel import com.example.bondoyap.ui.transactions.TransactionsViewModelFactory +import com.google.android.material.bottomnavigation.BottomNavigationView class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var sharedPreferences: SharedPreferences + private lateinit var networkObserver: NetworkObserver private lateinit var transactionsBroadcastReceiver: TransactionsBroadcastReceiver private val transactionsViewModel: TransactionsViewModel by viewModels { @@ -40,6 +41,15 @@ class MainActivity : AppCompatActivity() { sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) + networkObserver = NetworkObserver(applicationContext) + networkObserver.isConnected.observe(this) { isConnected -> + if (isConnected) { + Toast.makeText(this, "Koneksi terhubung", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(this, "Koneksi terputus", Toast.LENGTH_SHORT).show() + } + } + if (!isLoggedIn()) { val intent = Intent(this, LoginActivity::class.java) startActivity(intent) @@ -52,13 +62,17 @@ class MainActivity : AppCompatActivity() { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + val navView: BottomNavigationView = binding.navView val navController = findNavController(R.id.nav_host_fragment_activity_main) - val appBarConfiguration = AppBarConfiguration(setOf( + val appBarConfiguration = AppBarConfiguration( + setOf( R.id.navigation_settings, R.id.navigation_transactions, R.id.navigation_scanner, - R.id.navigation_graph)) + R.id.navigation_graph + ) + ) setupActionBarWithNavController(navController, appBarConfiguration) @@ -66,13 +80,15 @@ class MainActivity : AppCompatActivity() { transactionsBroadcastReceiver = TransactionsBroadcastReceiver(transactionsViewModel) val filter = IntentFilter(Constants.ACTION_RANDOMIZE_TRANSACTIONS) - LocalBroadcastManager.getInstance(this).registerReceiver(transactionsBroadcastReceiver, filter) + LocalBroadcastManager.getInstance(this) + .registerReceiver(transactionsBroadcastReceiver, filter) } override fun onSupportNavigateUp(): Boolean { val navController = findNavController(R.id.nav_host_fragment_activity_main) return navController.navigateUp() || super.onSupportNavigateUp() } + private fun isLoggedIn(): Boolean { return sharedPreferences.getBoolean("isLoggedIn", false) } diff --git a/app/src/main/java/com/example/bondoyap/service/NetworkObserver.kt b/app/src/main/java/com/example/bondoyap/service/NetworkObserver.kt new file mode 100644 index 0000000000000000000000000000000000000000..d350eaa65f1d266a1d345290355340ba3c19f8b9 --- /dev/null +++ b/app/src/main/java/com/example/bondoyap/service/NetworkObserver.kt @@ -0,0 +1,28 @@ +package com.example.bondoyap.service + +import android.content.Context +import android.net.ConnectivityManager +import android.net.Network +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData + +class NetworkObserver(context: Context) { + private val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + private val _isConnected = MutableLiveData<Boolean>() + val isConnected: LiveData<Boolean> = _isConnected + + init { + connectivityManager.registerDefaultNetworkCallback(object : + ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + _isConnected.postValue(true) + } + + override fun onLost(network: Network) { + _isConnected.postValue(false) + } + }) + } +} diff --git a/app/src/main/java/com/example/bondoyap/ui/scanner/ScannerFragment.kt b/app/src/main/java/com/example/bondoyap/ui/scanner/ScannerFragment.kt index 9d0ede0700d3679aa34b33f69bcffb9d5aa516fb..fb1c14b2dc93bc7534455625b9b5949a5e9bc338 100644 --- a/app/src/main/java/com/example/bondoyap/ui/scanner/ScannerFragment.kt +++ b/app/src/main/java/com/example/bondoyap/ui/scanner/ScannerFragment.kt @@ -11,6 +11,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.camera.core.CameraSelector @@ -25,6 +26,7 @@ import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import com.example.bondoyap.R import com.example.bondoyap.databinding.FragmentScannerBinding +import com.example.bondoyap.service.NetworkObserver import com.example.bondoyap.service.api.ApiClient import com.example.bondoyap.service.api.data.BillResponse import com.example.bondoyap.service.api.data.Items @@ -44,6 +46,7 @@ class ScannerFragment : Fragment() { private var _binding: FragmentScannerBinding? = null private val binding get() = _binding!! private lateinit var navController: NavController + private lateinit var networkObserver: NetworkObserver // Camera private var isBackCamera = true @@ -71,6 +74,8 @@ class ScannerFragment : Fragment() { val root: View = binding.root navController = findNavController() + networkObserver = NetworkObserver(requireContext()) + cacheDir = requireContext().cacheDir contentResolver = requireContext().contentResolver apiClient = ApiClient() @@ -86,7 +91,9 @@ class ScannerFragment : Fragment() { changeImage = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { - if (it.resultCode == Activity.RESULT_OK) { + if (networkObserver.isConnected.value == false) { + Toast.makeText(requireContext(), "Tidak ada koneksi", Toast.LENGTH_SHORT).show() + } else if (it.resultCode == Activity.RESULT_OK) { try { val data = it.data val imgUri = data?.data ?: throw Exception("Image Uri is null") @@ -137,7 +144,11 @@ class ScannerFragment : Fragment() { binding.uploadButton.isClickable = false binding.uploadButton.setOnClickListener { - cameraImageFile?.let { it1 -> uploadPhoto(it1) } + if (networkObserver.isConnected.value == false) { + Toast.makeText(requireContext(), "Tidak ada koneksi", Toast.LENGTH_SHORT).show() + } else { + cameraImageFile?.let { it1 -> uploadPhoto(it1) } + } } return root diff --git a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt index 5cef2c9ca4362d231fbfdf8fcfd4029621ae23f4..0e1b676e526758a09a346f67685df2147dcb20cd 100644 --- a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt @@ -1,11 +1,11 @@ package com.example.bondoyap.ui.settings +import EmailHelper +import android.Manifest import android.app.AlertDialog import android.content.DialogInterface import android.content.Intent import android.content.pm.PackageManager -import EmailHelper -import android.content.Intent import android.net.Uri import android.os.Bundle import android.util.Log @@ -21,19 +21,21 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.example.bondoyap.databinding.FragmentSettingsBinding +import com.example.bondoyap.service.NetworkObserver import com.example.bondoyap.service.api.Constants.ACTION_RANDOMIZE_TRANSACTIONS import com.example.bondoyap.ui.login.LoginActivity import com.example.bondoyap.ui.transactions.TransactionsApplication import com.example.bondoyap.ui.transactions.TransactionsViewModel import com.example.bondoyap.ui.transactions.TransactionsViewModelFactory -import android.Manifest import java.io.File class SettingsFragment : Fragment() { private var _binding: FragmentSettingsBinding? = null private val binding get() = _binding!! + private lateinit var settingsViewModel: SettingsViewModel + private lateinit var networkObserver: NetworkObserver private val transactionsViewModel: TransactionsViewModel by viewModels { TransactionsViewModelFactory((requireContext().applicationContext as TransactionsApplication).repository) @@ -47,9 +49,18 @@ class SettingsFragment : Fragment() { savedInstanceState: Bundle? ): View { _binding = FragmentSettingsBinding.inflate(inflater, container, false) - - if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE) + networkObserver = NetworkObserver(requireContext()) + + if (ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) != PackageManager.PERMISSION_GRANTED + ) { + ActivityCompat.requestPermissions( + requireActivity(), + arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), + PERMISSION_REQUEST_CODE + ) } return binding.root @@ -106,8 +117,16 @@ class SettingsFragment : Fragment() { // exporter = context?.let { TransactionsExporter(database, it) }!! saveButton.setOnClickListener { - if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - Toast.makeText(appContext, "Allow storage permission untuk menyimpan transaksi ke file xls/xlsx", Toast.LENGTH_SHORT).show() + if (ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) != PackageManager.PERMISSION_GRANTED + ) { + Toast.makeText( + appContext, + "Allow storage permission untuk menyimpan transaksi ke file xls/xlsx", + Toast.LENGTH_SHORT + ).show() } else { val formats = arrayOf("XLS", "XLSX") val builder = AlertDialog.Builder(requireContext()) @@ -115,14 +134,31 @@ class SettingsFragment : Fragment() { builder.setItems(formats) { dialog: DialogInterface, which: Int -> when (which) { 0 -> { - Toast.makeText(appContext, "Menyimpan transaksi ke xls...", Toast.LENGTH_SHORT).show() + Toast.makeText( + appContext, + "Menyimpan transaksi ke xls...", + Toast.LENGTH_SHORT + ).show() exporter.exportToXLS() - Toast.makeText(appContext, "Penyimpanan xls pada folder Documents berhasil...", Toast.LENGTH_SHORT).show() + Toast.makeText( + appContext, + "Penyimpanan xls pada folder Documents berhasil...", + Toast.LENGTH_SHORT + ).show() } + 1 -> { - Toast.makeText(appContext, "Menyimpan transaksi ke xlsx...", Toast.LENGTH_SHORT).show() + Toast.makeText( + appContext, + "Menyimpan transaksi ke xlsx...", + Toast.LENGTH_SHORT + ).show() exporter.exportToXLSX() - Toast.makeText(appContext, "Penyimpanan xlsx pada folder Documents berhasil...", Toast.LENGTH_SHORT).show() + Toast.makeText( + appContext, + "Penyimpanan xlsx pada folder Documents berhasil...", + Toast.LENGTH_SHORT + ).show() } } dialog.dismiss() @@ -132,26 +168,38 @@ class SettingsFragment : Fragment() { } sendButton.setOnClickListener { - Toast.makeText(appContext, "Mengirimkan transaksi ...", Toast.LENGTH_SHORT).show() - - var recipientEmail: String - val subject = "Transaksi Aplikasi Bondoman" - val message = "Halo, Berikut adalah detail transaksi aplikasi Bondoman.\n " + - "File Transaksi terlampir." - - settingsViewModel.getUser()?.let { user -> - recipientEmail = user.email - val attachment = getAttachmentUri("transactions.xls") - Log.d("Attachment", "Attachment URI: $attachment") - context?.let { EmailHelper(it) }?.sendGmail(recipientEmail, subject, message, attachment) + if (networkObserver.isConnected.value == false) { + Toast.makeText(requireContext(), "Tidak ada koneksi", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(appContext, "Mengirimkan transaksi ...", Toast.LENGTH_SHORT).show() + + var recipientEmail: String + val subject = "Transaksi Aplikasi Bondoman" + val message = "Halo, Berikut adalah detail transaksi aplikasi Bondoman.\n " + + "File Transaksi terlampir." + + settingsViewModel.getUser()?.let { user -> + recipientEmail = user.email + val attachment = getAttachmentUri("transactions.xls") + Log.d("Attachment", "Attachment URI: $attachment") + context?.let { EmailHelper(it) } + ?.sendGmail(recipientEmail, subject, message, attachment) + } } } - resetButton.setOnClickListener{ + resetButton.setOnClickListener { Toast.makeText(appContext, "Transaksi di-reset ...", Toast.LENGTH_SHORT).show() - showConfirmationDialog("Reset", "Apakah Anda yakin ingin me-reset data transaksi?") { + showConfirmationDialog( + "Reset", + "Apakah Anda yakin ingin me-reset data transaksi?" + ) { transactionsViewModel.deleteAllTransactions() - Toast.makeText(appContext, "Transaksi berhasil di-reset ...", Toast.LENGTH_SHORT).show() + Toast.makeText( + appContext, + "Transaksi berhasil di-reset ...", + Toast.LENGTH_SHORT + ).show() } } @@ -195,7 +243,11 @@ class SettingsFragment : Fragment() { Log.d("Attachment", "File path: ${fileCache.absolutePath}") - return FileProvider.getUriForFile(requireContext(), "${requireContext().packageName}.provider", fileCache) + return FileProvider.getUriForFile( + requireContext(), + "${requireContext().packageName}.provider", + fileCache + ) } override fun onDestroyView() {