From 3e017b9fc5ff5942624a5ca6fd6d73aea099bd6e Mon Sep 17 00:00:00 2001 From: bag <bagasaryo03@gmail.com> Date: Fri, 5 Apr 2024 09:39:46 +0700 Subject: [PATCH] fix:scan --- .../java/com/example/pbd_jwr/MainActivity.kt | 42 +- .../java/com/example/pbd_jwr/ScanActivity.kt | 366 ------------------ .../example/pbd_jwr/ui/scan/ScanFragment.kt | 348 +++++++++++++++++ .../example/pbd_jwr/ui/scan/ScanViewModel.kt | 13 + .../ui/transaction/TransactionDummyAdapter.kt | 6 +- .../{activity_scan.xml => fragment_scan.xml} | 14 +- .../main/res/navigation/mobile_navigation.xml | 6 +- 7 files changed, 379 insertions(+), 416 deletions(-) delete mode 100644 app/src/main/java/com/example/pbd_jwr/ScanActivity.kt create mode 100644 app/src/main/java/com/example/pbd_jwr/ui/scan/ScanFragment.kt create mode 100644 app/src/main/java/com/example/pbd_jwr/ui/scan/ScanViewModel.kt rename app/src/main/res/layout/{activity_scan.xml => fragment_scan.xml} (90%) 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 149cf8c..f2fb8e0 100644 --- a/app/src/main/java/com/example/pbd_jwr/MainActivity.kt +++ b/app/src/main/java/com/example/pbd_jwr/MainActivity.kt @@ -29,9 +29,6 @@ import com.example.pbd_jwr.encryptedSharedPref.EncryptedSharedPref import com.example.pbd_jwr.network.NetworkCallbackImplementation import com.example.pbd_jwr.ui.transaction.TransactionViewModel import com.google.android.material.floatingactionbutton.FloatingActionButton -import org.json.JSONObject -import java.util.Date -import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { @@ -74,14 +71,15 @@ class MainActivity : AppCompatActivity() { ) setupActionBarWithNavController(navController, appBarConfiguration) navView.setupWithNavController(navController) + val fab: FloatingActionButton = binding.fabScan fab.setOnClickListener { // Start ScanActivity - val intent = Intent(this, ScanActivity::class.java) - startScanActivityForResult.launch(intent) + navController.navigate(R.id.navigation_scan) } navView.setOnItemSelectedListener { menuItem -> when (menuItem.itemId) { + R.id.navigation_transaction -> { // Handle transaction navigation navController.navigate(R.id.navigation_transaction) @@ -191,40 +189,6 @@ class MainActivity : AppCompatActivity() { connectivityManager.unregisterNetworkCallback(networkCallback) } - private val startScanActivityForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - if (result.resultCode == Activity.RESULT_OK) { - - sharedPreferences = EncryptedSharedPref.create(applicationContext, "login") - val currentUserEmail = sharedPreferences.getString("email", "") ?: "" - - val data = result.data - val transactionDummyData = data?.getStringExtra("transactionDummyData") - - transactionDummyData?.let { - - val jsonObject = JSONObject(transactionDummyData) - val itemsArray = jsonObject.getJSONObject("items").getJSONArray("items") - - for (i in 0 until itemsArray.length()) { - val itemObject = itemsArray.getJSONObject(i) - val name = itemObject.getString("name") - val category = Category.EXPENSE - val price = itemObject.getDouble("price") - val qty = itemObject.getInt("qty") - val amount = (qty * price * 1000).roundToInt() / 1000.0 - val latitude = 6.8915 - val longitude = 107.6107 - val location = "Latitude: $latitude, Longitude: $longitude" - val date = Date().time - - mTransactionViewModel.addTransaction(Transaction(email = currentUserEmail, title = name, category = category, amount = amount, latitude = latitude, longitude = longitude, date = date)) - } - - - } - } - } - companion object { private const val LOCATION_PERMISSION_REQUEST_CODE = 1001 } diff --git a/app/src/main/java/com/example/pbd_jwr/ScanActivity.kt b/app/src/main/java/com/example/pbd_jwr/ScanActivity.kt deleted file mode 100644 index c69d98b..0000000 --- a/app/src/main/java/com/example/pbd_jwr/ScanActivity.kt +++ /dev/null @@ -1,366 +0,0 @@ -package com.example.pbd_jwr - -import android.content.Intent -import android.graphics.Bitmap -import android.os.Bundle -import android.provider.MediaStore -import android.widget.Button -import android.widget.ImageView -import androidx.appcompat.app.AppCompatActivity -import android.content.pm.PackageManager -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat -import android.Manifest -import android.annotation.SuppressLint -import android.app.Activity -import android.app.AlertDialog -import android.content.Context -import android.content.SharedPreferences -import android.graphics.BitmapFactory -import android.net.Uri -import android.widget.Toast -import okhttp3.* -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import java.io.File -import java.io.IOException -import android.os.Environment -import android.util.Log -import android.view.LayoutInflater -import android.widget.ListView -import androidx.core.content.FileProvider -import com.example.pbd_jwr.databinding.ActivityMainBinding -import com.example.pbd_jwr.databinding.ActivityScanBinding -import com.example.pbd_jwr.encryptedSharedPref.EncryptedSharedPref -import com.example.pbd_jwr.ui.transaction.TransactionDummyAdapter -import okhttp3.RequestBody.Companion.asRequestBody -import org.json.JSONObject -import java.io.FileOutputStream -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale - - -class ScanActivity : AppCompatActivity() { - - private var imageUri: Uri? = null - private var currentPhotoPath: String = "" - private lateinit var sharedPreferences: SharedPreferences - var transactionDummyData: String = "" - private lateinit var binding: ActivityScanBinding - companion object { - const val REQUEST_IMAGE_CAPTURE = 1 - const val REQUEST_CAMERA_PERMISSION = 2 - const val PICK_IMAGE = 3 - const val REQUEST_STORAGE_PERMISSION = 4 - } - - data class TransactionDummy( - val name: String, - val qty: Int, - val price: Double - ) - - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_scan) - binding = ActivityScanBinding.inflate(layoutInflater) - setContentView(binding.root) - sharedPreferences = EncryptedSharedPref.create(applicationContext,"login") - - val scanButton: Button = binding.scanBtn - val galleryBtn: Button = binding.galleryBtn - val uploadButton: Button = binding.uploadBtn - val saveBtn: Button = binding.saveTransactionsBtn - - val placeholderImage: ImageView = binding.imageView - placeholderImage.setImageResource(R.drawable.baseline_insert_photo_24) - addDummyTransactions("{\"items\":{\"items\":[{\"name\":\"none\",\"qty\":0,\"price\":0}]}}") - - scanButton.setOnClickListener { - checkAndRequestPermissions() - } - - galleryBtn.setOnClickListener { - openGallery() - } - - uploadButton.setOnClickListener { - if (imageUri != null) { - uploadImage(imageUri!!) - } else { - Toast.makeText(this, "No image selected or taken", Toast.LENGTH_SHORT).show() - } - } - - - saveBtn.setOnClickListener { - handleSave() - } - } - - private fun checkAndRequestPermissions() { - val permissionsNeeded = mutableListOf<String>() - - if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - permissionsNeeded.add(Manifest.permission.CAMERA) - } - - if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - permissionsNeeded.add(Manifest.permission.READ_EXTERNAL_STORAGE) - } - - if (permissionsNeeded.isNotEmpty()) { - ActivityCompat.requestPermissions(this, permissionsNeeded.toTypedArray(), REQUEST_STORAGE_PERMISSION) - } else { - // Semua izin sudah diberikan - openCamera() - } - } - - @SuppressLint("InflateParams") - private fun handleSave() { - if (transactionDummyData == "") { - Toast.makeText(this, "No transactions read", Toast.LENGTH_SHORT).show() - } else { - val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_confirm_transaction, null) - val dialogBuilder = AlertDialog.Builder(this).apply { - setView(dialogView) - setCancelable(true) // Memungkinkan dialog untuk dibatalkan dengan tombol back atau mengetuk di luar - } - val dialog = dialogBuilder.create() - - dialogView.findViewById<Button>(R.id.btnCancel).setOnClickListener { - dialog.dismiss() - } - - dialogView.findViewById<Button>(R.id.btnOkay).setOnClickListener { - val resultIntent = Intent() - resultIntent.putExtra("transactionDummyData", transactionDummyData) - setResult(Activity.RESULT_OK, resultIntent) - dialog.dismiss() - - - finish() - } - - dialog.show() - } - } - - private fun openCamera() { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) - } else { - Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent -> - takePictureIntent.resolveActivity(packageManager)?.also { - - val photoFile: File? = try { - createImageFile() - } catch (ex: IOException) { - // Error occurred while creating the File - null - } - - photoFile?.also { - val photoURI: Uri = FileProvider.getUriForFile( - this, - "JWR.provider", - it - ) - Log.d("ScanActivity", "Uri: $photoURI") - takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI) - imageUri = photoURI // Simpan Uri untuk digunakan nanti - startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE) - } - } - } - } - } - - private fun startCameraIntent() { - Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent -> - takePictureIntent.resolveActivity(packageManager)?.also { - startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE) - } - } - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - when (requestCode) { - REQUEST_STORAGE_PERMISSION -> { - if (grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED }) { - openCamera() - } else { - Toast.makeText(this, "Storage permission is required", Toast.LENGTH_SHORT).show() - } - } - REQUEST_CAMERA_PERMISSION -> { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startCameraIntent() - } else { - Toast.makeText(this, "Camera permission is required", Toast.LENGTH_SHORT).show() - } - } - } - } - - private fun openGallery() { - val intent = Intent(Intent.ACTION_PICK) - intent.type = "image/*" - startActivityForResult(intent, PICK_IMAGE) - } - - private fun getBitmapFromUri(uri: Uri, context: Context): Bitmap? { - val inputStream = context.contentResolver.openInputStream(uri) - return BitmapFactory.decodeStream(inputStream) - } - - private fun compressAndSaveBitmap(bitmap: Bitmap, context: Context): File { - // Membuat file output di direktori cache eksternal - val compressedFile = File(context.externalCacheDir, "compressed_${System.currentTimeMillis()}.jpg") - - FileOutputStream(compressedFile).use { outStream -> - // Kompres bitmap ke format JPEG dengan kualitas 75% (sesuaikan sesuai kebutuhan) - bitmap.compress(Bitmap.CompressFormat.JPEG, 75, outStream) - outStream.flush() - } - - return compressedFile - } - - @SuppressLint("Recycle") - private fun uploadImage(imageUri: Uri) { - val token = sharedPreferences.getString("token", null) - if (token.isNullOrEmpty()) { - runOnUiThread { - Toast.makeText(this, "Token not found.", Toast.LENGTH_SHORT).show() - } - return - } - - val bitmap = getBitmapFromUri(imageUri, this) - if (bitmap != null) { - // Kompres dan simpan bitmap ke file baru - val compressedFile = compressAndSaveBitmap(bitmap, this) - - val requestBody = MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("file", compressedFile.name, compressedFile.asRequestBody("image/jpeg".toMediaTypeOrNull())) - .build() - - val request = Request.Builder() - .url("https://pbd-backend-2024.vercel.app/api/bill/upload") - .addHeader("Authorization", "Bearer $token") - .post(requestBody) - .build() - - OkHttpClient().newCall(request).enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - runOnUiThread { Toast.makeText(this@ScanActivity, "Failed to upload image", Toast.LENGTH_SHORT).show() } - } - - override fun onResponse(call: Call, response: Response) { - runOnUiThread { - if (response.isSuccessful) { - val responseBody = response.body?.string() - - Log.d("Response", "response body: $responseBody") - - if (responseBody != null) { - transactionDummyData = responseBody - addDummyTransactions(responseBody) - Log.d("Response", "transactionDummyData: $transactionDummyData") - } - - Toast.makeText(this@ScanActivity, "Image uploaded successfully", Toast.LENGTH_SHORT).show() - } else { - Toast.makeText(this@ScanActivity, "Server error: ${response.code}", Toast.LENGTH_SHORT).show() - } - } - } - }) - - } else { - Toast.makeText(this, "Failed to read image", Toast.LENGTH_SHORT).show() - } - } - - @Throws(IOException::class) - private fun createImageFile(): File { - val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) - val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES)!! - return File.createTempFile("JPEG_${timeStamp}_", ".jpg", storageDir).apply { - currentPhotoPath = absolutePath - } - } - - private fun addDummyTransactions(response: String) { - val transactions = parseTransactions(response) - - // Tampilkan data transaksi pada ListView - val listView: ListView = findViewById(R.id.listDummyTransaction) - val adapter = TransactionDummyAdapter(this, transactions) - listView.adapter = adapter - } - - private fun parseTransactions(responseBody: String): MutableList<TransactionDummy> { - val transactions = mutableListOf<TransactionDummy>() - responseBody.let { - val jsonObject = JSONObject(it) - val itemsObject = jsonObject.getJSONObject("items") - val itemsArray = itemsObject.getJSONArray("items") - - for (i in 0 until itemsArray.length()) { - val itemObject = itemsArray.getJSONObject(i) - val name = itemObject.getString("name") - val qty = itemObject.getInt("qty") - val price = itemObject.getDouble("price") - val transaction = TransactionDummy(name, qty, price) - transactions.add(transaction) - } - } - return transactions - } - - private fun deleteImageFile() { - imageUri?.let { uri -> - try { - val contentResolver = applicationContext.contentResolver - val deleteSuccess = contentResolver.delete(uri, null, null) > 0 - if (deleteSuccess) { - Log.d("ScanActivity", "Image deleted successfully.") - } else { - Log.d("ScanActivity", "Failed to delete image.") - } - } catch (e: Exception) { - Log.e("ScanActivity", "Error deleting image", e) - } - } - } - - - override fun finish() { - deleteImageFile() - super.finish() - } - - @Deprecated("Deprecated in Java") - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { - val imageUri = Uri.parse(currentPhotoPath) - imageUri?.let { - findViewById<ImageView>(R.id.imageView).setImageURI(it) - } - } else if (requestCode == PICK_IMAGE && resultCode == RESULT_OK) { - data?.data?.let { uri -> - findViewById<ImageView>(R.id.imageView).setImageURI(uri) - this.imageUri = uri // Simpan Uri dari gambar yang dipilih - } - } - } -} - diff --git a/app/src/main/java/com/example/pbd_jwr/ui/scan/ScanFragment.kt b/app/src/main/java/com/example/pbd_jwr/ui/scan/ScanFragment.kt new file mode 100644 index 0000000..8bc6e6e --- /dev/null +++ b/app/src/main/java/com/example/pbd_jwr/ui/scan/ScanFragment.kt @@ -0,0 +1,348 @@ +package com.example.pbd_jwr.ui.scan + +import android.Manifest +import android.annotation.SuppressLint +import android.app.Activity.RESULT_OK +import android.app.AlertDialog +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.net.Uri +import android.os.Bundle +import android.os.Environment +import android.provider.MediaStore +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageView +import android.widget.ListView +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import com.example.pbd_jwr.R +import com.example.pbd_jwr.data.entity.Transaction +import com.example.pbd_jwr.data.model.Category +//import com.example.pbd_jwr.ScanActivity +import com.example.pbd_jwr.databinding.FragmentScanBinding +import com.example.pbd_jwr.encryptedSharedPref.EncryptedSharedPref +import com.example.pbd_jwr.ui.transaction.TransactionDummyAdapter +import com.example.pbd_jwr.ui.transaction.TransactionViewModel +import okhttp3.* +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.asRequestBody +import org.json.JSONObject +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.text.SimpleDateFormat +import java.util.* +import kotlin.math.roundToInt + +class ScanFragment : Fragment() { + + private var imageUri: Uri? = null + private var currentPhotoPath: String = "" + private lateinit var sharedPreferences: SharedPreferences + var transactionDummyData: String = "" + private var _binding: FragmentScanBinding? = null + private lateinit var mTransactionViewModel: TransactionViewModel + + + private val binding get() = _binding!! + + companion object { + const val REQUEST_CAMERA_PERMISSION = 101 + const val REQUEST_STORAGE_PERMISSION = 102 + const val REQUEST_IMAGE_CAPTURE = 1 + const val PICK_IMAGE = 3 + } + + data class TransactionDummy( + val name: String, + val qty: Int, + val price: Double + ) + + private val startCamera = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + val imageUri = Uri.parse(currentPhotoPath) + binding.imageView.setImageURI(imageUri) + binding.uploadBtn.isEnabled = true + } + } + + private val pickImage = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> + uri?.let { + binding.imageView.setImageURI(it) + imageUri = it + binding.uploadBtn.isEnabled = true + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = FragmentScanBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.uploadBtn.isEnabled = false + binding.saveTransactionsBtn.isEnabled = false + + mTransactionViewModel = ViewModelProvider(this)[TransactionViewModel::class.java] + sharedPreferences = EncryptedSharedPref.create(requireContext(), "login") + resetTransaction() + + binding.scanBtn.setOnClickListener { + checkAndRequestPermissions(Manifest.permission.CAMERA, REQUEST_CAMERA_PERMISSION) { + openCamera() + } + } + + binding.galleryBtn.setOnClickListener { + pickImage.launch("image/*") + } + + binding.uploadBtn.setOnClickListener { + imageUri?.let { uri -> + uploadImage(uri) + } ?: run { + Toast.makeText(context, "No image selected or taken", Toast.LENGTH_SHORT).show() + } + } + + binding.saveTransactionsBtn.setOnClickListener { + handleSave() + } + } + + private fun checkAndRequestPermissions(permission: String, requestCode: Int, grantedAction: () -> Unit) { + when { + ContextCompat.checkSelfPermission(requireContext(), permission) != PackageManager.PERMISSION_GRANTED -> { + requestPermissions(arrayOf(permission), requestCode) + } + else -> grantedAction() + } + } + + @Deprecated("Deprecated in Java") + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + when (requestCode) { + REQUEST_CAMERA_PERMISSION -> openCamera() + // Handle other permissions if necessary + } + } else { + Toast.makeText(context, "Permission denied", Toast.LENGTH_SHORT).show() + } + } + + private fun openCamera() { + val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) + activity?.packageManager?.let { packageManager -> + takePictureIntent.resolveActivity(packageManager)?.also { + val photoFile: File? = try { + createImageFile() + } catch (ex: IOException) { + // Error occurred while creating the File + null + } + photoFile?.also { + val photoURI: Uri = FileProvider.getUriForFile(requireContext(), "JWR.provider", it) + startCamera.launch(Intent(takePictureIntent).apply { + putExtra(MediaStore.EXTRA_OUTPUT, photoURI) + }) + imageUri = photoURI + } + } + } + } + + private fun openGallery() { + val intent = Intent(Intent.ACTION_PICK) + intent.type = "image/*" + startActivityForResult(intent, PICK_IMAGE) + } + + private fun uploadImage(imageUri: Uri) { + val token = sharedPreferences.getString("token", null) + if (token.isNullOrEmpty()) { + activity?.runOnUiThread { + Toast.makeText(requireContext(), "Token not found.", Toast.LENGTH_SHORT).show() + } + return + } + + val bitmap = getBitmapFromUri(imageUri, requireContext()) + if (bitmap != null) { + val compressedFile = compressAndSaveBitmap(bitmap, requireContext()) + + // Setup request with OkHttpClient as before + val requestBody = MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("file", compressedFile.name, compressedFile.asRequestBody("image/jpeg".toMediaTypeOrNull())) + .build() + + val request = Request.Builder() + .url("https://pbd-backend-2024.vercel.app/api/bill/upload") + .addHeader("Authorization", "Bearer $token") + .post(requestBody) + .build() + + OkHttpClient().newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + activity?.runOnUiThread { + Toast.makeText(requireContext(), "Failed to upload image", Toast.LENGTH_SHORT).show() + } + } + + override fun onResponse(call: Call, response: Response) { + activity?.runOnUiThread { + if (response.isSuccessful) { + val responseBody = response.body?.string() +// + Log.d("Response", "response body: $responseBody") + + if (responseBody != null) { + transactionDummyData = responseBody + addDummyTransactions(transactionDummyData) + Log.d("Response", "transactionDummyData: $transactionDummyData") + } + + binding.saveTransactionsBtn.isEnabled = true + binding.uploadBtn.isEnabled = false + + Toast.makeText(requireContext(), "Image uploaded successfully", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(requireContext(), "Server error: ${response.code}", Toast.LENGTH_SHORT).show() + } + } + } + }) + } else { + Toast.makeText(requireContext(), "Failed to read image", Toast.LENGTH_SHORT).show() + } + } + + @SuppressLint("InflateParams") + private fun handleSave() { + if (transactionDummyData.isEmpty()) { + Toast.makeText(context, "No transactions read", Toast.LENGTH_SHORT).show() + } else { + // Inflate the dialog layout + val dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_confirm_transaction, null) + val dialogBuilder = AlertDialog.Builder(requireContext()).apply { + setView(dialogView) + setCancelable(true) // Allows dialog to be canceled with back button or by tapping outside + } + + val dialog = dialogBuilder.create() + + dialogView.findViewById<Button>(R.id.btnCancel).setOnClickListener { + dialog.dismiss() + } + + dialogView.findViewById<Button>(R.id.btnOkay).setOnClickListener { + + val jsonObject = JSONObject(transactionDummyData) + val itemsArray = jsonObject.getJSONObject("items").getJSONArray("items") + + for (i in 0 until itemsArray.length()) { + val itemObject = itemsArray.getJSONObject(i) + val name = itemObject.getString("name") + val category = Category.EXPENSE + val price = itemObject.getDouble("price") + val qty = itemObject.getInt("qty") + val amount = (qty * price * 1000).roundToInt() / 1000.0 + val latitude = 6.8915 + val longitude = 107.6107 + val currentUserEmail = sharedPreferences.getString("email", "") ?: "" + val date = Date().time + + mTransactionViewModel.addTransaction(Transaction(email = currentUserEmail, title = name, category = category, amount = amount, latitude = latitude, longitude = longitude, date = date)) + } + dialog.dismiss() + resetTransaction() + Toast.makeText(context, "Transactions saved", Toast.LENGTH_SHORT).show() + } + + dialog.show() + } + } + + private fun resetTransaction() { + binding.uploadBtn.isEnabled = false + binding.saveTransactionsBtn.isEnabled = false + + binding.imageView.setImageResource(R.drawable.baseline_insert_photo_24) + addDummyTransactions("{\"items\":{\"items\":[{\"name\":\"none\",\"qty\":0,\"price\":0}]}}") + } + + + private fun getBitmapFromUri(uri: Uri, context: Context): Bitmap? { + val inputStream = context.contentResolver.openInputStream(uri) + return BitmapFactory.decodeStream(inputStream) + } + private fun compressAndSaveBitmap(bitmap: Bitmap, context: Context): File { + val compressedFile = File(context.externalCacheDir, "compressed_${System.currentTimeMillis()}.jpg") + FileOutputStream(compressedFile).use { outStream -> + bitmap.compress(Bitmap.CompressFormat.JPEG, 75, outStream) + outStream.flush() + } + return compressedFile + } + @Throws(IOException::class) + private fun createImageFile(): File { + val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) + val storageDir: File? = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES) + return File.createTempFile( + "JPEG_${timeStamp}_", /* prefix */ + ".jpg", /* suffix */ + storageDir /* directory */ + ).also { + currentPhotoPath = it.absolutePath + } + } + + + private fun addDummyTransactions(response: String) { + val transactions = parseTransactions(response) + + // Pastikan view sudah dibuat dan context tersedia sebelum mengakses binding atau requireContext() + val adapter = TransactionDummyAdapter(requireContext(), transactions) + + // Gunakan binding untuk mengakses ListView dan menetapkan adapter + binding.listDummyTransaction.adapter = adapter + } + + + private fun parseTransactions(responseBody: String): MutableList<TransactionDummy> { + val transactions = mutableListOf<TransactionDummy>() + responseBody.let { + val jsonObject = JSONObject(it) + val itemsObject = jsonObject.getJSONObject("items") + val itemsArray = itemsObject.getJSONArray("items") + + for (i in 0 until itemsArray.length()) { + val itemObject = itemsArray.getJSONObject(i) + val name = itemObject.getString("name") + val qty = itemObject.getInt("qty") + val price = itemObject.getDouble("price") + val transaction = TransactionDummy(name, qty, price) + transactions.add(transaction) + } + } + return transactions + } + +} 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 new file mode 100644 index 0000000..953040d --- /dev/null +++ b/app/src/main/java/com/example/pbd_jwr/ui/scan/ScanViewModel.kt @@ -0,0 +1,13 @@ +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/transaction/TransactionDummyAdapter.kt b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionDummyAdapter.kt index 7de77e9..529ccd7 100644 --- a/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionDummyAdapter.kt +++ b/app/src/main/java/com/example/pbd_jwr/ui/transaction/TransactionDummyAdapter.kt @@ -7,11 +7,11 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter -import com.example.pbd_jwr.ScanActivity +import com.example.pbd_jwr.ui.scan.ScanFragment import com.example.pbd_jwr.databinding.TransactionDummyDetailBinding -class TransactionDummyAdapter(context: Context, transactions: List<ScanActivity.TransactionDummy>) : - ArrayAdapter<ScanActivity.TransactionDummy>(context, 0, transactions) { +class TransactionDummyAdapter(context: Context, transactions: List<ScanFragment.TransactionDummy>) : + ArrayAdapter<ScanFragment.TransactionDummy>(context, 0, transactions) { @SuppressLint("SetTextI18n") override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { diff --git a/app/src/main/res/layout/activity_scan.xml b/app/src/main/res/layout/fragment_scan.xml similarity index 90% rename from app/src/main/res/layout/activity_scan.xml rename to app/src/main/res/layout/fragment_scan.xml index f0d80f9..eec9854 100644 --- a/app/src/main/res/layout/activity_scan.xml +++ b/app/src/main/res/layout/fragment_scan.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ScanActivity"> + tools:context=".ui.scan.ScanFragment"> <Button android:id="@+id/scanBtn" @@ -46,7 +46,6 @@ android:layout_width="335dp" android:layout_height="335dp" android:contentDescription="PreviewImg" - app:layout_constraintBottom_toTopOf="@+id/listDummyTransaction" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -67,13 +66,13 @@ <ListView android:id="@+id/listDummyTransaction" - android:layout_width="324dp" - android:layout_height="211dp" - android:layout_marginBottom="16dp" + android:layout_width="323dp" + android:layout_height="193dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.494" - app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/imageView" /> <TextView android:id="@+id/textView" @@ -84,6 +83,7 @@ app:layout_constraintBottom_toTopOf="@+id/listDummyTransaction" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.165" - app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/scanBtn" /> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index db2ab37..b9e8c33 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -51,7 +51,11 @@ 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/fragment_scan" /> <fragment -- GitLab