diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index dc106e148512297655d63fc98208564322a70715..b774c4bc44a0116ead0d2182d76a79fcec035292 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -6,7 +6,31 @@ <State /> </entry> <entry key="app"> - <State /> + <State> + <targetSelectedWithDropDown> + <Target> + <type value="QUICK_BOOT_TARGET" /> + <deviceKey> + <Key> + <type value="VIRTUAL_DEVICE_PATH" /> + <value value="C:\Users\ASUS\.android\avd\Pixel_7_API_31.avd" /> + </Key> + </deviceKey> + </Target> + </targetSelectedWithDropDown> + <timeTargetWasSelectedWithDropDown value="2024-04-01T07:28:26.326300300Z" /> + <targetsSelectedWithDialog> + <Target> + <type value="QUICK_BOOT_TARGET" /> + <deviceKey> + <Key> + <type value="VIRTUAL_DEVICE_PATH" /> + <value value="C:\Users\ASUS\.android\avd\Pixel_7_API_31.avd" /> + </Key> + </deviceKey> + </Target> + </targetsSelectedWithDialog> + </State> </entry> </value> </component> diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0614b62466727c22b2b769a378f3ccbc781c2fcd..a5035dc94adabcf6c2b2a7dde1185e89da8329aa 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -113,6 +113,10 @@ dependencies { testImplementation("junit:junit:4.13.2") testImplementation("org.mockito:mockito-core:3.12.4") + // Location + implementation("com.google.android.gms:play-services-location:18.0.0") + + //okHttp implementation("com.squareup.okhttp3:okhttp:4.12.0") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2647727d9d538f0487b41714f03143cb1a1c442d..c5ac1b1b316889f8294e7641ea5dddee5bc53f6d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,9 @@ <uses-feature android:name="android.hardware.camera" android:required="true"/> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" diff --git a/app/src/main/java/com/example/pbd_jwr/LoginActivity.kt b/app/src/main/java/com/example/pbd_jwr/LoginActivity.kt index d19b2db02b4ef1b009c61119d5ccc7a45072d17b..2e183a2b9d12873b0aa9ebe904fc57e129574f8a 100644 --- a/app/src/main/java/com/example/pbd_jwr/LoginActivity.kt +++ b/app/src/main/java/com/example/pbd_jwr/LoginActivity.kt @@ -55,7 +55,7 @@ class LoginActivity : AppCompatActivity() { if (passwordRemembered != null && emailRemembered != null) { post(emailRemembered,passwordRemembered,true) } - return + } connectivityManager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager diff --git a/app/src/main/java/com/example/pbd_jwr/MainActivity.kt b/app/src/main/java/com/example/pbd_jwr/MainActivity.kt index 131c0cba97435bcd32f9c8b1084cac50543d3166..05270e61b9678fff108ad3faf6d6e4835bc0ad5b 100644 --- a/app/src/main/java/com/example/pbd_jwr/MainActivity.kt +++ b/app/src/main/java/com/example/pbd_jwr/MainActivity.kt @@ -6,9 +6,15 @@ import android.content.SharedPreferences import android.content.SharedPreferences.Editor import android.net.ConnectivityManager import android.os.Bundle +import android.content.pm.PackageManager +import android.location.LocationManager +import android.widget.Toast +import android.Manifest import com.google.android.material.bottomnavigation.BottomNavigationView import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import androidx.navigation.findNavController import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.setupActionBarWithNavController @@ -41,6 +47,10 @@ class MainActivity : AppCompatActivity() { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + if (!isLocationPermissionGranted()) { + requestLocationPermission() + } + val navView: BottomNavigationView = binding.navView navView.background=null; @@ -49,7 +59,7 @@ class MainActivity : AppCompatActivity() { // menu should be considered as top level destinations. val appBarConfiguration = AppBarConfiguration( setOf( - R.id.navigation_transaction, R.id.navigation_dashboard,R.id.navigation_scan, R.id.navigation_settings + R.id.navigation_transaction, R.id.navigation_dashboard, R.id.navigation_settings ) ) setupActionBarWithNavController(navController, appBarConfiguration) @@ -93,6 +103,54 @@ class MainActivity : AppCompatActivity() { } + private fun isLocationPermissionGranted(): Boolean { + return ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + } + +// Request location permission + private fun requestLocationPermission() { + ActivityCompat.requestPermissions( + this, + arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ), + LOCATION_PERMISSION_REQUEST_CODE + ) + } + + // Handle the result of the permission request + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array<out String>, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // Permission granted, save the status + saveLocationPermissionGranted() + } else { + // Permission denied, inform the user + Toast.makeText( + this, + "Location permission denied. Some functionality may be limited.", + Toast.LENGTH_SHORT + ).show() + } + } + } + + private fun saveLocationPermissionGranted() { + val sharedPreferences = getSharedPreferences("my_preferences", Context.MODE_PRIVATE) + val editor = sharedPreferences.edit() + editor.putBoolean("location_permission_granted", true) + editor.apply() + } + override fun onDestroy() { super.onDestroy() val serviceIntent = Intent(this, JWTValidationService::class.java) @@ -108,4 +166,8 @@ class MainActivity : AppCompatActivity() { private fun unregisterNetworkCallback() { connectivityManager.unregisterNetworkCallback(networkCallback) } + + companion object { + private const val LOCATION_PERMISSION_REQUEST_CODE = 1001 + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/pbd_jwr/data/database/AppDatabase.kt b/app/src/main/java/com/example/pbd_jwr/data/database/AppDatabase.kt index 98b1ba76fb792137668a06be3910fee86093dfe5..35cda16e708444f7737a9dfdc36f03ebbdc27e96 100644 --- a/app/src/main/java/com/example/pbd_jwr/data/database/AppDatabase.kt +++ b/app/src/main/java/com/example/pbd_jwr/data/database/AppDatabase.kt @@ -75,8 +75,45 @@ abstract class AppDatabase : RoomDatabase() { location = "Location 2", date = System.currentTimeMillis() ) + val transaction3 = Transaction( + userId = user2.id, + title = "Transaction 3", + category = "Category 3", + amount = 300.0, + location = "Location 3", + date = System.currentTimeMillis() + ) + val transaction4 = Transaction( + userId = user2.id, + title = "Transaction 4", + category = "Category 4", + amount = 400.0, + location = "Location 4", + date = System.currentTimeMillis() + ) + val transaction5 = Transaction( + userId = user1.id, + title = "Transaction 5", + category = "Category 5", + amount = 500.0, + location = "Location 5", + date = System.currentTimeMillis() + ) + val transaction6 = Transaction( + userId = user1.id, + title = "Transaction 6", + category = "Category 6", + amount = 600.0, + location = "Location 6", + date = System.currentTimeMillis() + ) transactionDao.addTransaction(transaction1) transactionDao.addTransaction(transaction2) + transactionDao.addTransaction(transaction3) + transactionDao.addTransaction(transaction4) + transactionDao.addTransaction(transaction5) + transactionDao.addTransaction(transaction6) + } } } diff --git a/app/src/main/java/com/example/pbd_jwr/data/entity/Transaction.kt b/app/src/main/java/com/example/pbd_jwr/data/entity/Transaction.kt index 48313817923808d829e5da0f5f1e5e767242ffd3..0e68ab499f680c3fe2e0f087c132dc0bad36e866 100644 --- a/app/src/main/java/com/example/pbd_jwr/data/entity/Transaction.kt +++ b/app/src/main/java/com/example/pbd_jwr/data/entity/Transaction.kt @@ -8,8 +8,7 @@ import androidx.room.PrimaryKey @Parcelize @Entity(tableName = "transaction") data class Transaction( - @PrimaryKey(autoGenerate = true) - val id: Long = 0, + @PrimaryKey(autoGenerate = true) val id: Long = 0, val userId: Long, val title: String, val category: String, diff --git a/app/src/main/java/com/example/pbd_jwr/network/NetworkCallback.kt b/app/src/main/java/com/example/pbd_jwr/network/NetworkCallback.kt index 40c8467b5b6bcaf9014b310ad40eaaeeb7ccc2d8..ecf6e2c1e44d8e04fa7547559d456906a7633c9c 100644 --- a/app/src/main/java/com/example/pbd_jwr/network/NetworkCallback.kt +++ b/app/src/main/java/com/example/pbd_jwr/network/NetworkCallback.kt @@ -10,14 +10,14 @@ import android.widget.Toast import androidx.annotation.RequiresApi class NetworkCallbackImplementation(private val context: Context) : ConnectivityManager.NetworkCallback() { - private var firstLoad = true + private var isFirstLoad = true override fun onAvailable(network: Network) { super.onAvailable(network) val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val capabilities = connectivityManager.getNetworkCapabilities(network) - if(!firstLoad){ + if(!isFirstLoad){ capabilities?.let { if (it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { @@ -33,12 +33,15 @@ class NetworkCallbackImplementation(private val context: Context) : Connectivity } } }else{ - firstLoad = false + isFirstLoad = false } } override fun onLost(network: Network) { super.onLost(network) + if(isFirstLoad){ + isFirstLoad = false + } Toast.makeText(context, "No internet", Toast.LENGTH_LONG).show() } diff --git a/app/src/main/java/com/example/pbd_jwr/ui/scan/ScanViewModel.kt b/app/src/main/java/com/example/pbd_jwr/ui/scan/ScanViewModel.kt deleted file mode 100644 index 953040d873de54d9a54ae31cef5e5d1a321550e8..0000000000000000000000000000000000000000 --- a/app/src/main/java/com/example/pbd_jwr/ui/scan/ScanViewModel.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.pbd_jwr.ui.scan - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel - -class ScanViewModel : ViewModel() { - - private val _text = MutableLiveData<String>().apply { - value = "This is scan Fragment" - } - val text: LiveData<String> = _text -} \ No newline at end of file diff --git a/app/src/main/java/com/example/pbd_jwr/ui/settings/SettingsFragment.kt b/app/src/main/java/com/example/pbd_jwr/ui/settings/SettingsFragment.kt index 8a4fae225faf0efa73d8c9eef657940284b9307a..7bff66d226764b70185ffea9649b608d6ed53e72 100644 --- a/app/src/main/java/com/example/pbd_jwr/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/example/pbd_jwr/ui/settings/SettingsFragment.kt @@ -2,6 +2,9 @@ package com.example.pbd_jwr.ui.settings import android.content.ContentValues import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.net.Uri import android.os.Bundle import android.os.Environment import android.provider.MediaStore @@ -10,8 +13,9 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider +import androidx.fragment.app.viewModels import com.example.pbd_jwr.databinding.FragmentSettingsBinding +import com.example.pbd_jwr.encryptedSharedPref.EncryptedSharedPref import com.example.pbd_jwr.ui.transaction.TransactionViewModel import com.example.pbd_jwr.data.entity.Transaction import org.apache.poi.xssf.usermodel.XSSFWorkbook @@ -23,32 +27,68 @@ class SettingsFragment : Fragment() { private var _binding: FragmentSettingsBinding? = null private val binding get() = _binding!! - private lateinit var transactionViewModel: TransactionViewModel + private val transactionViewModel: TransactionViewModel by viewModels() + private lateinit var encryptedSharedPref: SharedPreferences override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentSettingsBinding.inflate(inflater, container, false) - val root: View = binding.root + encryptedSharedPref = EncryptedSharedPref.create(requireContext(), "login") - transactionViewModel = ViewModelProvider(this)[TransactionViewModel::class.java] + setupListeners() + return binding.root + } + + private fun setupListeners() { + val toEmail = encryptedSharedPref.getString("email", "") + binding.sendEmailButton.setOnClickListener { + sendEmailWithAttachment(toEmail) + } binding.saveBtn.setOnClickListener { - transactionViewModel.getAllTransactions().observe(viewLifecycleOwner) { transactions -> - exportTransactionsToExcel(transactions, requireContext()) + saveTransactionsToExcel() + } + } + + private fun sendEmailWithAttachment(toEmail: String?) { + val subject = "Comprehensive Account Transaction History Report" + val content = """ + Attached is a comprehensive report detailing all transactions associated with your account. Should you have any questions or require further assistance, please don't hesitate to reach out. + + Best regards, + + JWR App + """.trimIndent() + + transactionViewModel.getAllTransactions().observe(viewLifecycleOwner) { transactions -> + exportTransactionsToExcel(transactions, requireContext())?.let { uri -> + val emailIntent = Intent(Intent.ACTION_SEND).apply { + type = "vnd.android.cursor.dir/email" + putExtra(Intent.EXTRA_EMAIL, arrayOf(toEmail)) + putExtra(Intent.EXTRA_SUBJECT, subject) + putExtra(Intent.EXTRA_TEXT, content) + putExtra(Intent.EXTRA_STREAM, uri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + startActivity(Intent.createChooser(emailIntent, "Send email with...")) } } + } - return root + private fun saveTransactionsToExcel() { + transactionViewModel.getAllTransactions().observe(viewLifecycleOwner) { transactions -> + exportTransactionsToExcel(transactions, requireContext()) + } } - private fun exportTransactionsToExcel(transactions: List<Transaction>, context: Context) { + private fun exportTransactionsToExcel(transactions: List<Transaction>, context: Context): Uri? { val values = ContentValues().apply { - put(MediaStore.MediaColumns.DISPLAY_NAME, "Transactions.xlsx") // Nama file - put(MediaStore.MediaColumns.MIME_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") // MIME Type - put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) // Direktori tujuan + put(MediaStore.MediaColumns.DISPLAY_NAME, "Transactions.xlsx") + put(MediaStore.MediaColumns.MIME_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) } val resolver = context.contentResolver - val uri = resolver.insert(MediaStore.Files.getContentUri("external"), values) // Mendapatkan URI + val uri = resolver.insert(MediaStore.Files.getContentUri("external"), values) val workbook = XSSFWorkbook() @@ -79,7 +119,7 @@ class SettingsFragment : Fragment() { // Menyimpan file uri?.let { resolver.openOutputStream(it).use { outputStream -> - workbook.write(outputStream) // Menulis ke file + workbook.write(outputStream) } } @@ -90,11 +130,11 @@ class SettingsFragment : Fragment() { } finally { workbook.close() } + return uri } - override fun onDestroyView() { super.onDestroyView() _binding = null } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionAdapter.kt b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionAdapter.kt index 1c1afa7a6abb2d5e7bd217ca2eb6125f117fed80..253d4b27aa4f1213c79520d069f1970bb287bc56 100644 --- a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionAdapter.kt +++ b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionAdapter.kt @@ -38,6 +38,7 @@ class TransactionAdapter : ListAdapter<Transaction, TransactionAdapter.Transacti } itemView.findNavController().navigate(R.id.action_transactionFragment_to_transactionDetailFragment, bundle) } + } } diff --git a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionAddFragment.kt b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionAddFragment.kt index 2ad8434ab8f0e795a71a301e544cd3a9f96c43df..b65f19daa9527c8f3fbd5565106e17454ba28f99 100644 --- a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionAddFragment.kt +++ b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionAddFragment.kt @@ -1,14 +1,24 @@ package com.example.pbd_jwr.ui.transaction +import android.Manifest +import android.content.pm.PackageManager import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import com.example.pbd_jwr.data.entity.Transaction import com.example.pbd_jwr.databinding.FragmentTransactionAddBinding +import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationServices +import com.google.android.gms.location.LocationRequest +import com.google.android.gms.location.LocationCallback +import com.google.android.gms.location.LocationResult import java.util.Date class TransactionAddFragment : Fragment() { @@ -18,6 +28,8 @@ class TransactionAddFragment : Fragment() { private lateinit var mTransactionViewModel: TransactionViewModel + private lateinit var fusedLocationClient: FusedLocationProviderClient + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -28,35 +40,172 @@ class TransactionAddFragment : Fragment() { _binding = FragmentTransactionAddBinding.inflate(inflater, container, false) val root: View = binding.root + // Inisialisasi FusedLocationProviderClient + fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireActivity()) + + // Menampilkan lokasi saat ini di EditText + showCurrentLocation() + binding.btnSubmit.setOnClickListener { onSubmitClicked() } + val editMode = arguments?.getBoolean("editMode", false) ?: false + if (editMode) { + val transaction = arguments?.getParcelable<Transaction>("transaction") + transaction?.let { + binding.editTextTitle.setText(it.title) + binding.editTextCategory.setText(it.category) + binding.editTextAmount.setText(it.amount.toString()) + binding.editTextLocation.setText(it.location) + } + } + return root } + private fun showCurrentLocation() { + if (ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + ) { + println("granted") + // Jika izin diberikan, dapatkan lokasi saat ini + fusedLocationClient.lastLocation + .addOnSuccessListener { location -> + // Jika lokasi berhasil didapatkan, set nilai EditText lokasi + location?.let { + val latitude = it.latitude + val longitude = it.longitude + val currentLocation = "Latitude: $latitude, Longitude: $longitude" + println(currentLocation) + binding.editTextLocation.setText(currentLocation) + } + } + .addOnFailureListener { e -> + // Jika gagal mendapatkan lokasi, tampilkan pesan kesalahan + println("haha") + Toast.makeText( + requireContext(), + "Failed to get location: ${e.message}", + Toast.LENGTH_SHORT + ).show() + } + + val locationRequest = LocationRequest.create().apply { + priority = LocationRequest.PRIORITY_HIGH_ACCURACY + interval = 10000 // Interval pembaruan lokasi dalam milidetik (contoh: 10 detik) + fastestInterval = 5000 // Interval tercepat untuk pembaruan lokasi dalam milidetik (contoh: 5 detik) + } + + // Meminta pembaruan lokasi secara real-time + fusedLocationClient.requestLocationUpdates( + locationRequest, + object : LocationCallback() { + override fun onLocationResult(locationResult: LocationResult) { + super.onLocationResult(locationResult) + // Mendapatkan lokasi saat ini dari locationResult + val lastLocation = locationResult.lastLocation + val latitude = lastLocation.latitude + val longitude = lastLocation.longitude + val currentLocation = "Latitude: $latitude, Longitude: $longitude" + binding.editTextLocation.setText(currentLocation) + } + }, + null + ) + } else { + println("not granted") + // Jika izin lokasi belum diberikan, minta izin + ActivityCompat.requestPermissions( + requireActivity(), + arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), + LOCATION_PERMISSION_REQUEST_CODE + ) + } + } + + @Suppress("DEPRECATION") + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array<out String>, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // Izin lokasi diberikan, panggil showCurrentLocation untuk menampilkan lokasi + showCurrentLocation() + } else { + // Izin lokasi ditolak, tampilkan pesan kesalahan + Toast.makeText( + requireContext(), + "Location permission denied. Some functionality may be limited.", + Toast.LENGTH_SHORT + ).show() + } + } + } + + private fun onSubmitClicked() { - val title = binding.editTextTitle.text.toString() - val category = binding.editTextCategory.text.toString() - val amount = binding.editTextAmount.text.toString().toDoubleOrNull() ?: return // Handle invalid input - val location = binding.editTextLocation.text.toString() - val date = Date().time // You can get the date from a date picker or another source - // Call the addTransaction method from TransactionViewModel - mTransactionViewModel.addTransaction(Transaction(14,1, title, category, amount, location, date)) + val title = binding.editTextTitle.text.toString().trim() + val category = binding.editTextCategory.text.toString().trim() + val amountString = binding.editTextAmount.text.toString().trim() + val location = binding.editTextLocation.text.toString().trim() + + // Validate input fields + if (title.isEmpty() || category.isEmpty() || amountString.isEmpty() || location.isEmpty()) { + showError("All fields are required") + return + } + + val amount = amountString.toDoubleOrNull() + if (amount == null || amount <= 0) { + showError("Invalid amount") + return + } + + val date = Date().time + + + val editMode = arguments?.getBoolean("editMode", false) ?: false + println("editMode") + println(editMode) + if (editMode) { + // Editing mode + val transactionId = arguments?.getLong("transactionId", -1L) ?: -1L + editTransaction(transactionId, title, category, amount, location, date) + findNavController().popBackStack() + } else { + // Adding mode + addTransaction(title, category, amount, location, date) + } findNavController().popBackStack() -// mTransactionViewModel.transactionSubmitted.observe(viewLifecycleOwner) { transactionSubmitted -> -// if (transactionSubmitted) { -// -// } -// } } + private fun addTransaction(title: String, category: String, amount: Double, location: String, date: Long) { + mTransactionViewModel.addTransaction(Transaction(userId = 1, title = title, category = category, amount = amount, location = location, date = date)) + } + + private fun editTransaction(transactionId: Long, title: String, category: String, amount: Double, location: String, date: Long) { + mTransactionViewModel.updateTransaction(Transaction(transactionId, userId = 1, title = title, category = category, amount = amount, location = location, date = date)) + } + + private fun showError(message: String) { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() + } override fun onDestroyView() { super.onDestroyView() - _binding = null + + } + + companion object { + private const val LOCATION_PERMISSION_REQUEST_CODE = 1001 } } diff --git a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionDetailFragment.kt b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionDetailFragment.kt index 0023581ab2a9d54f96fc59005ef5f2071bd9a7d4..87e55a331c4999fde6d79c1e413a77294d556347 100644 --- a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionDetailFragment.kt +++ b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionDetailFragment.kt @@ -5,9 +5,12 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController +import com.example.pbd_jwr.R import com.example.pbd_jwr.data.entity.Transaction import com.example.pbd_jwr.databinding.FragmentTransactionDetailBinding @@ -31,15 +34,33 @@ class TransactionDetailFragment : Fragment() { val transaction = arguments?.getParcelable<Transaction>("transaction") - transaction?.let { displayTransactionDetails(it) } - binding.btnBack.setOnClickListener { + if (transaction != null) { + displayTransactionDetails(transaction) + + + binding.btnBack.setOnClickListener { + findNavController().popBackStack() + } + + binding.btnDelete.setOnClickListener { + transaction?.let { deleteTransaction(it) } + } + + binding.btnEdit.setOnClickListener { + val bundle = Bundle().apply { + putParcelable("transaction", transaction) + putLong("transactionId", transaction.id) // Pass the transaction ID + putBoolean("editMode", true) // Set edit mode to true + } + findNavController().navigate(R.id.action_transactionDetailFragment_to_transactionAddFragment, bundle) + } + + } else { + Toast.makeText(requireContext(), "Transaction not found", Toast.LENGTH_SHORT).show() findNavController().popBackStack() } - binding.btnDelete.setOnClickListener { - transaction?.let { deleteTransaction(it) } - } return root } diff --git a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionFragment.kt b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionFragment.kt index 146c4901e537c898414538ad766e54b51b43f6eb..b75985dc6fb46bbcb8dd080307254424b25e3e5f 100644 --- a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionFragment.kt +++ b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionFragment.kt @@ -26,11 +26,13 @@ class TransactionFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - mTransactionViewModel = ViewModelProvider(this).get(TransactionViewModel::class.java) - _binding = FragmentTransactionBinding.inflate(inflater, container, false) val root: View = binding.root + mTransactionViewModel = ViewModelProvider(this)[TransactionViewModel::class.java] + + + mTransactionViewModel.getAllTransactions().observe(viewLifecycleOwner, Observer { transactions -> transactions.forEach { transaction -> Log.d("Transaction", "Title: ${transaction.title}, Category: ${transaction.category}, Amount: ${transaction.amount}") @@ -45,12 +47,10 @@ class TransactionFragment : Fragment() { } binding.btnAdd.setOnClickListener { - // Navigate to TransactionAddFragment when the button is clicked findNavController().navigate(R.id.action_transactionFragment_to_transactionAddFragment) } mTransactionViewModel.getAllTransactions().observe(viewLifecycleOwner, Observer { transactions -> - Log.d("Transaction", "${transactions}") transactionAdapter.submitList(transactions) }) diff --git a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionViewModel.kt b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionViewModel.kt index 81218dd07ffb854b1d00bb390127e26cb4f626ce..587eb0f2feadc65e402940bb7314159a369259e0 100644 --- a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionViewModel.kt +++ b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionViewModel.kt @@ -31,8 +31,25 @@ class TransactionViewModel(application: Application) : AndroidViewModel(applicat fun addTransaction(transaction: Transaction) { viewModelScope.launch(Dispatchers.IO) { - repository.addTransaction(transaction) - _transactionSubmitted.value = true + try { + repository.addTransaction(transaction) + _transactionSubmitted.postValue(true) + println("lolos") + } catch (e: Exception) { + _transactionSubmitted.postValue(false) + println("ga lolos") + } + } + } + + fun updateTransaction(transaction: Transaction) { + viewModelScope.launch(Dispatchers.IO) { + try { + repository.updateTransaction(transaction) + _transactionSubmitted.postValue(true) + } catch (e: Exception) { + _transactionSubmitted.postValue(false) + } } } diff --git a/app/src/main/res/color/add_button_color.xml b/app/src/main/res/color/add_button_color.xml new file mode 100644 index 0000000000000000000000000000000000000000..e1ab725d01f16b53dbfe8ba864908aed31189e9a --- /dev/null +++ b/app/src/main/res/color/add_button_color.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Dark Mode --> + <item android:color="@color/gray_light" android:state_checked="true" /> + <!-- Light Mode --> + <item android:color="@color/gray_extralight" /> +</selector> diff --git a/app/src/main/res/drawable/add_button_shape.xml b/app/src/main/res/drawable/add_button_shape.xml new file mode 100644 index 0000000000000000000000000000000000000000..ffdc6490fdaaa10efe45920c26490f7d5f23f365 --- /dev/null +++ b/app/src/main/res/drawable/add_button_shape.xml @@ -0,0 +1,5 @@ +<!-- circular_background.xml --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="@color/add_button_color" /> +</shape> diff --git a/app/src/main/res/drawable/baseline_add_24.xml b/app/src/main/res/drawable/baseline_add_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..a1eb28904100d849056d7edfa8d7f0e743b5bb64 --- /dev/null +++ b/app/src/main/res/drawable/baseline_add_24.xml @@ -0,0 +1,6 @@ + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="32dp" android:tint="@color/gold" android:viewportHeight="24" android:viewportWidth="24" android:width="32dp"> + + <path android:fillColor="@color/gold" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> + +</vector> diff --git a/app/src/main/res/drawable/bottom_nav_icon_color_selector.xml b/app/src/main/res/drawable/bottom_nav_icon_color_selector.xml index b6e05581759a3da1944229a7045392dc19830a6e..36f30e9718c5d51f7c30a26eb7755bc8bb2ab7c8 100644 --- a/app/src/main/res/drawable/bottom_nav_icon_color_selector.xml +++ b/app/src/main/res/drawable/bottom_nav_icon_color_selector.xml @@ -2,5 +2,5 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true" android:color="@color/gold" /> <item android:state_pressed="true" android:state_enabled="true" android:color="@android:color/white" /> - <item android:color="@color/gray_1" /> + <item android:color="@color/gray_light" /> </selector> \ No newline at end of file diff --git a/app/src/main/res/drawable/custom_input.xml b/app/src/main/res/drawable/custom_input.xml index 315ec8e0d511e309438ffd2acd4b0c499e1e631c..f094b5c7cd3a16556066eac54a492b02e993e582 100644 --- a/app/src/main/res/drawable/custom_input.xml +++ b/app/src/main/res/drawable/custom_input.xml @@ -12,7 +12,7 @@ <shape android:shape="rectangle"> <solid android:color="@color/white"/> <corners android:radius="10dp" /> - <stroke android:color="@color/gray_1" android:width="1dp" /> + <stroke android:color="@color/gray_light" android:width="1dp" /> </shape> </item> </selector> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_document_scanner_24.xml b/app/src/main/res/drawable/ic_document_scanner_24.xml index f7099c0fe1da6e91f65007b4c4f61ea50d285172..2bb9f405c60838b4890e66f9b175069bc4ff9451 100644 --- a/app/src/main/res/drawable/ic_document_scanner_24.xml +++ b/app/src/main/res/drawable/ic_document_scanner_24.xml @@ -1,5 +1,5 @@ <vector android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M7,3H4v3H2V1h5V3zM22,6V1h-5v2h3v3H22zM7,21H4v-3H2v5h5V21zM20,18v3h-3v2h5v-5H20zM19,18c0,1.1 -0.9,2 -2,2H7c-1.1,0 -2,-0.9 -2,-2V6c0,-1.1 0.9,-2 2,-2h10c1.1,0 2,0.9 2,2V18zM15,8H9v2h6V8zM15,11H9v2h6V11zM15,14H9v2h6V14z"/> + <path android:fillColor="@color/white" android:pathData="M7,3H4v3H2V1h5V3zM22,6V1h-5v2h3v3H22zM7,21H4v-3H2v5h5V21zM20,18v3h-3v2h5v-5H20zM19,18c0,1.1 -0.9,2 -2,2H7c-1.1,0 -2,-0.9 -2,-2V6c0,-1.1 0.9,-2 2,-2h10c1.1,0 2,0.9 2,2V18zM15,8H9v2h6V8zM15,11H9v2h6V11zM15,14H9v2h6V14z"/> </vector> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 5e603391b8074ce65edd3dbb5ba6588edf1e8879..0a62c782699a00bebe25f55ed10e1915d2ecd708 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -42,7 +42,7 @@ android:src="@drawable/ic_document_scanner_24" app:layout_anchor="@id/bottomAppBar" app:backgroundTint="@color/gold" - + app:tint="@color/white" /> <fragment diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 9b0e51d1bf8f189173b7484a0ba14ac1dd214a6d..3e726bb318880e119196e1646145ffc2d501e130 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -9,19 +9,19 @@ > - <TextView - android:id="@+id/text_settings" - android:layout_width="match_parent" + <Button + android:id="@+id/sendEmailButton" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:layout_marginTop="8dp" - android:layout_marginEnd="8dp" - android:textAlignment="center" - android:textSize="20sp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + android:layout_marginTop="24dp" + android:drawableEnd="@drawable/baseline_email_24" + android:paddingHorizontal="50dp" + android:text="@string/send_email" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" + /> + <Button android:id="@+id/saveBtn" diff --git a/app/src/main/res/layout/fragment_transaction.xml b/app/src/main/res/layout/fragment_transaction.xml index 0c936036d8feb920ea21798fbe41841a061aac0d..b68e6ad953576a77ef7786529a70fa20eb214c39 100644 --- a/app/src/main/res/layout/fragment_transaction.xml +++ b/app/src/main/res/layout/fragment_transaction.xml @@ -18,7 +18,6 @@ app:layout_constraintTop_toTopOf="parent" /> - <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerViewTransactions" android:layout_width="match_parent" @@ -28,15 +27,17 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> - <Button + <ImageButton android:id="@+id/btnAdd" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Add" + android:layout_width="40dp" + android:layout_height="40dp" + android:backgroundTint="@null" + android:background="@drawable/add_button_shape" + android:src="@drawable/baseline_add_24" android:layout_margin="16dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - /> + /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/fragment_transaction_add.xml b/app/src/main/res/layout/fragment_transaction_add.xml index b4c0f42b576f44461d14226b857d56dc8215d564..a6ef50f2d5911bffe07f25d08aeac2a6da689d64 100644 --- a/app/src/main/res/layout/fragment_transaction_add.xml +++ b/app/src/main/res/layout/fragment_transaction_add.xml @@ -3,6 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" + xmlns:tools="http://schemas.android.com/tools" + tools:context=".MainActivity" android:padding="16dp"> <!-- Back Button --> @@ -53,19 +55,6 @@ android:layout_marginEnd="16dp" android:layout_marginStart="16dp"/> - <!-- Date Field --> - <EditText - android:id="@+id/editTextDate" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:hint="Date" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/editTextAmount" - android:layout_marginTop="16dp" - android:layout_marginEnd="16dp" - android:layout_marginStart="16dp"/> - <!-- Location Field --> <EditText android:id="@+id/editTextLocation" @@ -74,7 +63,7 @@ android:hint="Location" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/editTextDate" + app:layout_constraintTop_toBottomOf="@id/editTextAmount" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginStart="16dp"/> @@ -85,10 +74,10 @@ android:id="@+id/btnSubmit" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Submit" - app:layout_constraintTop_toBottomOf="@id/editTextAmount" - android:onClick="onSubmitClicked" - app:layout_constraintEnd_toEndOf="parent" + android:text="@string/submit" + app:layout_constraintBottom_toBottomOf="@id/editTextLocation" + app:layout_constraintEnd_toEndOf="@id/editTextLocation" + app:layout_constraintStart_toStartOf="@id/editTextLocation" android:layout_marginTop="16dp" android:layout_marginEnd="16dp"/> diff --git a/app/src/main/res/layout/fragment_transaction_detail.xml b/app/src/main/res/layout/fragment_transaction_detail.xml index d9e5d33bc372d41d4f79cfee2997942aa1452546..d8a81388707a4e1e0a96f7acc131ecd4b5ff5ee2 100644 --- a/app/src/main/res/layout/fragment_transaction_detail.xml +++ b/app/src/main/res/layout/fragment_transaction_detail.xml @@ -12,7 +12,7 @@ android:textSize="24sp" android:textStyle="bold" android:layout_marginTop="8dp" - app:layout_constraintTop_toBottomOf="@id/btnBack" + app:layout_constraintTop_toBottomOf="@id/btnEdit" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> @@ -86,20 +86,20 @@ android:layout_marginTop="16dp" android:layout_marginStart="16dp" android:elevation="2dp" - app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/btnBack"/> -<!-- <Button--> -<!-- android:id="@+id/btnEdit"--> -<!-- android:layout_width="wrap_content"--> -<!-- android:layout_height="wrap_content"--> -<!-- android:text="Edit"--> -<!-- android:textColor="@android:color/black"--> -<!-- android:layout_marginBottom="16dp"--> -<!-- android:layout_marginEnd="16dp"--> -<!-- android:layout_alignParentBottom="true"--> -<!-- android:layout_alignParentEnd="true"--> -<!-- android:elevation="2dp"/>--> + <Button + android:id="@+id/btnEdit" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Edit" + android:textColor="@android:color/black" + android:layout_marginBottom="16dp" + android:layout_marginEnd="16dp" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:elevation="2dp" + app:layout_constraintTop_toBottomOf="@id/btnDelete"/> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 153e95752e7ea6dcc085549a598b523f0a822ee3..ee20a61468c6b658821eda8b34718bb3a49d6f81 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -24,7 +24,12 @@ android:id="@+id/navigation_transaction_detail" android:name="com.example.pbd_jwr.ui.transaction.TransactionDetailFragment" android:label="" - tools:layout="@layout/fragment_transaction_detail" /> + tools:layout="@layout/fragment_transaction_detail"> + + <action + android:id="@+id/action_transactionDetailFragment_to_transactionAddFragment" + app:destination="@id/navigation_transaction_add" /> + </fragment> <fragment android:id="@+id/navigation_transaction_add" @@ -33,18 +38,15 @@ tools:layout="@layout/fragment_transaction_add" /> -<fragment - android:id="@+id/navigation_dashboard" - android:name="com.example.pbd_jwr.ui.dashboard.DashboardFragment" - android:label="@string/title_dashboard" - tools:layout="@layout/fragment_dashboard" /> + <fragment + android:id="@+id/navigation_dashboard" + android:name="com.example.pbd_jwr.ui.dashboard.DashboardFragment" + android:label="@string/title_dashboard" + tools:layout="@layout/fragment_dashboard" /> + + - <fragment - android:id="@+id/navigation_scan" - android:name="com.example.pbd_jwr.ui.scan.ScanFragment" - android:label="@string/title_scan" - tools:layout="@layout/activity_scan" /> <fragment android:id="@+id/navigation_settings" diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index ea52126a5ef3b181e7666b6b44f1678d450da1e8..d20581de288ee77cd3c3a7dc55364e311b43cfa1 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -2,13 +2,13 @@ <!-- Base application theme. --> <style name="Theme.PBDJWR" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <!-- Primary brand color. --> - <item name="colorPrimary">@color/black</item> + <item name="colorPrimary">@color/gray_dark</item> <item name="colorPrimaryVariant">@color/black</item> <item name="colorOnPrimary">@color/white</item> <!-- Secondary brand color. --> <item name="colorSecondary">@color/gold</item> <item name="colorSecondaryVariant">@color/gold</item> - <item name="colorOnSecondary">@color/gray_1</item> + <item name="colorOnSecondary">@color/gray_dark</item> <!-- Status bar color. --> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> <item name="android:windowLightStatusBar">false</item> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c2f37431379f50641045e04a18dcfe62f41700ed..bdb1f84676fd240f2a870ad4817df27be0c17a6a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,7 +6,9 @@ <color name="teal_200">#FF03DAC5</color> <color name="teal_700">#FF018786</color> <color name="gold">#FFB29F7A</color> - <color name="gray_1">#FFC4C4C4</color> + <color name="gray_light">#FFC4C4C4</color> + <color name="gray_dark">#FF5F646D</color> <color name="black">#FF000000</color> <color name="white">#FFFFFFFF</color> + <color name="gray_extralight">#FFECECEC</color> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c337978456beace0642199f521c7e7c20d6ba77e..f58d63363b810c20e6b1d9441ac281e949a6308d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,4 +10,6 @@ <string name="jwr">JWR</string> <string name="logout">logout</string> <string name="title_scan">Scan</string> + <string name="send_email">Send Email</string> + <string name="submit">Submit</string> </resources> \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index ffde4b79f3bf0e6b5e4b7b6eda9dd88469eb5ac3..5cf2105177c3d52e19649d5c35bf3ec5593ad14e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,9 @@ buildscript { dependencies { val navVersion = "2.7.7" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$navVersion") + classpath("com.google.android.gms:play-services-maps:18.2.0") + + // val androidPluginVersion ="8.3.1" //