diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bdfbaf43c2d199bf1668e2d8f6fdab7aa95c7b61..2b2fd334243257d02f1d7f7e1c4c677f096562a0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -64,6 +64,7 @@ dependencies { implementation(libs.play.services.location) implementation(libs.androidx.camera.view) implementation(libs.androidx.camera.lifecycle) + implementation(libs.androidx.room.ktx) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/java/itb/bos/bondoman/fragment/ui/statistic/StatisticFragment.kt b/app/src/main/java/itb/bos/bondoman/fragment/ui/statistic/StatisticFragment.kt index bae36f7e7941d7287a70bc43f3a03d6a5140a785..7764fec952f7aa19d9de9899a8f549d9f4eb0676 100644 --- a/app/src/main/java/itb/bos/bondoman/fragment/ui/statistic/StatisticFragment.kt +++ b/app/src/main/java/itb/bos/bondoman/fragment/ui/statistic/StatisticFragment.kt @@ -58,12 +58,6 @@ class StatisticFragment : Fragment() { setData(expenseEntries, incomeEntries) } - override fun onDestroyView() { - super.onDestroyView() - // Close the database connection - database.close() - } - private fun setupLineChart(expenseTransactions: List<SqlTransaction>, incomeTransactions: List<SqlTransaction>) { binding.lineChart.apply { // Set the title for the graph diff --git a/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/AddTransactionFragment.kt b/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/AddTransactionFragment.kt index 0fa880975ac2ec1db97e9df61d1cf528a097b449..36710617383d8138b88eb591ef5abf9c4db95896 100644 --- a/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/AddTransactionFragment.kt +++ b/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/AddTransactionFragment.kt @@ -59,7 +59,7 @@ class AddTransactionFragment : Fragment() { // Check for both ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions if (ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED - && ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + || ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { Log.e("fetchLocation", "Both fine and coarse location permissions granted") fetchLocation { fetchedAddress -> address = fetchedAddress @@ -73,9 +73,7 @@ class AddTransactionFragment : Fragment() { } binding.addTransaction.setOnClickListener { - fetchLocation { fetchedAddress -> - address = fetchedAddress - } + // Retrieve values from other fields val name = binding.addNameField.text.toString() val amountText = binding.addPriceField.text.toString() @@ -83,8 +81,6 @@ class AddTransactionFragment : Fragment() { // Retrieve selected category from the Spinner val category = categorySpinner.selectedItem.toString() - Log.d("addTransactionWoi", "Button clicked") - // Check if amountText is a valid number val amount: Int try { @@ -102,8 +98,6 @@ class AddTransactionFragment : Fragment() { binding.addLocationField.text.toString() } - Log.d("addTransactionWoi", "the location is $location") - // Create the transaction val newTransaction = SqlTransaction( name = name, @@ -113,7 +107,11 @@ class AddTransactionFragment : Fragment() { location = location ?: "" // Use an empty string if location is null ) // Insert the transaction + if (!::database.isInitialized || !database.isOpen) { + database = TransactionData.getInstance(requireContext()) + } database.transactionDao().insertAll(newTransaction) + // Go back to the previous fragment requireActivity().onBackPressed() } @@ -126,40 +124,75 @@ class AddTransactionFragment : Fragment() { } private fun fetchLocation(callback: (String) -> Unit) { - var address = "Unknown" + val address = "Unknown" // Check for both ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions - if (ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED - && ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + when { + ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED -> { + fetchPreciseLocation(callback) + } + ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED -> { + fetchApproximateLocation(callback) + } + else -> { + // Request location permissions for both fine and coarse locations + ActivityCompat.requestPermissions(requireActivity(), arrayOf( + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_COARSE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE) + callback(address) + } + } + + } + + private fun fetchPreciseLocation(callback: (String) -> Unit) { + if (ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { fusedLocationClient.lastLocation.addOnSuccessListener { location -> location?.let { - Log.e("fetchLocation", "Fetching location") + Log.e("fetchLocation", "Fetching precise location") val latitude = location.latitude val longitude = location.longitude - address = getAddress(latitude, longitude) + val address = getAddress(latitude, longitude, true) callback(address) } }.addOnFailureListener { e -> // Handle failure to fetch location - Log.e("fetchLocation", "Failed to fetch location: ${e.message}") - Toast.makeText(requireContext(), "Failed to fetch location", Toast.LENGTH_SHORT).show() - callback(address) + Log.e("fetchLocation", "Failed to fetch precise location: ${e.message}") + Toast.makeText(requireContext(), "Failed to fetch location", Toast.LENGTH_SHORT) + .show() + callback("Unknown") } - } else { - // Request location permissions for both fine and coarse locations - ActivityCompat.requestPermissions(requireActivity(), arrayOf( - android.Manifest.permission.ACCESS_FINE_LOCATION, - android.Manifest.permission.ACCESS_COARSE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE) - callback(address) } } + private fun fetchApproximateLocation(callback: (String) -> Unit) { + if (ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + fusedLocationClient.lastLocation.addOnSuccessListener { location -> + location?.let { + Log.e("fetchLocation", "Fetching approximate location") + val latitude = location.latitude + val longitude = location.longitude + val address = getAddress(latitude, longitude, false) + callback(address) + } + }.addOnFailureListener { e -> + // Handle failure to fetch location + Log.e("fetchLocation", "Failed to fetch approximate location: ${e.message}") + Toast.makeText(requireContext(), "Failed to fetch location", Toast.LENGTH_SHORT) + .show() + callback("Unknown") + } + } + } - - private fun getAddress(latitude: Double, longitude: Double): String { + private fun getAddress(latitude: Double, longitude: Double, isPreciseLocation: Boolean): String { val addresses = geocoder.getFromLocation(latitude, longitude, 1) return if (!addresses.isNullOrEmpty()) { val address = addresses[0] - "${address.getAddressLine(0)}, ${address.locality}, ${address.adminArea}, ${address.countryName}" + if (isPreciseLocation) { + "${address.getAddressLine(0)}, ${address.locality}, ${address.adminArea}, ${address.countryName}" + } else { + "${address.locality}, ${address.adminArea}, ${address.countryName}" + } } else { "Unknown Location" } @@ -175,18 +208,28 @@ class AddTransactionFragment : Fragment() { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Log.e("fetchLocation", "Location permission granted") + var fineLocationPermissionGranted = false + var coarseLocationPermissionGranted = false + + for (i in permissions.indices) { + if (permissions[i] == android.Manifest.permission.ACCESS_FINE_LOCATION) { + fineLocationPermissionGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED + } else if (permissions[i] == android.Manifest.permission.ACCESS_COARSE_LOCATION) { + coarseLocationPermissionGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED + } + } + + if (fineLocationPermissionGranted || coarseLocationPermissionGranted) { + Log.e("fetchLocation", "Location permissions granted") fetchLocation { fetchedAddress -> address = fetchedAddress } isFetched = true } else { - Log.e("fetchLocation", "Location permission denied") + Log.e("fetchLocation", "Location permissions denied") Toast.makeText(requireContext(), "Location permission denied", Toast.LENGTH_SHORT).show() } } } - } diff --git a/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/EditTransactionFragment.kt b/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/EditTransactionFragment.kt index 4994a83fb7ea18d1aa89a4371e9e182b7458aa67..87dcc8bfaae5368556ce196172b0f3553b5dbc47 100644 --- a/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/EditTransactionFragment.kt +++ b/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/EditTransactionFragment.kt @@ -79,8 +79,6 @@ class EditTransactionFragment : Fragment() { populateTransactionDetails(it) } - database.close() - } diff --git a/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/TransactionFragment.kt b/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/TransactionFragment.kt index f02d3c382907e5893f8a414e30900b0605a6b604..b79bf9857c7814a324648214da274388b77cf391 100644 --- a/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/TransactionFragment.kt +++ b/app/src/main/java/itb/bos/bondoman/fragment/ui/transaction/TransactionFragment.kt @@ -112,7 +112,7 @@ class TransactionFragment : Fragment() { // var transactionAdapter = TransactionAdapter(filteredList) // transactionData.adapter = transactionAdapter -// searchBar = binding.searchBar + searchBar = binding.searchBar // binding.searchBar.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { @@ -133,7 +133,6 @@ class TransactionFragment : Fragment() { super.onDestroyView() // Unregister the BroadcastReceiver when the fragment's view is destroyed requireContext().unregisterReceiver(receiver) - database.close() } override fun onResume() { diff --git a/app/src/main/res/layout/fragment_transaction.xml b/app/src/main/res/layout/fragment_transaction.xml index a7ef318c9c9579ea66788f70a1078566d56e3475..503b83e4e24c714922836a4d2781ef55ac8c829e 100644 --- a/app/src/main/res/layout/fragment_transaction.xml +++ b/app/src/main/res/layout/fragment_transaction.xml @@ -43,6 +43,7 @@ android:id="@+id/searchBar" android:layout_width="match_parent" android:layout_height="match_parent" + android:textColor="@color/dark_grey" android:background="@android:color/transparent" android:hint="Search" android:inputType="textEmailAddress" diff --git a/app/src/main/res/layout/row_transaction.xml b/app/src/main/res/layout/row_transaction.xml index b70a7370b191387111f3245c3ce2689c2651d114..7aaba5327dd21f3ea7a5ecf9b568eb652572a1de 100644 --- a/app/src/main/res/layout/row_transaction.xml +++ b/app/src/main/res/layout/row_transaction.xml @@ -3,62 +3,87 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" - android:orientation="horizontal"> - <LinearLayout - android:layout_width="220dp" - android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_margin="8dp"> - <TextView - android:id="@+id/category" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@drawable/cornered_box" - android:textColor="@color/light_grey" - android:padding="4dp" - android:textSize="12sp" - /> - <TextView - android:id="@+id/itemName" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="2dp" - android:textSize="16sp" - /> - <TextView - android:id="@+id/itemDate" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="2dp" - android:textSize="12sp" - /> - </LinearLayout> + android:orientation="vertical"> + <LinearLayout - android:layout_width="90dp" - android:layout_height="match_parent" - android:layout_marginEnd="5dp" - android:layout_marginStart="0dp" - android:orientation="vertical" - android:gravity="end"> - <TextView - android:id="@+id/itemPrice" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="2dp" - android:textSize="16sp" - android:layout_weight="1"/> - <TextView - android:id="@+id/itemLocation" - android:layout_width="wrap_content" + android:orientation="horizontal"> + + <LinearLayout + android:layout_width="220dp" android:layout_height="wrap_content" - android:padding="2dp" - android:textSize="12sp" - android:ellipsize="end" - android:maxLines="1" - android:layout_weight="2" - android:clickable="true" /> + android:orientation="vertical" + android:layout_margin="8dp"> + <TextView + android:id="@+id/category" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/cornered_box" + android:textColor="@color/light_grey" + android:padding="4dp" + android:textSize="12sp" + android:text="Category" /> - </LinearLayout> + <TextView + android:id="@+id/itemName" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="2dp" + android:textSize="16sp" + android:text="Item Name" + android:textColor="@color/dark_grey" /> + + <TextView + android:id="@+id/itemDate" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="2dp" + android:textSize="12sp" + android:text="Item Date" + android:textColor="@color/dark_grey" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="90dp" + android:layout_height="80dp" + android:layout_marginStart="0dp" + android:layout_marginEnd="5dp" + android:gravity="end" + android:orientation="vertical"> + + <TextView + android:id="@+id/itemPrice" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:padding="2dp" + android:textColor="@color/dark_grey" + android:textSize="16sp" + android:text="Item Price" /> + + <TextView + android:id="@+id/itemLocation" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="2" + android:clickable="true" + android:ellipsize="end" + android:maxLines="1" + android:padding="2dp" + android:textColor="@color/dark_grey" + android:textSize="12sp" + android:text="Item Location" /> + + </LinearLayout> </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@color/dark_grey" /> + +</LinearLayout> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 814ad41a15f598789a5efde7a34791df24b82467..3a3ca8a6f16c326785fca3c66481301650403559 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,6 +6,7 @@ <color name="base">#1C4A5A</color> <color name="off_white">#F2F2F2</color> <color name="light_grey">#E8E8E8</color> + <color name="dark_grey">#586375</color> <color name="red">#AB2900</color> <color name="light_blue_400">#FF29B6F6</color> <color name="light_blue_600">#FF039BE5</color> diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f262475f1ca7ab3bdde4bc311cdd83119159dc1..acf83112a7866875a127e709138d7eb798bf713c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,6 +17,7 @@ securityCryptoKtx = "1.0.0" playServicesLocation = "21.2.0" cameraView = "1.3.2" cameraLifecycle = "1.3.2" +roomKtx = "2.6.1" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -36,6 +37,7 @@ androidx-security-crypto-ktx = { group = "androidx.security", name = "security-c play-services-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "playServicesLocation" } androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraView" } androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraLifecycle" } +androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "roomKtx" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" }