diff --git a/.editorconfig b/.editorconfig index f730903d042089f40969b72aba113ae81689d15d..6571f9db65ae1cfbb7868c12601f696ff1babd4d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,7 +8,8 @@ trim_trailing_whitespace = true [*.{kt,kts}] indent_style = tab indent_size = 4 -ktlint_code_style=ktlint_official +ktlint_code_style = ktlint_official ktlint_standard_package-name = disabled ktlint_standard_no-wildcard-imports = disabled -ktlint_standard_no-consecutive-comments = disabled \ No newline at end of file +ktlint_standard_no-consecutive-comments = disabled +ktlint_standard_if-else-wrapping = disabled \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/data/viewmodels/scanReceipt/ScanReceiptViewModel.kt b/app/src/main/java/com/example/bondoman/data/viewmodels/scanReceipt/ScanReceiptViewModel.kt index c6d6fd2bac08e0238656fc4c0822fc5e815f6b35..62521d6193982288150494c51e65346179c89f80 100644 --- a/app/src/main/java/com/example/bondoman/data/viewmodels/scanReceipt/ScanReceiptViewModel.kt +++ b/app/src/main/java/com/example/bondoman/data/viewmodels/scanReceipt/ScanReceiptViewModel.kt @@ -17,27 +17,33 @@ class ScanReceiptViewModel : ViewModel() { private val _scanReceiptResponse = MutableLiveData<ScanReceiptResponseItemsWrapper>() val scanReceiptResponse = _scanReceiptResponse - fun scanReceipt(scanReceiptImageFile: File, bearerToken: String) { + fun scanReceipt( + scanReceiptImageFile: File, + bearerToken: String, + ) { viewModelScope.launch { - val repository = ScanReceiptRepository( - RetrofitClient.getInstanceWithAuth(bearerToken) - .create(ScanReceiptService::class.java) - ) - val formData = MultipartBody.Part.createFormData( - "file", - scanReceiptImageFile.name, - RequestBody.create(MediaType.parse("image/*"), scanReceiptImageFile) - ) + val repository = + ScanReceiptRepository( + RetrofitClient.getInstanceWithAuth(bearerToken) + .create(ScanReceiptService::class.java), + ) + val formData = + MultipartBody.Part.createFormData( + "file", + scanReceiptImageFile.name, + RequestBody.create(MediaType.parse("image/*"), scanReceiptImageFile), + ) val scanReceiptResponse = repository.scanReceipt(formData) if (scanReceiptResponse.isSuccessful) { _scanReceiptResponse.value = scanReceiptResponse.body() } else { - val error = when (scanReceiptResponse.code()) { - 401 -> "Upload receipt failed. Unauthorized" - 413 -> "Upload receipt failed. File too large" - else -> "Upload receipt failed. Unknown Error" - } + val error = + when (scanReceiptResponse.code()) { + 401 -> "Upload receipt failed. Unauthorized" + 413 -> "Upload receipt failed. File too large" + else -> "Upload receipt failed. Unknown Error" + } _scanReceiptResponse.value = ScanReceiptResponseItemsWrapper(null, error) } diff --git a/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModel.kt b/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModel.kt index 2341bf8e541e46ad42acef391a083c40f5bc15d4..2f61bb3a0f89aea1e4afebf6f6fea085cb3fc91e 100644 --- a/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModel.kt +++ b/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModel.kt @@ -56,7 +56,7 @@ class TransactionViewModel( Transaction( title = title, owner = currentUserNim, - amount = amount, + amount = Long.MAX_VALUE, category = category, location = location, ) diff --git a/app/src/main/java/com/example/bondoman/networks/requests/ScanReceiptRequest.kt b/app/src/main/java/com/example/bondoman/networks/requests/ScanReceiptRequest.kt index ec0d70f72bd39777ca064f1dfd6bb4adb0dcbb38..a4a4f8f2be8f02cba1862b36a236aafc32f34bd1 100644 --- a/app/src/main/java/com/example/bondoman/networks/requests/ScanReceiptRequest.kt +++ b/app/src/main/java/com/example/bondoman/networks/requests/ScanReceiptRequest.kt @@ -3,8 +3,7 @@ package com.example.bondoman.networks.requests import okhttp3.MultipartBody import retrofit2.http.Part - data class ScanReceiptRequest( @Part - val formData: MultipartBody.Part? = null + val formData: MultipartBody.Part? = null, ) diff --git a/app/src/main/java/com/example/bondoman/networks/responses/ScanReceiptResponse.kt b/app/src/main/java/com/example/bondoman/networks/responses/ScanReceiptResponse.kt index af04a3a949576eb5d56164ae41ec6119c217fe52..ff960974c7b1aaa121bc2e4264c0bf2dac771068 100644 --- a/app/src/main/java/com/example/bondoman/networks/responses/ScanReceiptResponse.kt +++ b/app/src/main/java/com/example/bondoman/networks/responses/ScanReceiptResponse.kt @@ -7,23 +7,21 @@ import com.squareup.moshi.JsonClass data class ScanReceiptResponseItemsWrapper( @Json(name = "items") val items: ScanReceiptResponseItems?, - val error: String? + val error: String?, ) @JsonClass(generateAdapter = true) data class ScanReceiptResponseItems( @Json(name = "items") - val items: List<ScanReceiptResponseItem> + val items: List<ScanReceiptResponseItem>, ) @JsonClass(generateAdapter = true) data class ScanReceiptResponseItem( @Json(name = "name") val name: String, - @Json(name = "qty") val quantity: Int, - @Json(name = "price") - val price: Double + val price: Double, ) diff --git a/app/src/main/java/com/example/bondoman/networks/services/ScanReceiptService.kt b/app/src/main/java/com/example/bondoman/networks/services/ScanReceiptService.kt index 656d3df2f8fb645673fbe6357818a7e5d29e8b60..6369406a6d9365beaf670de3f368c8ceaff4446f 100644 --- a/app/src/main/java/com/example/bondoman/networks/services/ScanReceiptService.kt +++ b/app/src/main/java/com/example/bondoman/networks/services/ScanReceiptService.kt @@ -11,6 +11,6 @@ interface ScanReceiptService { @Multipart @POST("/api/bill/upload") suspend fun scanReceipt( - @Part request: MultipartBody.Part + @Part request: MultipartBody.Part, ): Response<ScanReceiptResponseItemsWrapper> } diff --git a/app/src/main/java/com/example/bondoman/views/activities/MainActivity.kt b/app/src/main/java/com/example/bondoman/views/activities/MainActivity.kt index 8c0b93a42aaf394a513d15d99c9e544b3a5daded..6ca9f085178da159187feaf6589740833c4e818d 100644 --- a/app/src/main/java/com/example/bondoman/views/activities/MainActivity.kt +++ b/app/src/main/java/com/example/bondoman/views/activities/MainActivity.kt @@ -55,15 +55,17 @@ class MainActivity : AppCompatActivity(), ParentActivityService { private lateinit var transactionViewModel: TransactionViewModel private lateinit var navController: NavController + // destinations private val destinationListeners = mutableListOf<(Int) -> Unit>() - private var currentFragmentId: Int? = null + private var isConnectionlost: Boolean = false + private var isOnConnectionLostFragment: Boolean = false + private var isOnBack: Boolean = false private val expiryReceiver = ExpiryBroadcastReceiver() - private var isConnectionlost: Boolean = false - var randomizeNextTransaction: Boolean = false + private val randomizeReceiver = object : BroadcastReceiver() { override fun onReceive( @@ -109,13 +111,17 @@ class MainActivity : AppCompatActivity(), ParentActivityService { override fun showBackButton(onClick: () -> Unit) { backButton.visibility = View.VISIBLE - backButton.setOnClickListener { onClick() } + backButton.setOnClickListener { + isOnBack = true + onClick() + } } override fun hideBackButton() { backButton.visibility = View.GONE backButton.setOnClickListener { + isOnBack = true navController.popBackStack() } } @@ -196,58 +202,91 @@ class MainActivity : AppCompatActivity(), ParentActivityService { val headerText = findViewById<TextView>(R.id.header_text) navbar.setupWithNavController(navController) + navController.addOnDestinationChangedListener { _, destination, _ -> headerText.text = destination.label ?: headerText.text + if (destination.id in MAIN_FRAGMENT_IDS) { + hideBackButton() + } + destinationListeners.forEach { it.invoke(destination.id) } - // handle connection lost fragment - if (isConnectionlost) { - if (destination.id != R.id.ConnectionLostFragment) { - navController.navigate(R.id.action_global_to_ConnectionLostFragment) - } else { - currentFragmentId = R.id.ConnectionLostFragment + if (destination.id != R.id.ConnectionLostFragment) { + isOnConnectionLostFragment = false + } + + // handle back navigation + // IMPORTANT: must be checked before isConnectionLost condition + if (isOnBack) { + if (isOnConnectionLostFragment) { + isOnConnectionLostFragment = false + navController.popBackStack() + return@addOnDestinationChangedListener } - return@addOnDestinationChangedListener + isOnBack = false } - // ignore destination - if (currentFragmentId == destination.id) { + // remove old ConnectionLostFragment + if (isOnConnectionLostFragment && destination.id == R.id.ConnectionLostFragment) { + navController.popBackStack() return@addOnDestinationChangedListener } // handle twibbon preview navigation - else if ( - (currentFragmentId != R.id.twibbonFragment && currentFragmentId != R.id.ConnectionLostFragment) - && destination.id == R.id.twibbonPreviewFragment + if ( + (currentFragmentId != R.id.twibbonFragment) && + destination.id == R.id.twibbonPreviewFragment ) { // Set the action to go back to TwibbonFragment navController.popBackStack(R.id.twibbonFragment, false) currentFragmentId = R.id.twibbonFragment + return@addOnDestinationChangedListener } // handle navigation to add and update page - else if ( - (currentFragmentId != R.id.transactionListFragment && currentFragmentId != R.id.ConnectionLostFragment) - && (destination.id == R.id.transactionAddFragment || destination.id == R.id.transactionUpdateFragment) + if ( + (currentFragmentId != R.id.transactionListFragment) && + (destination.id == R.id.transactionAddFragment || destination.id == R.id.transactionUpdateFragment) ) { // Set the action to go back to TransactionListFragment navController.popBackStack(R.id.transactionListFragment, false) - currentFragmentId = R.id.transactionAddFragment + return@addOnDestinationChangedListener } // handle scan fragment navigation - else if (currentFragmentId != R.id.scanReceiptFragment && destination.id == R.id.scanReceiptResultFragment) { + if (currentFragmentId != R.id.scanReceiptFragment && destination.id == R.id.scanReceiptResultFragment) { navController.popBackStack(R.id.scanReceiptFragment, false) - currentFragmentId = R.id.scanReceiptFragment + return@addOnDestinationChangedListener + } + + // handle connection lost fragment + if (isConnectionlost) { + if (destination.id != R.id.ConnectionLostFragment) { + navController.navigate(R.id.action_global_to_ConnectionLostFragment) + } else { + isOnConnectionLostFragment = true + } + return@addOnDestinationChangedListener + } + + // ignore existing connection lost page on stack + if (destination.id == R.id.ConnectionLostFragment) { + navController.popBackStack() + } + + // handle self destination + else if (currentFragmentId == destination.id) { + if (!isOnConnectionLostFragment) { + return@addOnDestinationChangedListener + } else { + currentFragmentId = destination.id + } } // handle default behaviour else { - if (destination.id in MAIN_FRAGMENT_IDS) { - hideBackButton() - } currentFragmentId = destination.id } } @@ -277,36 +316,37 @@ class MainActivity : AppCompatActivity(), ParentActivityService { private fun monitorConnection() { val connectivityManager = getSystemService(ConnectivityManager::class.java) as ConnectivityManager - connectivityManager.registerDefaultNetworkCallback(object : - ConnectivityManager.NetworkCallback() { - override fun onAvailable(network: Network) { - Handler(Looper.getMainLooper()).post { - if (isConnectionlost) { - isConnectionlost = false - navController.popBackStack() + connectivityManager.registerDefaultNetworkCallback( + object : + ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + Handler(Looper.getMainLooper()).post { + if (isConnectionlost) { + isConnectionlost = false + navController.popBackStack() + } } } - } - override fun onLost(network: Network) { - Handler(Looper.getMainLooper()).post { - isConnectionlost = true - navController.navigate(R.id.action_global_to_ConnectionLostFragment) + override fun onLost(network: Network) { + Handler(Looper.getMainLooper()).post { + isConnectionlost = true + navController.navigate(R.id.action_global_to_ConnectionLostFragment) + } } - } - override fun onCapabilitiesChanged( - network: Network, - networkCapabilities: NetworkCapabilities - ) { - } + override fun onCapabilitiesChanged( + network: Network, + networkCapabilities: NetworkCapabilities, + ) { + } - override fun onLinkPropertiesChanged( - network: Network, - linkProperties: LinkProperties - ) { - } - } + override fun onLinkPropertiesChanged( + network: Network, + linkProperties: LinkProperties, + ) { + } + }, ) } diff --git a/app/src/main/java/com/example/bondoman/views/components/TextInputComponent.kt b/app/src/main/java/com/example/bondoman/views/components/TextInputComponent.kt index b48dee18ac8a2e3c3fb98dd3c2e4d29db0bf81c5..55a3292e29bedbc5a51ad02cd5609c5ec9f22ef3 100644 --- a/app/src/main/java/com/example/bondoman/views/components/TextInputComponent.kt +++ b/app/src/main/java/com/example/bondoman/views/components/TextInputComponent.kt @@ -15,200 +15,199 @@ import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textview.MaterialTextView class TextInputComponent -@JvmOverloads -constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, -) : LinearLayout(context, attrs, defStyleAttr) { - private val labelTextView: MaterialTextView - private val inputEditText: TextInputEditText - private var beforeTextChangedListener: ((text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)? = - null - private var onTextChangedListener: ((text: CharSequence?, start: Int, before: Int, count: Int) -> Unit)? = - null - private var afterTextChangedListener: ((text: Editable?) -> Unit)? = null - - companion object { - enum class Keyboard(val value: Int) { - TEXT(0), - NUMBER(1), + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + ) : LinearLayout(context, attrs, defStyleAttr) { + private val labelTextView: MaterialTextView + private val inputEditText: TextInputEditText + private var beforeTextChangedListener: ((text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)? = + null + private var onTextChangedListener: ((text: CharSequence?, start: Int, before: Int, count: Int) -> Unit)? = + null + private var afterTextChangedListener: ((text: Editable?) -> Unit)? = null + + companion object { + enum class Keyboard(val value: Int) { + TEXT(0), + NUMBER(1), + } } - } - - init { - LayoutInflater.from(context).inflate(R.layout.component_text_input, this, true) - - labelTextView = findViewById(R.id.text_input_component_label) - inputEditText = findViewById(R.id.text_input_component_field) - - // Load attributes - val attributes = context.obtainStyledAttributes(attrs, R.styleable.TextInputComponent) - val labelText = attributes.getString(R.styleable.TextInputComponent_textInputLabel) ?: "" - val hint = attributes.getString(R.styleable.TextInputComponent_textInputHint) ?: "" - val inputType = - attrs?.getAttributeIntValue( - "http://schemas.android.com/apk/res/android", - "inputType", - InputType.TYPE_CLASS_TEXT, - ) ?: InputType.TYPE_CLASS_TEXT - - val digits = - attrs?.getAttributeValue( - "http://schemas.android.com/apk/res/android", - "digits", - ) - - val maxLength = - attrs?.getAttributeValue( - "http://schemas.android.com/apk/res/android", - "maxLength", - ) - val keyboardOrdinal = - attributes.getInt( - R.styleable.TextInputComponent_textInputKeyboard, - TextInputComponent.Companion.Keyboard.TEXT.ordinal, - ) - - - attributes.recycle() - - // Set attributes - labelTextView.text = labelText - inputEditText.hint = hint - inputEditText.inputType = inputType - - if (digits != null) { - when (keyboardOrdinal) { - Keyboard.TEXT.ordinal -> { - val textWatcher = - object : TextWatcher { - override fun beforeTextChanged( - s: CharSequence?, - start: Int, - count: Int, - after: Int, - ) { - // This method is called before the text is changed - } + init { + LayoutInflater.from(context).inflate(R.layout.component_text_input, this, true) + + labelTextView = findViewById(R.id.text_input_component_label) + inputEditText = findViewById(R.id.text_input_component_field) + + // Load attributes + val attributes = context.obtainStyledAttributes(attrs, R.styleable.TextInputComponent) + val labelText = attributes.getString(R.styleable.TextInputComponent_textInputLabel) ?: "" + val hint = attributes.getString(R.styleable.TextInputComponent_textInputHint) ?: "" + val inputType = + attrs?.getAttributeIntValue( + "http://schemas.android.com/apk/res/android", + "inputType", + InputType.TYPE_CLASS_TEXT, + ) ?: InputType.TYPE_CLASS_TEXT + + val digits = + attrs?.getAttributeValue( + "http://schemas.android.com/apk/res/android", + "digits", + ) + + val maxLength = + attrs?.getAttributeValue( + "http://schemas.android.com/apk/res/android", + "maxLength", + ) + + val keyboardOrdinal = + attributes.getInt( + R.styleable.TextInputComponent_textInputKeyboard, + TextInputComponent.Companion.Keyboard.TEXT.ordinal, + ) + + attributes.recycle() + + // Set attributes + labelTextView.text = labelText + inputEditText.hint = hint + inputEditText.inputType = inputType + + if (digits != null) { + when (keyboardOrdinal) { + Keyboard.TEXT.ordinal -> { + val textWatcher = + object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int, + ) { + // This method is called before the text is changed + } - override fun onTextChanged( - s: CharSequence?, - start: Int, - before: Int, - count: Int, - ) { - if (s != null) { - val filteredText = - s.filter { - it in digits + override fun onTextChanged( + s: CharSequence?, + start: Int, + before: Int, + count: Int, + ) { + if (s != null) { + val filteredText = + s.filter { + it in digits + } + + if (filteredText.length < s.length) { + inputEditText.setText(filteredText) + inputEditText.setSelection( + filteredText.length, + ) } - - if (filteredText.length < s.length) { - inputEditText.setText(filteredText) - inputEditText.setSelection( - filteredText.length, - ) } } - } - override fun afterTextChanged(s: Editable?) { - // This method is called after the text has changed + override fun afterTextChanged(s: Editable?) { + // This method is called after the text has changed + } } - } - // Attach the TextWatcher to the EditText - inputEditText.addTextChangedListener(textWatcher) - } + // Attach the TextWatcher to the EditText + inputEditText.addTextChangedListener(textWatcher) + } - Keyboard.NUMBER.ordinal -> { - inputEditText.keyListener = DigitsKeyListener.getInstance(digits) + Keyboard.NUMBER.ordinal -> { + inputEditText.keyListener = DigitsKeyListener.getInstance(digits) + } } } - } - - if (maxLength != null) { - val filterArray = arrayOfNulls<InputFilter>(1) - filterArray[0] = LengthFilter(maxLength.toInt()) - inputEditText.setFilters(filterArray) - } - if (maxLength != null) { - val filterArray = arrayOfNulls<InputFilter>(1) - filterArray[0] = LengthFilter(maxLength.toInt()) - inputEditText.setFilters(filterArray) - } - - // Set up text watcher - inputEditText.addTextChangedListener( - object : TextWatcher { - override fun beforeTextChanged( - s: CharSequence?, - start: Int, - count: Int, - after: Int, - ) { - beforeTextChangedListener?.invoke(s, start, count, after) - } + if (maxLength != null) { + val filterArray = arrayOfNulls<InputFilter>(1) + filterArray[0] = LengthFilter(maxLength.toInt()) + inputEditText.setFilters(filterArray) + } - override fun onTextChanged( - s: CharSequence?, - start: Int, - before: Int, - count: Int, - ) { - onTextChangedListener?.invoke(s, start, before, count) - } + if (maxLength != null) { + val filterArray = arrayOfNulls<InputFilter>(1) + filterArray[0] = LengthFilter(maxLength.toInt()) + inputEditText.setFilters(filterArray) + } - override fun afterTextChanged(s: Editable?) { - afterTextChangedListener?.invoke(s) - } - }, - ) - } + // Set up text watcher + inputEditText.addTextChangedListener( + object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int, + ) { + beforeTextChangedListener?.invoke(s, start, count, after) + } + + override fun onTextChanged( + s: CharSequence?, + start: Int, + before: Int, + count: Int, + ) { + onTextChangedListener?.invoke(s, start, before, count) + } + + override fun afterTextChanged(s: Editable?) { + afterTextChangedListener?.invoke(s) + } + }, + ) + } - fun setInputType(type: Int) { - inputEditText.inputType = type - } + fun setInputType(type: Int) { + inputEditText.inputType = type + } - fun setText(text: String) { - inputEditText.setText(text) - } + fun setText(text: String) { + inputEditText.setText(text) + } - fun getText(): String { - return inputEditText.text.toString() - } + fun getText(): String { + return inputEditText.text.toString() + } - fun disable() { - inputEditText.isCursorVisible = false - inputEditText.isFocusableInTouchMode = false - } + fun disable() { + inputEditText.isCursorVisible = false + inputEditText.isFocusableInTouchMode = false + } - fun enable() { - inputEditText.isCursorVisible = true - inputEditText.isFocusableInTouchMode = true - } + fun enable() { + inputEditText.isCursorVisible = true + inputEditText.isFocusableInTouchMode = true + } - // Setter methods for text change listeners - fun setBeforeTextChangedListener(listener: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit) { - beforeTextChangedListener = listener - } + // Setter methods for text change listeners + fun setBeforeTextChangedListener(listener: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit) { + beforeTextChangedListener = listener + } - fun setOnTextChangedListener(listener: (text: CharSequence?, start: Int, before: Int, count: Int) -> Unit) { - onTextChangedListener = listener - } + fun setOnTextChangedListener(listener: (text: CharSequence?, start: Int, before: Int, count: Int) -> Unit) { + onTextChangedListener = listener + } - fun setAfterTextChangedListener(listener: (text: Editable?) -> Unit) { - afterTextChangedListener = listener - } + fun setAfterTextChangedListener(listener: (text: Editable?) -> Unit) { + afterTextChangedListener = listener + } - fun addTextWactcher(textWatcher: TextWatcher) { - inputEditText.addTextChangedListener(textWatcher) - } + fun addTextWactcher(textWatcher: TextWatcher) { + inputEditText.addTextChangedListener(textWatcher) + } - fun removeTextWatcher(textWatcher: TextWatcher) { - inputEditText.removeTextChangedListener(textWatcher) + fun removeTextWatcher(textWatcher: TextWatcher) { + inputEditText.removeTextChangedListener(textWatcher) + } } -} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/ConnectionLostFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/ConnectionLostFragment.kt index 88faba1482d650b4c7cc0e7dc04f49805979ac14..9465ca97f56314b90290b442f50f21bf72189348 100644 --- a/app/src/main/java/com/example/bondoman/views/fragments/ConnectionLostFragment.kt +++ b/app/src/main/java/com/example/bondoman/views/fragments/ConnectionLostFragment.kt @@ -8,7 +8,6 @@ import androidx.fragment.app.Fragment import com.example.bondoman.databinding.FragmentConnectionLostBinding class ConnectionLostFragment : Fragment() { - companion object { @JvmStatic fun newInstance() = @@ -27,5 +26,4 @@ class ConnectionLostFragment : Fragment() { binding = FragmentConnectionLostBinding.inflate(inflater) return binding.root } - } diff --git a/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptFragment.kt index 91e44ffaeb9e020dc3d0e3ed4b59f3db669401cf..00cb1d344dbf79d87b1ba1808541ac820e0486d3 100644 --- a/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptFragment.kt +++ b/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptFragment.kt @@ -74,10 +74,9 @@ class ScanReceiptFragment : Fragment() { val scanReceiptImageBitmap = uriToBitmap(uri) val action = ScanReceiptFragmentDirections.actionScanReceiptFragmentToScanReceiptResultFragment( - ParcelableBitmap(scanReceiptImageBitmap) + ParcelableBitmap(scanReceiptImageBitmap), ) findNavController().navigate(action) - } else { Log.d(TAG, "No image selected") } @@ -96,11 +95,10 @@ class ScanReceiptFragment : Fragment() { // Set up the listeners for capture button viewBinding.receiptCaptureButton.setOnClickListener { - takePhoto(); + takePhoto() } } - private fun startCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext()) @@ -146,11 +144,12 @@ class ScanReceiptFragment : Fragment() { val action = ScanReceiptFragmentDirections.actionScanReceiptFragmentToScanReceiptResultFragment( - ParcelableBitmap(scanReceiptImageBitmap) + ParcelableBitmap(scanReceiptImageBitmap), ) findNavController().navigate(action) } - }) + }, + ) } private fun uriToBitmap(uri: Uri): Bitmap? { diff --git a/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptResultFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptResultFragment.kt index 52bf45d4bfe494daaf97f0166f162acecf725d95..f62f7be1d7a5133a36edeea0752c933dbc37741a 100644 --- a/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptResultFragment.kt +++ b/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptResultFragment.kt @@ -40,7 +40,6 @@ import java.io.IOException import java.util.Locale import java.util.function.Consumer - class ScanReceiptResultFragment : Fragment() { private lateinit var viewBinding: FragmentScanReceiptResultBinding private lateinit var locationManager: LocationManager @@ -79,7 +78,7 @@ class ScanReceiptResultFragment : Fragment() { Toast.makeText( this.requireContext(), "Location permission denied", - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } } @@ -95,8 +94,9 @@ class ScanReceiptResultFragment : Fragment() { } override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, ): View { viewBinding = FragmentScanReceiptResultBinding.inflate(inflater, container, false) @@ -127,7 +127,10 @@ class ScanReceiptResultFragment : Fragment() { return viewBinding.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) // POST the image to the server @@ -139,59 +142,60 @@ class ScanReceiptResultFragment : Fragment() { scanReceiptViewModel.scanReceipt(scanReceiptImageFile, token) - val observer = Observer<ScanReceiptResponseItemsWrapper> { response -> - if (response.error != null) { - Toast.makeText(this.requireContext(), response.error, Toast.LENGTH_SHORT).show() - Navigation.findNavController(view) - .navigate(R.id.action_scanReceiptResultFragment_to_scanReceiptFragment) - } else { - // Hide loading indicator - viewBinding.scanReceiptLoading.visibility = View.GONE - - val viewWrapper = viewBinding.scanReceiptViewWrapper - - // Storing total amount of the receipt - var totalAmount = 0L - - if (response.items != null) { - val itemsWrapper = viewBinding.scanReceiptItems - for (item in response.items.items) { - val amount = (item.price * item.quantity * 100).toLong() - totalAmount += amount - - val scanReceiptItemViewBinding = - ComponentScanReceiptItemCardBinding.inflate( - LayoutInflater.from(viewBinding.root.context), - viewBinding.root, - false - ) - val scanReceiptItemView = scanReceiptItemViewBinding.root - - scanReceiptItemViewBinding.scanReceiptItemName.text = item.name - - val itemPriceString = - String.format("%d", amount) - val itemPriceFormatted = formatAmountString(itemPriceString) - - scanReceiptItemViewBinding.scanReceiptItemPrice.text = - String.format("Rp$itemPriceFormatted") - - itemsWrapper.addView(scanReceiptItemView) + val observer = + Observer<ScanReceiptResponseItemsWrapper> { response -> + if (response.error != null) { + Toast.makeText(this.requireContext(), response.error, Toast.LENGTH_SHORT).show() + Navigation.findNavController(view) + .navigate(R.id.action_scanReceiptResultFragment_to_scanReceiptFragment) + } else { + // Hide loading indicator + viewBinding.scanReceiptLoading.visibility = View.GONE + + val viewWrapper = viewBinding.scanReceiptViewWrapper + + // Storing total amount of the receipt + var totalAmount = 0L + + if (response.items != null) { + val itemsWrapper = viewBinding.scanReceiptItems + for (item in response.items.items) { + val amount = (item.price * item.quantity * 100).toLong() + totalAmount += amount + + val scanReceiptItemViewBinding = + ComponentScanReceiptItemCardBinding.inflate( + LayoutInflater.from(viewBinding.root.context), + viewBinding.root, + false, + ) + val scanReceiptItemView = scanReceiptItemViewBinding.root + + scanReceiptItemViewBinding.scanReceiptItemName.text = item.name + + val itemPriceString = + String.format("%d", amount) + val itemPriceFormatted = formatAmountString(itemPriceString) + + scanReceiptItemViewBinding.scanReceiptItemPrice.text = + String.format("Rp$itemPriceFormatted") + + itemsWrapper.addView(scanReceiptItemView) + } } - } - // Show all child except ProgressBar - for (i in 0 until viewWrapper.childCount) { - val childView = viewWrapper.getChildAt(i) - if (childView !is ProgressBar) { - childView.visibility = View.VISIBLE + // Show all child except ProgressBar + for (i in 0 until viewWrapper.childCount) { + val childView = viewWrapper.getChildAt(i) + if (childView !is ProgressBar) { + childView.visibility = View.VISIBLE + } } - } - // Set amount input field value - viewBinding.scanReceiptFormAmount.setText(totalAmount.toString()) + // Set amount input field value + viewBinding.scanReceiptFormAmount.setText(totalAmount.toString()) + } } - } scanReceiptViewModel.scanReceiptResponse.observe(viewLifecycleOwner, observer) } @@ -199,7 +203,7 @@ class ScanReceiptResultFragment : Fragment() { // Get current location changeLocation() - //Retry scan receipt process + // Retry scan receipt process viewBinding.scanReceiptFormButtonRetake.setOnClickListener { Navigation.findNavController(view) .navigate(R.id.action_scanReceiptResultFragment_to_scanReceiptFragment) @@ -218,7 +222,7 @@ class ScanReceiptResultFragment : Fragment() { title, TransactionCategory.EXPENSE, amount, - location + location, ) { _, message -> Toast.makeText(this.requireContext(), message, Toast.LENGTH_SHORT).show() findNavController().popBackStack(R.id.scanReceiptFragment, false) @@ -229,7 +233,11 @@ class ScanReceiptResultFragment : Fragment() { } } - private fun validateInput(title: String, amount: Long, location: String): String? { + private fun validateInput( + title: String, + amount: Long, + location: String, + ): String? { if (title == "" || title.length > 50) { return "Title is required" } @@ -349,7 +357,7 @@ class ScanReceiptResultFragment : Fragment() { Toast.makeText( this.requireContext(), "No provider to get location. Enable GPS", - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() } } @@ -463,7 +471,10 @@ class ScanReceiptResultFragment : Fragment() { return formattedBuilder.reverse().toString() } - private fun bitmapToFile(bitmap: Bitmap, file: File): File { + private fun bitmapToFile( + bitmap: Bitmap, + file: File, + ): File { return try { val fileOutputStream = FileOutputStream(file) bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream) @@ -487,7 +498,6 @@ class ScanReceiptResultFragment : Fragment() { @JvmStatic fun newInstance() = ScanReceiptResultFragment().apply { - } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index bca856c0e3797e14c5f6d12b32f09cab3dd58fff..a386a0be8c379383d672012fe4e0a5abd515d4c2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,25 +1,24 @@ pluginManagement { - repositories { - google { - content { - includeGroupByRegex("com\\.android.*") - includeGroupByRegex("com\\.google.*") - includeGroupByRegex("androidx.*") - } - } - mavenCentral() - gradlePluginPortal() - } + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } } dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - google() - mavenCentral() - maven("https://jitpack.io" ) - } + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven("https://jitpack.io") + } } rootProject.name = "BondoMan" include(":app") - \ No newline at end of file