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