diff --git a/app/src/main/java/com/example/bondoman/database/repository/TransactionRepository.kt b/app/src/main/java/com/example/bondoman/database/repository/TransactionRepository.kt index e86553243eee8ed104049e1d19ae4a574d46920f..3638eef01a24aad7caa8c541001e1e6f65af4256 100644 --- a/app/src/main/java/com/example/bondoman/database/repository/TransactionRepository.kt +++ b/app/src/main/java/com/example/bondoman/database/repository/TransactionRepository.kt @@ -38,11 +38,11 @@ class TransactionRepository(private val transactionDao: TransactionDao) { transactionDao.deleteAll() } - suspend fun postUploadNota(imageReqBody: RequestBody): Boolean { + suspend fun postUploadNota(imageReqBody: RequestBody): List<TransactionEntity> { try { // TODO: Get stored auth token val authToken = - "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaW0iOiIxMzUyMTE0OSIsImlhdCI6MTcxMTgxNTE1OCwiZXhwIjoxNzExODE1NDU4fQ.mASnON98EJmGWmVMdjWB47ef3pwYIVJelaYMpMjy_BY" + "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaW0iOiIxMzUyMTE0OSIsImlhdCI6MTcxMTkxOTI3NCwiZXhwIjoxNzExOTE5NTc0fQ.eek7Uf1vXidl8J7ns04K3lBRGlqCjtIxwZvhPVzT6cE" val response = RetrofitClient.uploadInstance.uploadImage( MultipartBody.Part.createFormData( "file", "test", imageReqBody @@ -51,32 +51,26 @@ class TransactionRepository(private val transactionDao: TransactionDao) { if (!response.isSuccessful) { Log.d(TAG, response.code().toString()) - return false + return emptyList() } - for (item in response.body()!!.items.items) { - insert( - TransactionEntity( - id = 0, title = item.name, - // TODO: Category - category = "scanned", amount = item.qty * item.price.toInt(), - // TODO: Location - latitude = null, - longitude = null, - timestamp = SimpleDateFormat( - "yyyy-MM-dd HH:mm:ss", Locale.getDefault() - ).format( - Date() - ) + return response.body()!!.items.items.map { item -> + TransactionEntity( + id = 0, title = item.name, + // TODO: Category + category = "scanned", amount = item.qty * item.price.toInt(), + // TODO: Location + latitude = null, longitude = null, timestamp = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format( + Date() ) ) } - - return true } catch (e: Exception) { Log.e(TAG, e.toString()) - return false + return emptyList() } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanAdapter.kt b/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..9a4aef30761322a56114c3261c38556d60883e39 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanAdapter.kt @@ -0,0 +1,54 @@ +package com.example.bondoman.ui.hub.scan + +import android.content.Context +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.example.bondoman.database.entity.TransactionEntity +import com.example.bondoman.databinding.ItemTransactionBinding +import java.text.NumberFormat +import java.util.Locale + +class ScanAdapter(private val context: Context, private val tsList: List<TransactionEntity>) : + RecyclerView.Adapter<ScanAdapter.ViewHolder>() { + inner class ViewHolder(val view: ItemTransactionBinding) : RecyclerView.ViewHolder(view.root) + + private lateinit var transaction: TransactionEntity + private lateinit var binding: ItemTransactionBinding + + companion object { + val locale: Locale = Locale("id", "ID") + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + Log.d("ScanAdapter", "masuk 2") + binding = ItemTransactionBinding.inflate(LayoutInflater.from(context), parent, false) + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.view.apply { + tvDate.text = tsList[position].timestamp.split(" ")[0] + tvCategory.text = tsList[position].category + tvTitle.text = tsList[position].title + + val formatter: NumberFormat = NumberFormat.getNumberInstance(locale) + val amount = "Rp${formatter.format(tsList[position].amount)}" + tvAmount.text = amount + + btnLocation.visibility = View.GONE + tvLocation.visibility = View.GONE + locationIcon.visibility = View.GONE + + btnDelete.visibility = View.GONE + btnEdit.visibility = View.GONE + } + } + + override fun getItemCount(): Int { + Log.d("ScanAdapter", tsList.size.toString()) + return tsList.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanDialogFragment.kt b/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanDialogFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..20d62bbe3575b4d3072a58b2294ab71f97350c1a --- /dev/null +++ b/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanDialogFragment.kt @@ -0,0 +1,61 @@ +package com.example.bondoman.ui.hub.scan + +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import com.example.bondoman.database.AppDatabase +import com.example.bondoman.database.entity.TransactionEntity +import com.example.bondoman.database.repository.TransactionRepository +import com.example.bondoman.databinding.FragmentDialogScanBinding +import com.example.bondoman.viewmodel.scan.ScanViewModel +import com.example.bondoman.viewmodel.scan.ScanViewModelFactory +import kotlinx.coroutines.launch + +class ScanDialogFragment : DialogFragment() { + private lateinit var binding: FragmentDialogScanBinding + private lateinit var scanViewModel: ScanViewModel + + val scannedTransactions = MutableLiveData(emptyList<TransactionEntity>()) + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentDialogScanBinding.inflate(inflater, container, false) + + val database = AppDatabase.getInstance(requireActivity()) + val transactionRepo = TransactionRepository(database.transactionDao) + val scanViewModelFactory = ScanViewModelFactory(transactionRepo) + scanViewModel = ViewModelProvider(this, scanViewModelFactory)[ScanViewModel::class.java] + + binding.rvScanned.layoutManager = LinearLayoutManager(requireContext()) + val adapter = scannedTransactions.value?.let { ScanAdapter(requireContext(), it) } + binding.rvScanned.adapter = adapter + + binding.btnRescan.setOnClickListener(::onRescanClick) + binding.btnSave.setOnClickListener(::onSaveClick) + + return binding.root + } + + override fun onDismiss(dialog: DialogInterface) { + scannedTransactions.value = emptyList() + } + + private fun onRescanClick(view: View) = dismiss() + + private fun onSaveClick(view: View) { + scannedTransactions.value?.let { it1 -> + viewLifecycleOwner.lifecycleScope.launch { + scanViewModel.saveScanned(it1) + dismiss() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanFragment.kt b/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanFragment.kt index 9d243c1dfd9510bd5f1d295f6755b164914a6f68..d4e2f2c92d7bf0026309cfc33a94cba50c6c9f05 100644 --- a/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanFragment.kt +++ b/app/src/main/java/com/example/bondoman/ui/hub/scan/ScanFragment.kt @@ -45,6 +45,8 @@ class ScanFragment : Fragment() { private var imageCapture: ImageCapture? = null private lateinit var cameraExecutor: ExecutorService + private val scanDialog: ScanDialogFragment = ScanDialogFragment() + private val activityResultLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission() ) { isGranted: Boolean -> @@ -90,12 +92,8 @@ class ScanFragment : Fragment() { viewLifecycleOwner, cameraPermissionObserver ) - binding.imageCaptureButton.setOnClickListener { - takePhoto() - } - binding.selectPhotoButton.setOnClickListener { - selectPhoto() - } + binding.imageCaptureButton.setOnClickListener(::onImageCaptureClick) + binding.selectPhotoButton.setOnClickListener(::onSelectPhotoClick) cameraExecutor = Executors.newSingleThreadExecutor() @@ -168,7 +166,7 @@ class ScanFragment : Fragment() { }, ContextCompat.getMainExecutor(requireActivity())) } - private fun takePhoto() { + private fun onImageCaptureClick(view: View) { val imageCapture = imageCapture ?: return imageCapture.takePicture(ContextCompat.getMainExecutor(requireActivity()), @@ -190,25 +188,20 @@ class ScanFragment : Fragment() { val imageReqBody = RequestBody.create(MediaType.parse("image/*"), byteArray) viewLifecycleOwner.lifecycleScope.launchWhenStarted { - val success = scanViewModel.uploadNota(imageReqBody) + val scannedTransactions = scanViewModel.uploadNota(imageReqBody) - if (success) { - Toast.makeText( - requireActivity(), - getString(R.string.scan_add_toast_success), - Toast.LENGTH_SHORT - ).show() + if (scannedTransactions.isNotEmpty()) { + scanDialog.scannedTransactions.value = scannedTransactions + scanDialog.show(parentFragmentManager, "scan") } else { Toast.makeText( requireActivity(), getString(R.string.scan_add_toast_failed), Toast.LENGTH_SHORT ).show() } } - - } - private fun selectPhoto() { + private fun onSelectPhotoClick(view: View) { val intent = Intent(Intent.ACTION_GET_CONTENT) intent.type = "image/*" startActivityForResult(intent, 1) diff --git a/app/src/main/java/com/example/bondoman/viewmodel/scan/ScanViewModel.kt b/app/src/main/java/com/example/bondoman/viewmodel/scan/ScanViewModel.kt index 117402c85f119c656ad71b56715f4fd8b907a413..bac7cfc971ae67e9d7276145316c606bce9958ee 100644 --- a/app/src/main/java/com/example/bondoman/viewmodel/scan/ScanViewModel.kt +++ b/app/src/main/java/com/example/bondoman/viewmodel/scan/ScanViewModel.kt @@ -2,13 +2,20 @@ package com.example.bondoman.viewmodel.scan import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.example.bondoman.database.entity.TransactionEntity import com.example.bondoman.database.repository.TransactionRepository import okhttp3.RequestBody class ScanViewModel(private val repository: TransactionRepository) : ViewModel() { val isCameraPermissionGranted = MutableLiveData(false) - suspend fun uploadNota(imageReqBody: RequestBody): Boolean { + suspend fun uploadNota(imageReqBody: RequestBody): List<TransactionEntity> { return repository.postUploadNota(imageReqBody) } + + suspend fun saveScanned(scannedTransactions: List<TransactionEntity>) { + for (item in scannedTransactions) { + repository.insert(item) + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_dialog_scan.xml b/app/src/main/res/layout/fragment_dialog_scan.xml new file mode 100644 index 0000000000000000000000000000000000000000..03c8e887c32cce4f5dce7c29aa9dc97c77959a70 --- /dev/null +++ b/app/src/main/res/layout/fragment_dialog_scan.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".ui.hub.scan.ScanDialogFragment"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/rvScanned" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/btnRescan" + tools:listitem="@layout/item_transaction" /> + + <Button + android:id="@+id/btnRescan" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/scan_rescan" + app:layout_constraintEnd_toStartOf="@+id/btnSave" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <Button + android:id="@+id/btnSave" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/scan_save" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toEndOf="@+id/btnRescan" + app:layout_constraintTop_toTopOf="parent" /> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_scan.xml b/app/src/main/res/layout/fragment_scan.xml index b41fe01cf251c62d6cfa3ac9888b78ef2fc503ff..93af8b874423478647422edf8ebd1f06a5ed5795 100644 --- a/app/src/main/res/layout/fragment_scan.xml +++ b/app/src/main/res/layout/fragment_scan.xml @@ -4,8 +4,8 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ui.hub.scan.ScanFragment" - android:background="?attr/background"> + android:background="?attr/background" + tools:context=".ui.hub.scan.ScanFragment"> <androidx.camera.view.PreviewView android:id="@+id/viewFinder" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 36dd85dddb31c2d6f34e41db634996aa5c11279a..691cd10ed1acd8bc4b13b50cded288c03989d4d4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -55,8 +55,10 @@ <string name="select_photo">Select Photo</string> <string name="scan_add_toast_success">Successfully added new transactions</string> - <string name="scan_add_toast_failed">Failed to add new transactions</string> + <string name="scan_add_toast_failed">Failed to get response from server</string> <string name="scan_enable_camera">Please allow camera permission to access this feature</string> + <string name="scan_rescan">Rescan</string> + <string name="scan_save">Save</string> <string name="twibbon_add_toast_success">Successfully saved twibbon</string> <string name="twibbon_add_toast_failed">Failed to save twibbon</string>