diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bba3e321a3861f73ac47f7d6bd7001208eb8b38e..2272f789944aba680437d791883e802dea47c956 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,9 @@ <uses-feature android:name="android.hardware.camera.any" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" diff --git a/app/src/main/java/com/example/bondoman/MainApplication.kt b/app/src/main/java/com/example/bondoman/MainApplication.kt index ba1311aaa4be2bafbfe761de781e88089cf347bf..5e736d3d5485ad046251c06d10b40ccdcdfd3af1 100644 --- a/app/src/main/java/com/example/bondoman/MainApplication.kt +++ b/app/src/main/java/com/example/bondoman/MainApplication.kt @@ -7,6 +7,7 @@ import com.example.bondoman.data.databases.TransactionDatabase class MainApplication : Application() { lateinit var container: AppContainer + override fun onCreate() { super.onCreate() val database = TransactionDatabase.getInstance(applicationContext) diff --git a/app/src/main/java/com/example/bondoman/data/dataaccess/TransactionDao.kt b/app/src/main/java/com/example/bondoman/data/dataaccess/TransactionDao.kt index ff77ff3708c8122ef514ed250ea866cc08a17cbb..f795d8a85fba16282f9074831a4de965d97f9a6f 100644 --- a/app/src/main/java/com/example/bondoman/data/dataaccess/TransactionDao.kt +++ b/app/src/main/java/com/example/bondoman/data/dataaccess/TransactionDao.kt @@ -24,7 +24,7 @@ interface TransactionDao { id: Long, owner: String, title: String, - amount: Double, + amount: Long, location: String?, ) diff --git a/app/src/main/java/com/example/bondoman/data/models/Transaction.kt b/app/src/main/java/com/example/bondoman/data/models/Transaction.kt index 543459384f541c8502fdc69d77608da180590e90..9c47fd4c1e56abcc8e104783b2efdb9d983dddae 100644 --- a/app/src/main/java/com/example/bondoman/data/models/Transaction.kt +++ b/app/src/main/java/com/example/bondoman/data/models/Transaction.kt @@ -23,17 +23,17 @@ data class Transaction( val title: String, val owner: String, val category: TransactionCategory, - val amount: Double, + val amount: Long, val date: Date = Date(), val location: String? = null, ) : Serializable { init { require(title.isNotEmpty()) { "Transaction " } - require(amount in -MAX_AMOUNT..MAX_AMOUNT) { "Amount cannot exceed $MAX_AMOUNT" } + require(amount in 0..MAX_AMOUNT) { "Amount cannot exceed $MAX_AMOUNT" } require(location == null || location.isNotEmpty()) { "Location cannot be blank" } } companion object { - const val MAX_AMOUNT = Double.MAX_VALUE + const val MAX_AMOUNT = Long.MAX_VALUE } } diff --git a/app/src/main/java/com/example/bondoman/data/repositories/TransactionRepository.kt b/app/src/main/java/com/example/bondoman/data/repositories/TransactionRepository.kt index 8e7b496876682b7addfc62a3cc9d258889c87925..a840fb70da0d869a3debe4257418c4b849e3b691 100644 --- a/app/src/main/java/com/example/bondoman/data/repositories/TransactionRepository.kt +++ b/app/src/main/java/com/example/bondoman/data/repositories/TransactionRepository.kt @@ -17,7 +17,7 @@ class TransactionRepository(private val transactionDao: TransactionDao) { id: Long, owner: String, title: String, - amount: Double, + amount: Long, location: String?, ) = transactionDao.update(id, owner, title, amount, location) diff --git a/app/src/main/java/com/example/bondoman/data/utils/TransactionParser.kt b/app/src/main/java/com/example/bondoman/data/utils/TransactionParser.kt index 363a75908eb2c3b815f1e1d500d9b05e8f2e6894..ba2c947167eb83ce7855b1d7c6df540b2495f2f1 100644 --- a/app/src/main/java/com/example/bondoman/data/utils/TransactionParser.kt +++ b/app/src/main/java/com/example/bondoman/data/utils/TransactionParser.kt @@ -1,9 +1,9 @@ package com.example.bondoman.data.utils +import com.example.bondoman.data.models.TransactionCategory import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -import kotlin.math.abs class TransactionParser { companion object { @@ -14,7 +14,7 @@ class TransactionParser { private val dateFormat = SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH) - private fun formatIntegerString(integerString: String): String { + private fun formatAmountString(integerString: String): String { val reversedString = integerString.reversed() val formattedBuilder = StringBuilder() @@ -32,13 +32,12 @@ class TransactionParser { return dateFormat.format(date) } - fun getAmountString(amount: Double): String { - val formattedAmount = String.format("%.2f", abs(amount)) - val sign = if (amount < 0) "-" else "" - val parts = formattedAmount.split(".") - val integerPart = parts[0] - val decimalPart = parts.getOrElse(1) { "00" } - val integerFormatted = formatIntegerString(integerPart) - return "$sign Rp$integerFormatted,$decimalPart" + fun getAmountString( + amount: Long, + category: TransactionCategory, + ): String { + val sign = if (category == TransactionCategory.EXPENSE) "-" else "" + val amountFormatted = formatAmountString(amount.toString()) + return "$sign Rp$amountFormatted" } } 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 87c6b0c94d7ea89028c125f7da7cf6cf8c2f8f4f..64a4441a0c3ded721ccf2b2145c737c00a85dee8 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 @@ -42,7 +42,7 @@ class TransactionViewModel( fun insertTransaction( title: String, category: TransactionCategory, - amount: Double, + amount: Long, location: String?, ) { try { @@ -69,7 +69,7 @@ class TransactionViewModel( fun updateCurrentTransaction( title: String, - amount: Double, + amount: Long, location: String, ) { } diff --git a/app/src/main/java/com/example/bondoman/views/adapters/TransactionListAdapter.kt b/app/src/main/java/com/example/bondoman/views/adapters/TransactionListAdapter.kt index 0d8b7e2ecc881bb6eb4f40bddc6cb48e01731639..8446a9b14fc236707e54fad670cb1af74b0f16f0 100644 --- a/app/src/main/java/com/example/bondoman/views/adapters/TransactionListAdapter.kt +++ b/app/src/main/java/com/example/bondoman/views/adapters/TransactionListAdapter.kt @@ -72,7 +72,7 @@ class TransactionListAdapter( ) holder.dateTextView.text = transactionParser.getDateString(item.date) - holder.amountTextView.text = transactionParser.getAmountString(item.amount) + holder.amountTextView.text = transactionParser.getAmountString(item.amount, item.category) holder.titleTextView.text = item.title holder.locationTextView.text = item.location ?: "Unknown" 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 2f8e48db3a101ad8b038f7526c2aa0d39385e2fd..f5262780de4b67a9d56c36aa23101b145c255cc9 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 @@ -2,7 +2,9 @@ package com.example.bondoman.views.components import android.content.Context import android.text.Editable +import android.text.InputType import android.text.TextWatcher +import android.text.method.DigitsKeyListener import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout @@ -35,11 +37,28 @@ class TextInputComponent 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", + ) attributes.recycle() - // Set label text and hint + // Set attributes labelTextView.text = labelText inputEditText.hint = hint + inputEditText.inputType = inputType + + if (digits != null) { + inputEditText.keyListener = DigitsKeyListener.getInstance(digits) + } // Set up text watcher inputEditText.addTextChangedListener( diff --git a/app/src/main/java/com/example/bondoman/views/components/TransactionDetailComponent.kt b/app/src/main/java/com/example/bondoman/views/components/TransactionDetailComponent.kt index 309b18d37feec2ea3c11a0a1bbe56b63619d342e..71e0ee550e62bf2adb96c86e4c42960606714964 100644 --- a/app/src/main/java/com/example/bondoman/views/components/TransactionDetailComponent.kt +++ b/app/src/main/java/com/example/bondoman/views/components/TransactionDetailComponent.kt @@ -117,7 +117,7 @@ class TransactionDetailComponent fun setTransaction(transaction: Transaction) { viewBinding.transactionDetailDate.text = transactionParser.getDateString(transaction.date) viewBinding.transactionDetailAmount.text = - transactionParser.getAmountString(transaction.amount) + transactionParser.getAmountString(transaction.amount, transaction.category) viewBinding.transactionDetailTitle.text = transaction.title viewBinding.transactionDetailLocationText.text = transaction.location ?: "Unknown" diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TransactionAddFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TransactionAddFragment.kt index 31c4efb6a2cabe36c0f296a8798c3e154ddd649a..d3fd8be1f5ca1c6252a427d03014a335a4f316f0 100644 --- a/app/src/main/java/com/example/bondoman/views/fragments/TransactionAddFragment.kt +++ b/app/src/main/java/com/example/bondoman/views/fragments/TransactionAddFragment.kt @@ -4,9 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.example.bondoman.R import com.example.bondoman.data.models.TransactionCategory -import com.example.bondoman.views.components.TextInputComponent import java.util.Date class TransactionAddFragment : TransactionFormFragment() { @@ -27,7 +25,7 @@ class TransactionAddFragment : TransactionFormFragment() { category: TransactionCategory?, date: Date?, title: String, - amount: Double?, + amount: Long?, location: String?, ) { TODO("Not yet implemented") @@ -41,7 +39,7 @@ class TransactionAddFragment : TransactionFormFragment() { // Inflate the layout for this fragment val view = super.onCreateView(inflater, container, savedInstanceState)!! - view.findViewById<TextInputComponent>(R.id.transaction_form_date).visibility = View.GONE + getBinding().transactionFormDate.visibility = View.GONE return view } diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TransactionFormFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TransactionFormFragment.kt index 6055d428e689e11d570527168209b487b0c34d0b..441612e4f911608abc503509da0dc1980f7c1694 100644 --- a/app/src/main/java/com/example/bondoman/views/fragments/TransactionFormFragment.kt +++ b/app/src/main/java/com/example/bondoman/views/fragments/TransactionFormFragment.kt @@ -1,11 +1,21 @@ package com.example.bondoman.views.fragments +import android.content.Context +import android.content.pm.PackageManager +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.os.Build import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import com.example.bondoman.R import com.example.bondoman.data.models.TransactionCategory @@ -15,24 +25,95 @@ import com.example.bondoman.databinding.FragmentTransactionFormBinding import com.example.bondoman.views.utils.interfaces.ParentActivityService import com.example.bondoman.views.utils.interfaces.SecondaryFragment import java.util.Date +import java.util.function.Consumer abstract class TransactionFormFragment : Fragment(), SecondaryFragment { + companion object { + private val REQUIRED_PERMISSIONS = + mutableListOf( + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_COARSE_LOCATION, + ).toTypedArray() + } + private lateinit var parentActivityService: ParentActivityService private lateinit var binding: FragmentTransactionFormBinding + lateinit var locationManager: LocationManager private var category: TransactionCategory? = null - private var date: Date? = null private var title: String = "" - private var amount: Double? = null + private var date: Date? = null + private var amount: Long? = null private var location: String? = null + private var currentGpsLocation: Location? = null + private var currentNetworkLocation: Location? = null private val transactionParser = TransactionParser() + private val activityResultLauncher = + registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions(), + ) { permissions -> + when { + permissions.getOrDefault(android.Manifest.permission.ACCESS_FINE_LOCATION, false) -> { + // Precise location access granted. + requestLocation() + } + + permissions.getOrDefault(android.Manifest.permission.ACCESS_COARSE_LOCATION, false) -> { + // Only approximate location access granted. + requestLocation() + } + + else -> { + Toast.makeText( + requireActivity().baseContext, + "Permission request denied", + Toast.LENGTH_SHORT, + ).show() + } + } + } + + private val gpsLocationListener: LocationListener = + object : LocationListener { + override fun onLocationChanged(location: Location) { + currentGpsLocation = location + } + + override fun onStatusChanged( + provider: String, + status: Int, + extras: Bundle, + ) {} + + override fun onProviderEnabled(provider: String) {} + + override fun onProviderDisabled(provider: String) {} + } + + private val networkLocationListener: LocationListener = + object : LocationListener { + override fun onLocationChanged(location: Location) { + currentNetworkLocation = location + } + + override fun onStatusChanged( + provider: String, + status: Int, + extras: Bundle, + ) {} + + override fun onProviderEnabled(provider: String) {} + + override fun onProviderDisabled(provider: String) {} + } + protected abstract fun onTransactionSave( category: TransactionCategory?, date: Date?, title: String, - amount: Double?, + amount: Long?, location: String?, ) @@ -55,9 +136,9 @@ abstract class TransactionFormFragment : Fragment(), SecondaryFragment { binding.transactionFormDate.setText(transactionParser.getDateString(date)) } - protected fun setAmount(amount: Double) { + protected fun setAmount(amount: Long) { this.amount = amount - binding.transactionFormAmount.setText(transactionParser.getAmountString(amount)) + binding.transactionFormAmount.setText(amount.toString().split(".").first()) } protected fun setLocation(location: String?) { @@ -69,6 +150,8 @@ abstract class TransactionFormFragment : Fragment(), SecondaryFragment { super.onCreate(savedInstanceState) parentActivityService = requireActivity() as ParentActivityService + locationManager = + requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager } private fun configureDropdown() { @@ -92,26 +175,151 @@ abstract class TransactionFormFragment : Fragment(), SecondaryFragment { } } + private val gpsLocationCallback = + Consumer<Location> { location -> + if (location != null) { + currentGpsLocation = location + changeLocationData() + + Log.d("gps LOCAAAAA", currentGpsLocation.toString()) + } + } + + private val networkLocationCallback = + Consumer<Location> { location -> + if (location != null) { + currentNetworkLocation = location + changeLocationData() + + Log.d("network LOCAAAAA", currentNetworkLocation.toString()) + } + } + + private fun changeLocationData() { + assert(currentGpsLocation != null || currentNetworkLocation != null) + + var currentLocation: Location? = null + + if (currentGpsLocation != null && currentNetworkLocation != null) { + currentLocation = + if (currentGpsLocation!!.accuracy >= currentNetworkLocation!!.accuracy) { + currentGpsLocation + } else { + currentNetworkLocation + } + } else if (currentGpsLocation != null) { + currentLocation = currentGpsLocation + } else if (currentNetworkLocation != null) { + currentLocation = currentNetworkLocation + } + + location = currentLocation.toString() + binding.transactionFormLocationField.setText(currentLocation.toString()) + } + + private fun requestLocation() { + assert( + ContextCompat.checkSelfPermission( + requireContext(), + android.Manifest.permission.ACCESS_FINE_LOCATION, + ) == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission( + requireContext(), + android.Manifest.permission.ACCESS_COARSE_LOCATION, + ) == PackageManager.PERMISSION_GRANTED, + ) + + currentGpsLocation = null + currentNetworkLocation = null + + val hasGps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) + val hasNetwork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + + val currentApiVersion = Build.VERSION.SDK_INT + + Log.d("HAS GPS", hasGps.toString()) + Log.d("HAS NETWORK", hasNetwork.toString()) + + if (hasGps) { + if (currentApiVersion >= Build.VERSION_CODES.R) { + locationManager.getCurrentLocation( + LocationManager.GPS_PROVIDER, + null, + requireActivity().mainExecutor, + gpsLocationCallback, + ) + } else { + locationManager.requestSingleUpdate( + LocationManager.GPS_PROVIDER, + gpsLocationListener, + null, + ) + } + } + + if (hasNetwork) { + if (currentApiVersion >= Build.VERSION_CODES.R) { + locationManager.getCurrentLocation( + LocationManager.NETWORK_PROVIDER, + null, + requireActivity().mainExecutor, + networkLocationCallback, + ) + } else { + locationManager.requestSingleUpdate( + LocationManager.NETWORK_PROVIDER, + networkLocationListener, + null, + ) + } + } + + if (!hasGps and !hasNetwork) { + Toast.makeText( + requireActivity().baseContext, + "No provider to get location. Enable GPS", + Toast.LENGTH_SHORT, + ).show() + } + } + private fun changeLocation() { - val currentLocation = "new" - location = currentLocation - binding.transactionFormLocationField.setText(currentLocation) + if ( + ContextCompat.checkSelfPermission( + requireContext(), + android.Manifest.permission.ACCESS_FINE_LOCATION, + ) != PackageManager.PERMISSION_GRANTED + ) { + activityResultLauncher.launch(REQUIRED_PERMISSIONS) + } else { + requestLocation() + } } private fun configureInputFields() { configureDropdown() binding.transactionFormAutoComplete.onItemClickListener = - AdapterView.OnItemClickListener { parent, view, position, id -> + AdapterView.OnItemClickListener { parent, _, position, _ -> val selectedItem = parent.getItemAtPosition(position).toString() category = transactionCategoryMap[selectedItem] } - binding.transactionFormTitle.setBeforeTextChangedListener { text, _, _, _ -> + binding.transactionFormTitle.setOnTextChangedListener { text, _, _, _ -> title = text.toString() } // TODO: set text watcher for amount field + binding.transactionFormAmount.setOnTextChangedListener { text, _, _, _ -> + val textString = text.toString() + + amount = + if (textString.isNotEmpty()) { + textString.toLong() + } else { + null + } + } binding.transactionFormChangeLocationText.setOnClickListener { changeLocation() diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TransactionListFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TransactionListFragment.kt index d5c7fb02694106bf9b679675e132a712efbde1c4..773ed23df5b5c296b15ceccab1580a37c6399ddc 100644 --- a/app/src/main/java/com/example/bondoman/views/fragments/TransactionListFragment.kt +++ b/app/src/main/java/com/example/bondoman/views/fragments/TransactionListFragment.kt @@ -41,7 +41,7 @@ class TransactionListFragment : Fragment(), TransactionClickListener { title = "Transaction 2", owner = "13521148", category = TransactionCategory.EXPENSE, - amount = -20000.05, + amount = 200000000000000L, ), ) diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TransactionUpdateFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TransactionUpdateFragment.kt index 2b1eab038ced644b8c91030962486c2b08f49a70..0a4136bda88f2f85e39b408ab1a8bba4c965eed2 100644 --- a/app/src/main/java/com/example/bondoman/views/fragments/TransactionUpdateFragment.kt +++ b/app/src/main/java/com/example/bondoman/views/fragments/TransactionUpdateFragment.kt @@ -4,13 +4,10 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.AutoCompleteTextView import androidx.navigation.fragment.navArgs import com.example.bondoman.R import com.example.bondoman.data.models.TransactionCategory -import com.example.bondoman.views.components.TextInputComponent import com.example.bondoman.views.utils.interfaces.ParentActivityService -import com.google.android.material.textfield.TextInputLayout import java.util.Date class TransactionUpdateFragment : TransactionFormFragment() { @@ -35,7 +32,7 @@ class TransactionUpdateFragment : TransactionFormFragment() { category: TransactionCategory?, date: Date?, title: String, - amount: Double?, + amount: Long?, location: String?, ) { TODO("Not yet implemented") @@ -56,13 +53,11 @@ class TransactionUpdateFragment : TransactionFormFragment() { val view = super.onCreateView(inflater, container, savedInstanceState)!! // configure category dropdown - val textInputLayout = - view.findViewById<TextInputLayout>(R.id.transaction_form_category_dropdown) + val textInputLayout = getBinding().transactionFormCategoryDropdown textInputLayout.hint = getString(R.string.hint_category) textInputLayout.endIconDrawable = null - val autoCompleteTextView = - textInputLayout.findViewById<AutoCompleteTextView>(R.id.transaction_form_auto_complete) + val autoCompleteTextView = getBinding().transactionFormAutoComplete autoCompleteTextView.dropDownHeight = 0 // fill input fields @@ -74,7 +69,7 @@ class TransactionUpdateFragment : TransactionFormFragment() { setLocation(currentTransaction.location) // configure date input - val dateFieldComponent = view.findViewById<TextInputComponent>(R.id.transaction_form_date) + val dateFieldComponent = getBinding().transactionFormDate dateFieldComponent.disable() return view } diff --git a/app/src/main/res/layout/component_transaction_card.xml b/app/src/main/res/layout/component_transaction_card.xml index f9771df49bf63771dd879eae19472ab33389ae0b..f63bf37d0dd9bc610793aadabd810d7e7d97f529 100644 --- a/app/src/main/res/layout/component_transaction_card.xml +++ b/app/src/main/res/layout/component_transaction_card.xml @@ -1,108 +1,117 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.constraintlayout.widget.ConstraintLayout - xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:background="@color/bg_main" android:layout_width="match_parent" android:layout_height="120dp" - android:padding="20dp" - android:layout_marginBottom="1dp"> + android:layout_marginBottom="1dp" + android:background="@color/bg_main" + android:padding="20dp"> + <androidx.cardview.widget.CardView android:id="@+id/transaction_icon_card" android:layout_width="wrap_content" android:layout_height="wrap_content" app:cardCornerRadius="10dp" - app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintTop_toTopOf="parent" + app:cardElevation="0dp" app:layout_constraintBottom_toBottomOf="parent" - app:cardElevation="0dp"> + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent"> + <ImageView android:id="@+id/transaction_icon_image" android:layout_width="40dp" android:layout_height="40dp" android:background="@color/rose_200" android:contentDescription="@string/desc_transaction_icon" - android:src="@drawable/ic_shopping_bag_minus" - android:scaleType="fitCenter" android:padding="5dp" - app:tint="@color/zinc_700"/> + android:scaleType="fitCenter" + android:src="@drawable/ic_shopping_bag_minus" + app:tint="@color/zinc_700" /> </androidx.cardview.widget.CardView> + <TextView android:id="@+id/date_text" - android:theme="@style/TextAppearance.Transaction.Date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dummy_transaction_date" + android:theme="@style/TextAppearance.Transaction.Date" + app:layout_constraintBottom_toTopOf="@id/transaction_card_guideline2" app:layout_constraintLeft_toRightOf="@id/transaction_card_guideline1" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/transaction_card_guideline2"/> + app:layout_constraintTop_toTopOf="parent" /> + <TextView android:id="@+id/amount_text" - android:theme="@style/TextAppearance.Transaction.Amount" - android:maxWidth="200dp" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" - android:text="@string/dummy_transaction_amount" + android:layout_marginLeft="20dp" android:ellipsize="end" android:maxLines="1" + android:text="@string/dummy_transaction_amount" + android:theme="@style/TextAppearance.Transaction.Amount" + app:layout_constraintBottom_toTopOf="@id/transaction_card_guideline2" + app:layout_constraintHorizontal_bias="1" app:layout_constraintLeft_toRightOf="@id/date_text" app:layout_constraintRight_toRightOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/transaction_card_guideline2" - app:layout_constraintHorizontal_bias="1"/> + app:layout_constraintTop_toTopOf="parent" /> + <TextView android:id="@+id/title_text" - android:theme="@style/TextAppearance.Transaction.Title" android:layout_width="0dp" android:layout_height="wrap_content" - android:text="@string/dummy_transaction_title" android:ellipsize="end" android:maxLines="1" - app:layout_constraintStart_toEndOf="@id/transaction_card_guideline1" - app:layout_constraintTop_toBottomOf="@id/transaction_card_guideline2" + android:text="@string/dummy_transaction_title" + android:theme="@style/TextAppearance.Transaction.Title" app:layout_constraintBottom_toTopOf="@id/transaction_card_guideline3" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0"/> + app:layout_constraintHorizontal_bias="0" + app:layout_constraintStart_toEndOf="@id/transaction_card_guideline1" + app:layout_constraintTop_toBottomOf="@id/transaction_card_guideline2" /> + <ImageView android:id="@+id/transaction_location_icon_image" android:layout_width="18dp" android:layout_height="18dp" android:contentDescription="@string/desc_location_icon" - android:src="@drawable/ic_map_pin" android:scaleType="fitCenter" + android:src="@drawable/ic_map_pin" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/transaction_card_guideline1" app:layout_constraintTop_toBottomOf="@id/transaction_card_guideline3" - app:layout_constraintBottom_toBottomOf="parent" - app:tint="@color/zinc_400"/> + app:tint="@color/zinc_400" /> + <TextView android:id="@+id/transaction_location_text" - android:theme="@style/TextAppearance.Transaction.Location" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="5dp" - android:text="@string/dummy_transaction_location" android:ellipsize="end" android:maxLines="1" - app:layout_constraintStart_toEndOf="@id/transaction_location_icon_image" - app:layout_constraintTop_toBottomOf="@id/transaction_card_guideline3" + android:text="@string/dummy_transaction_location" + android:theme="@style/TextAppearance.Transaction.Location" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent"/> + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/transaction_location_icon_image" + app:layout_constraintTop_toBottomOf="@id/transaction_card_guideline3" /> + <androidx.constraintlayout.widget.Guideline android:id="@+id/transaction_card_guideline1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" - app:layout_constraintGuide_begin="60dp"/> + app:layout_constraintGuide_begin="60dp" /> + <androidx.constraintlayout.widget.Guideline android:id="@+id/transaction_card_guideline2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" - app:layout_constraintGuide_percent="0.33"/> + app:layout_constraintGuide_percent="0.33" /> + <androidx.constraintlayout.widget.Guideline android:id="@+id/transaction_card_guideline3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" - app:layout_constraintGuide_percent="0.66"/> -</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file + app:layout_constraintGuide_percent="0.66" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/fragment_transaction_form.xml b/app/src/main/res/layout/fragment_transaction_form.xml index 2b2fd0bafd294e2fd8de585513df0bc00e7008d3..b640e3a9b85e7cbfb2efa4ce5de92ea66c1f44b3 100644 --- a/app/src/main/res/layout/fragment_transaction_form.xml +++ b/app/src/main/res/layout/fragment_transaction_form.xml @@ -49,6 +49,7 @@ android:id="@+id/transaction_form_amount" android:layout_width="match_parent" android:layout_height="wrap_content" + android:inputType="number" app:textInputHint="@string/hint_transaction_form_amount" app:textInputLabel="Amount (Rp)" /> diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 74ce450a8e8c2a616d3a89b06cd56ed19b59b68a..9c80c19ece14f94b444c75aafd43de53074acd6b 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -15,30 +15,33 @@ <attr name="borderBottomRightRadius" format="dimension" /> </declare-styleable> <declare-styleable name="TextInputComponent"> - <attr name="textInputLabel" format="string"/> - <attr name="textInputHint" format="string"/> + <attr name="textInputLabel" format="string" /> + <attr name="textInputHint" format="string" /> + <attr name="textInputType" format="string" /> + <attr name="android:inputType" /> + <attr name="android:digits" /> </declare-styleable> <declare-styleable name="DateInputComponent"> - <attr name="dateInputLabel" format="string"/> + <attr name="dateInputLabel" format="string" /> </declare-styleable> <declare-styleable name="DialogComponent"> - <attr name="dialogText" format="string"/> - <attr name="firstButtonText" format="string"/> - <attr name="secondButtonText" format="string"/> + <attr name="dialogText" format="string" /> + <attr name="firstButtonText" format="string" /> + <attr name="secondButtonText" format="string" /> <attr name="dialogFirstButtonType" format="enum"> - <enum name="positive" value="1"/> - <enum name="neutral" value="0"/> - <enum name="negative" value="-1"/> + <enum name="positive" value="1" /> + <enum name="neutral" value="0" /> + <enum name="negative" value="-1" /> </attr> <attr name="dialogSecondButtonType" format="enum"> - <enum name="positive" value="1"/> - <enum name="neutral" value="0"/> - <enum name="negative" value="-1"/> + <enum name="positive" value="1" /> + <enum name="neutral" value="0" /> + <enum name="negative" value="-1" /> </attr> </declare-styleable> <declare-styleable name="SettingButtonComponent"> - <attr name="settingButtonIcon" format="reference"/> - <attr name="settingButtonTitle" format="string"/> - <attr name="settingButtonSubtitle" format="string"/> + <attr name="settingButtonIcon" format="reference" /> + <attr name="settingButtonTitle" format="string" /> + <attr name="settingButtonSubtitle" format="string" /> </declare-styleable> </resources>