From 89f79a0dc16a29da1ff1c3293e6b709591984a27 Mon Sep 17 00:00:00 2001
From: Fahrian Afdholi <89321009+fchrgrib@users.noreply.github.com>
Date: Thu, 21 Mar 2024 11:36:14 +0700
Subject: [PATCH] fixed: bug

---
 .../ui/screen/mainmenu/MainActivity.kt        | 90 +++++++++++++++++++
 .../ui/screen/mainmenu/fragment/Scan.kt       | 47 ++++++++--
 .../screen/mainmenu/fragment/Transaction.kt   |  2 +-
 .../mainmenu/fragment/TransactionForm.kt      | 18 ++--
 .../transactionapp/ui/viewmodel/auth/Auth.kt  |  8 +-
 .../viewmodel/location/LocationViewModel.kt   | 16 ++++
 .../ui/viewmodel/transaction/Transaction.kt   | 70 ++++++++++++++-
 app/src/main/res/menu/button_menu.xml         |  3 +-
 8 files changed, 231 insertions(+), 23 deletions(-)
 create mode 100644 app/src/main/java/com/example/transactionapp/ui/viewmodel/location/LocationViewModel.kt

diff --git a/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/MainActivity.kt b/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/MainActivity.kt
index 1b7e279..4de6ad2 100644
--- a/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/MainActivity.kt
+++ b/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/MainActivity.kt
@@ -1,8 +1,15 @@
 package com.example.transactionapp.ui.screen.mainmenu
 
+import android.content.pm.PackageManager
+import android.location.Geocoder
+import android.location.Location
+import android.os.Build
 import android.os.Bundle
+import android.os.Looper
 import android.util.Log
+import androidx.annotation.RequiresApi
 import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import com.example.transactionapp.R
@@ -12,13 +19,21 @@ import com.example.transactionapp.ui.screen.mainmenu.fragment.Settings
 import com.example.transactionapp.ui.screen.mainmenu.fragment.Statistics
 import com.example.transactionapp.ui.screen.mainmenu.fragment.Transaction
 import com.example.transactionapp.ui.screen.mainmenu.fragment.TransactionForm
+import com.example.transactionapp.ui.viewmodel.location.LocationViewModel
 import com.example.transactionapp.ui.viewmodel.transaction.TransactionViewModel
+import com.google.android.gms.location.FusedLocationProviderClient
+import com.google.android.gms.location.LocationCallback
+import com.google.android.gms.location.LocationResult
+import com.google.android.gms.location.LocationServices
 import dagger.hilt.android.AndroidEntryPoint
 import java.util.Date
+import java.util.Locale
 
 @AndroidEntryPoint
 class MainActivity : AppCompatActivity() {
+    private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
     private lateinit var db : TransactionViewModel
+    private lateinit var locationViewModel: LocationViewModel
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -26,6 +41,7 @@ class MainActivity : AppCompatActivity() {
         setContentView(binding.root)
 
         db = ViewModelProvider(this)[TransactionViewModel::class.java]
+        locationViewModel = ViewModelProvider(this)[LocationViewModel::class.java]
         db.getAllDate()
         db.getTransactions("all")
         db.getCashFlowAndGrowthByMonth(Date())
@@ -43,6 +59,7 @@ class MainActivity : AppCompatActivity() {
             fragment.replace(frame, TransactionForm())
             fragment.addToBackStack(null)
             fragment.commit()
+            binding.bottomNavigationView.selectedItemId = R.id.empty
         }
 
         //TODO: Add Animation When Fragment Change
@@ -85,11 +102,84 @@ class MainActivity : AppCompatActivity() {
                 }
             }
         }
+
+        db.addTransactionStatus.observe(this){
+            if (it){
+                db.getAllDate()
+                db.getTransactions("all")
+                db.getCashFlowAndGrowthByMonth(Date())
+                fragment = supportFragmentManager.beginTransaction()
+                fragment.replace(frame, Transaction())
+                fragment.addToBackStack(null)
+                fragment.commit()
+                db.resetAddTransactionStatus()
+                db.changeAddStatus(false)
+                binding.bottomNavigationView.selectedItemId = R.id.IbTransactionBtn
+            }
+        }
     }
 
+    @RequiresApi(Build.VERSION_CODES.S)
     override fun onStart() {
         super.onStart()
         db = ViewModelProvider(this)[TransactionViewModel::class.java]
         db.getAllDate()
+        NewLocationData()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        db.removeObserveAllData(this)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.S)
+    override fun onRequestPermissionsResult(
+        requestCode: Int,
+        permissions: Array<out String>,
+        grantResults: IntArray
+    ) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+        if(requestCode == 1010){
+            if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){
+                NewLocationData()
+            }
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.S)
+    fun NewLocationData(){
+        var locationRequest = com.google.android.gms.location.LocationRequest()
+        locationRequest.priority = com.google.android.gms.location.LocationRequest.PRIORITY_HIGH_ACCURACY
+        locationRequest.interval = 0
+        locationRequest.fastestInterval = 0
+        locationRequest.numUpdates = 1
+        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
+        if (ActivityCompat.checkSelfPermission(this,android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
+            ActivityCompat.checkSelfPermission(this,android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
+        ) {
+            fusedLocationProviderClient!!.requestLocationUpdates(
+                locationRequest,locationCallback, Looper.myLooper()
+            )
+            return
+        }
+
+    }
+
+    private val locationCallback = object : LocationCallback() {
+        override fun onLocationResult(locationResult: LocationResult) {
+            var lastLocation: Location? = locationResult.lastLocation
+            if (lastLocation != null) {
+                locationViewModel.setLocation(getCityName(lastLocation.latitude, lastLocation.longitude))
+            }
+        }
+    }
+
+    private fun getCityName(lat: Double,long: Double):String{
+        var cityName = ""
+        val geoCoder = Geocoder(this, Locale.getDefault())
+        val Address = geoCoder.getFromLocation(lat,long,3)
+
+        cityName = Address!!.get(0).subAdminArea
+        return cityName
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/Scan.kt b/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/Scan.kt
index 915b6fa..6eca4e8 100644
--- a/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/Scan.kt
+++ b/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/Scan.kt
@@ -26,12 +26,18 @@ import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.core.app.ActivityCompat
 import androidx.core.content.ContentProviderCompat.requireContext
 import androidx.core.content.ContextCompat
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
 import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.distinctUntilChanged
 import com.example.transactionapp.R
 import com.example.transactionapp.databinding.FragmentScanBinding
 import com.example.transactionapp.databinding.FragmentTransactionBinding
+import com.example.transactionapp.domain.db.model.Transaction
 import com.example.transactionapp.ui.viewmodel.auth.Auth
+import com.example.transactionapp.ui.viewmodel.location.LocationViewModel
 import com.example.transactionapp.ui.viewmodel.model.BillResponseSealed
+import com.example.transactionapp.ui.viewmodel.transaction.TransactionViewModel
 import okhttp3.MediaType
 import okhttp3.MediaType.Companion.toMediaTypeOrNull
 import okhttp3.MultipartBody
@@ -43,12 +49,15 @@ import java.util.concurrent.Executors
 import okhttp3.RequestBody.Companion.asRequestBody
 import okhttp3.RequestBody.Companion.toRequestBody
 import java.io.FileOutputStream
+import java.util.Date
 
 class Scan : Fragment() {
 
     private lateinit var binding: FragmentScanBinding
     private var imageCapture: ImageCapture? = null
     private lateinit var outputDirectory: File
+    private val db: TransactionViewModel by activityViewModels()
+    private val locationViewModel: LocationViewModel by activityViewModels()
 
     private lateinit var cameraExecutor: ExecutorService
 
@@ -60,6 +69,7 @@ class Scan : Fragment() {
     ): View? {
 
         binding = FragmentScanBinding.inflate(layoutInflater)
+        val billList = mutableListOf<Transaction>()
 
         outputDirectory = getOutputDirectory()
         cameraExecutor = Executors.newSingleThreadExecutor()
@@ -99,20 +109,39 @@ class Scan : Fragment() {
             }
         }
 
-        auth.billResponse.observe(requireActivity()) {
-            when (val data = it) {
+        auth.billResponse.observe(requireActivity(), Observer {billValue ->
+            when (val data = billValue) {
                 is BillResponseSealed.Success -> {
-                    Toast.makeText(requireContext(), data.data.toString(), Toast.LENGTH_SHORT).show()
+
+                    var locationValue = ""
+                    locationViewModel.location.observe(requireActivity()){ locationLambda ->
+                        locationValue = locationLambda
+                    }
+
+                    data.data.items.items.forEach {
+                        val transaction = Transaction(
+                            title = it.name,
+                            nominal = it.price.toLong() * 12000L,
+                            category = "Expense",
+                            createdAt = Date(),
+                            location = locationValue
+                        )
+                        if (!billList.contains(transaction)) {
+                            billList.add(transaction)
+                        }
+
+                    }
+
+                    db.insertBillTransaction(billList)
+                    db.changeAddStatus(true)
+                    auth.resetBillResponse()
                 }
                 is BillResponseSealed.Error -> {
                     Toast.makeText(requireContext(), data.message, Toast.LENGTH_SHORT).show()
-                    Log.d("ScanFragment", "onCreateView: ${data.message}")
-                }
-                is BillResponseSealed.Loading -> {
-                    Toast.makeText(requireContext(), "Loading", Toast.LENGTH_SHORT).show()
                 }
+                else -> {}
             }
-        }
+        })
 
         return binding.root
     }
@@ -151,7 +180,7 @@ class Scan : Fragment() {
     private fun sendBillToServer(bitmap: Bitmap) {
         val requestFile = bitmap.toString().toRequestBody("image/jpg".toMediaTypeOrNull())
         val body = MultipartBody.Part.createFormData("file", "image.jpg", requestFile)
-        val token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaW0iOiIxMzUyMTAyMSIsImlhdCI6MTcxMDg1NzA2MiwiZXhwIjoxNzEwODU3MzYyfQ.h-Y8ZStKG3tkrFYjoC-9Qb_fcVAb-DPxLPMCRRFBItk"
+        val token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaW0iOiIxMzUyMTAzMSIsImlhdCI6MTcxMDk5NTU0NywiZXhwIjoxNzEwOTk1ODQ3fQ._qxhoK5LGA6VznXOPxm7CODVr2OucU1ZNjPkGpKtzhs"
 
         auth.postBill("Bearer $token", body)
     }
diff --git a/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/Transaction.kt b/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/Transaction.kt
index d5df907..9def518 100644
--- a/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/Transaction.kt
+++ b/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/Transaction.kt
@@ -26,7 +26,7 @@ class Transaction : Fragment() {
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
-    ): View? {
+    ): View {
         val binding = FragmentTransactionBinding.inflate(layoutInflater)
 
         db.dateAll.observe(requireActivity()){
diff --git a/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/TransactionForm.kt b/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/TransactionForm.kt
index d29ae61..2c24510 100644
--- a/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/TransactionForm.kt
+++ b/app/src/main/java/com/example/transactionapp/ui/screen/mainmenu/fragment/TransactionForm.kt
@@ -22,6 +22,7 @@ import androidx.lifecycle.MutableLiveData
 import com.example.transactionapp.R
 import com.example.transactionapp.databinding.FragmentTransactionFormBinding
 import com.example.transactionapp.domain.db.model.Transaction
+import com.example.transactionapp.ui.viewmodel.location.LocationViewModel
 import com.example.transactionapp.ui.viewmodel.transaction.TransactionViewModel
 import com.example.transactionapp.utils.changeDateTypeToStandardDateLocal
 import com.google.android.gms.location.FusedLocationProviderClient
@@ -39,17 +40,15 @@ import java.util.Locale
 
 @AndroidEntryPoint
 class TransactionForm : Fragment() {
-    lateinit var fusedLocationProviderClient: FusedLocationProviderClient
-    private var cityName : MutableLiveData<String> = MutableLiveData()
+    private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
     private val db : TransactionViewModel by activityViewModels()
+    private val locationViewModel: LocationViewModel by activityViewModels()
 
     @RequiresApi(Build.VERSION_CODES.S)
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View {
-        RequestPermission()
-        NewLocationData()
 
         val binding = FragmentTransactionFormBinding.inflate(inflater)
 
@@ -76,7 +75,7 @@ class TransactionForm : Fragment() {
 
         binding.dateInput.text = changeDateTypeToStandardDateLocal(Date())
 
-        cityName.observe(viewLifecycleOwner){
+        locationViewModel.location.observe(viewLifecycleOwner){
             binding.locationInput.text = it
         }
 
@@ -101,7 +100,7 @@ class TransactionForm : Fragment() {
                         location = binding.locationInput.text.toString()
                     )
                 )
-                db.getAllDate()
+                db.changeAddStatus(true)
                 Toast.makeText(requireContext(), "Transaction Added", Toast.LENGTH_SHORT).show()
                 requireActivity().onBackPressed()
             }
@@ -109,6 +108,11 @@ class TransactionForm : Fragment() {
         return binding.root
     }
 
+    override fun onStart() {
+        super.onStart()
+        RequestPermission()
+    }
+
     fun RequestPermission(){
         ActivityCompat.requestPermissions(
             requireActivity(),
@@ -154,7 +158,7 @@ class TransactionForm : Fragment() {
         override fun onLocationResult(locationResult: LocationResult) {
             var lastLocation: Location? = locationResult.lastLocation
             if (lastLocation != null) {
-                cityName.postValue(getCityName(lastLocation.latitude, lastLocation.longitude))
+                locationViewModel.setLocation(getCityName(lastLocation.latitude, lastLocation.longitude))
             }
         }
     }
diff --git a/app/src/main/java/com/example/transactionapp/ui/viewmodel/auth/Auth.kt b/app/src/main/java/com/example/transactionapp/ui/viewmodel/auth/Auth.kt
index 8accccf..f3f038e 100644
--- a/app/src/main/java/com/example/transactionapp/ui/viewmodel/auth/Auth.kt
+++ b/app/src/main/java/com/example/transactionapp/ui/viewmodel/auth/Auth.kt
@@ -24,13 +24,13 @@ class Auth @Inject constructor(
 ): ViewModel(){
 
     private val _loginResponse = MutableLiveData<LoginResponseSealed>()
-    private val _billResponse = MutableLiveData<BillResponseSealed>()
+    private val _billResponse = MutableLiveData<BillResponseSealed?>()
     private val _tokenResponse = MutableLiveData<TokenResponseSealed>()
 
     val loginResponse: LiveData<LoginResponseSealed>
         get() = _loginResponse
 
-    val billResponse: LiveData<BillResponseSealed>
+    val billResponse: LiveData<BillResponseSealed?>
         get() = _billResponse
 
     val tokenResponse: LiveData<TokenResponseSealed>
@@ -71,4 +71,8 @@ class Auth @Inject constructor(
             }
         }
     }
+
+    fun resetBillResponse(){
+        _billResponse.postValue(null)
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/transactionapp/ui/viewmodel/location/LocationViewModel.kt b/app/src/main/java/com/example/transactionapp/ui/viewmodel/location/LocationViewModel.kt
new file mode 100644
index 0000000..bc83809
--- /dev/null
+++ b/app/src/main/java/com/example/transactionapp/ui/viewmodel/location/LocationViewModel.kt
@@ -0,0 +1,16 @@
+package com.example.transactionapp.ui.viewmodel.location
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+
+class LocationViewModel: ViewModel() {
+    private val _location = MutableLiveData<String>()
+
+    val location: MutableLiveData<String>
+        get() = _location
+
+    fun setLocation(location: String){
+        _location.postValue(location)
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/transactionapp/ui/viewmodel/transaction/Transaction.kt b/app/src/main/java/com/example/transactionapp/ui/viewmodel/transaction/Transaction.kt
index cc96f73..51732b5 100644
--- a/app/src/main/java/com/example/transactionapp/ui/viewmodel/transaction/Transaction.kt
+++ b/app/src/main/java/com/example/transactionapp/ui/viewmodel/transaction/Transaction.kt
@@ -1,11 +1,13 @@
 package com.example.transactionapp.ui.viewmodel.transaction
 
 import android.util.Log
+import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import com.example.transactionapp.R
+import com.example.transactionapp.domain.api.model.Items
 import com.example.transactionapp.domain.db.model.Transaction
 import com.example.transactionapp.domain.db.repo.TransactionDatabaseRepoImpl
 import com.example.transactionapp.ui.viewmodel.model.TransactionDate
@@ -29,6 +31,11 @@ class TransactionViewModel @Inject constructor(
     private val _cashFlow: MutableLiveData<Long> = MutableLiveData()
     private val _growth: MutableLiveData<Long> = MutableLiveData()
 
+    // STATUS
+    private val _addTransactionStatus: MutableLiveData<Boolean> = MutableLiveData()
+    private val _deleteTransactionStatus: MutableLiveData<Boolean> = MutableLiveData()
+    private val _updateTransactionStatus: MutableLiveData<Boolean> = MutableLiveData()
+
     val transaction: LiveData<List<Transaction>>
         get() = _transaction
 
@@ -44,21 +51,48 @@ class TransactionViewModel @Inject constructor(
     val growth: LiveData<Long>
         get() = _growth
 
+    val addTransactionStatus: LiveData<Boolean>
+        get() = _addTransactionStatus
+
+    val deleteTransactionStatus: LiveData<Boolean>
+        get() = _deleteTransactionStatus
+
+    val updateTransactionStatus: LiveData<Boolean>
+        get() = _updateTransactionStatus
+
     fun insertTransaction(transaction: Transaction) {
         viewModelScope.launch {
             transactionDatabaseRepoImpl.insertTransaction(transaction)
         }
     }
 
+    fun insertBillTransaction(items: List<Transaction>){
+        viewModelScope.launch {
+            items.forEach {
+                val transaction = Transaction(
+                    title = it.title,
+                    category = "Expense",
+                    nominal = it.nominal,
+                    location = it.location,
+                    createdAt = Date()
+                )
+                transactionDatabaseRepoImpl.insertTransaction(transaction)
+            }
+            changeAddStatus(true)
+        }
+    }
+
     fun deleteTransaction(transaction: Transaction) {
         viewModelScope.launch {
             transactionDatabaseRepoImpl.deleteTransaction(transaction)
+            _deleteTransactionStatus.postValue(true)
         }
     }
 
     fun updateTransaction(transaction: Transaction) {
         viewModelScope.launch {
             transactionDatabaseRepoImpl.updateTransaction(transaction)
+            _updateTransactionStatus.postValue(true)
         }
     }
 
@@ -92,7 +126,11 @@ class TransactionViewModel @Inject constructor(
             val data = TreeMap<String, MutableList<TransactionDate>>()
             val listData: MutableList<TransactionDateList> = mutableListOf()
             val response = transactionDatabaseRepoImpl.getAllTransactionsDesc()
+            val keySort = mutableListOf<String>()
             response.forEach {
+                if(!keySort.contains(changeDateTypeToStandardDateLocal(it.createdAt))){
+                    keySort.add(changeDateTypeToStandardDateLocal(it.createdAt))
+                }
                 if (!data.containsKey(changeDateTypeToStandardDateLocal(it.createdAt))){
                     data[changeDateTypeToStandardDateLocal(it.createdAt)] = mutableListOf()
                 }
@@ -121,11 +159,11 @@ class TransactionViewModel @Inject constructor(
 
             }
 
-            for((key, value) in data){
+            for(value in keySort){
                 listData.add(
                     TransactionDateList(
-                        date = key,
-                        listTransaction = value
+                        date = value,
+                        listTransaction = data[value] ?: mutableListOf()
                     )
                 )
             }
@@ -182,4 +220,30 @@ class TransactionViewModel @Inject constructor(
             _growth.postValue(growth)
         }
     }
+
+
+    fun resetAddTransactionStatus(){
+        _addTransactionStatus.postValue(false)
+    }
+
+    fun resetDeleteTransactionStatus(){
+        _deleteTransactionStatus.postValue(false)
+    }
+
+    fun resetUpdateTransactionStatus(){
+        _updateTransactionStatus.postValue(false)
+    }
+
+    fun changeAddStatus(status: Boolean){
+        _addTransactionStatus.postValue(status)
+    }
+
+    fun removeObserveAllData(lifecycleOwner: LifecycleOwner){
+        dateAll.removeObservers(lifecycleOwner)
+        transaction.removeObservers(lifecycleOwner)
+        balance.removeObservers(lifecycleOwner)
+        cashFlow.removeObservers(lifecycleOwner)
+        growth.removeObservers(lifecycleOwner)
+    }
+
 }
\ No newline at end of file
diff --git a/app/src/main/res/menu/button_menu.xml b/app/src/main/res/menu/button_menu.xml
index 56d2c36..fb7f193 100644
--- a/app/src/main/res/menu/button_menu.xml
+++ b/app/src/main/res/menu/button_menu.xml
@@ -9,7 +9,8 @@
         android:id="@+id/IbScanBtn"
         android:enabled="true"
         android:icon="@drawable/ic_scan_button" />
-    <item android:title=""/>
+    <item android:title=""
+        android:id="@+id/empty"/>
     <item android:title="Statistics"
         android:id="@+id/IbStatisticsBtn"
         android:enabled="true"
-- 
GitLab