Aji Kamaludin 2 years ago
parent 3026220cd0
commit f6e2966e90
No known key found for this signature in database
GPG Key ID: 670E1F26AD5A8099

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -3,8 +3,6 @@ package id.ajikamaludin.wallet.data
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.text.NumberFormat
@Entity(tableName = "transactions")
data class Transaction(
@ -18,6 +16,4 @@ data class Transaction(
val type: Int,
@ColumnInfo(name = "created_at", defaultValue = "CURRENT_TIMESTAMP")
val createdAt: String,
) {
fun getFormattedAmount(): String = NumberFormat.getCurrencyInstance().format(amount)
}
)

@ -1,6 +1,5 @@
package id.ajikamaludin.wallet.database
import android.content.ClipData
import androidx.room.*
import id.ajikamaludin.wallet.ITEM_EXPENSE
import id.ajikamaludin.wallet.ITEM_INCOME
@ -28,4 +27,7 @@ interface TransactionDao {
@Query("SELECT SUM(amount) FROM transactions WHERE type = $ITEM_EXPENSE")
fun getTotalExpense(): Flow<String>
@Query("SELECT * FROM transactions WHERE id = :id")
fun getTransaction(id: Long): Flow<Transaction>
}

@ -1,18 +1,24 @@
package id.ajikamaludin.wallet.ui
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.TextView
import androidx.fragment.app.activityViewModels
import com.google.android.material.snackbar.Snackbar
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import id.ajikamaludin.wallet.ITEM_EXPENSE
import id.ajikamaludin.wallet.ITEM_INCOME
import id.ajikamaludin.wallet.R
import id.ajikamaludin.wallet.WalletApplication
import id.ajikamaludin.wallet.data.Transaction
import id.ajikamaludin.wallet.databinding.FragmentAddTransactionBinding
import id.ajikamaludin.wallet.databinding.FragmentTransactionListBinding
class AddTransactionFragment : Fragment() {
@ -24,6 +30,9 @@ class AddTransactionFragment : Fragment() {
private var _binding: FragmentAddTransactionBinding? = null
private val binding get() = _binding!!
lateinit var transaction: Transaction
private val navigationArgs: AddTransactionFragmentArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@ -35,13 +44,118 @@ class AddTransactionFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// force select list
val items = listOf(getString(R.string.income), getString(R.string.expense))
val adapter = ArrayAdapter(requireContext(), R.layout.item_type, items)
(binding.itemType as? AutoCompleteTextView)?.setAdapter(adapter)
binding.itemType.let {
it.setAdapter(adapter)
it.setText(getString(R.string.income), false)
}
binding.deleteAction.visibility = View.INVISIBLE
val id = navigationArgs.id
if(id > 0) {
viewModel.retrieveTransaction(id).observe(this.viewLifecycleOwner) { selectedTransaction ->
transaction = selectedTransaction
bind(transaction)
}
binding.deleteAction.visibility = View.VISIBLE
binding.deleteAction.setOnClickListener {
showConfirmationDialog()
}
} else {
binding.saveAction.setOnClickListener {
addNewItem()
}
}
}
private fun isEntryValid(): Boolean {
return viewModel.isEntryValid(
binding.itemAmount.text.toString(),
binding.itemDescription.text.toString(),
binding.itemType.text.toString()
)
}
private fun addNewItem() {
if (isEntryValid()) {
val modelType = getTransactionType()
viewModel.addTransaction(
binding.itemAmount.text.toString(),
binding.itemDescription.text.toString(),
modelType
)
val action = AddTransactionFragmentDirections.actionAddTransactionFragmentToTransactionListFragment()
findNavController().navigate(action)
} else {
binding.itemAmount.error = "Amount required"
}
}
private fun showConfirmationDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(android.R.string.dialog_alert_title))
.setMessage(getString(R.string.delete_question))
.setCancelable(false)
.setNegativeButton(getString(R.string.no)) { _, _ -> }
.setPositiveButton(getString(R.string.yes)) { _, _ ->
deleteItem()
}
.show()
}
private fun deleteItem() {
viewModel.deleteTransaction(transaction)
findNavController().navigateUp()
}
private fun bind(transaction: Transaction) {
binding.apply {
itemAmount.setText(transaction.amount.toString(), TextView.BufferType.SPANNABLE)
itemDescription.setText(transaction.description, TextView.BufferType.SPANNABLE)
when(transaction.type) {
ITEM_INCOME -> itemType.setText(getString(R.string.income), TextView.BufferType.SPANNABLE)
else -> itemType.setText(getString(R.string.expense), TextView.BufferType.SPANNABLE)
}
saveAction.setOnClickListener { updateItem() }
}
}
binding.saveAction.setOnClickListener {
Snackbar.make(this, binding.itemType.text, Snackbar.LENGTH_SHORT)
private fun updateItem() {
if (isEntryValid()) {
val modelType = getTransactionType()
viewModel.update(
this.navigationArgs.id,
binding.itemAmount.text.toString(),
binding.itemDescription.text.toString(),
modelType,
transaction.createdAt
)
val action = AddTransactionFragmentDirections.actionAddTransactionFragmentToTransactionListFragment()
findNavController().navigate(action)
} else {
binding.itemAmount.error = "Amount required"
}
}
private fun getTransactionType(): Int {
val modelType = when(binding.itemType.text.toString()) {
getString(R.string.income) -> ITEM_INCOME
else -> ITEM_EXPENSE
}
return modelType
}
override fun onDestroyView() {
super.onDestroyView()
val inputMethodManager = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as
InputMethodManager
inputMethodManager.hideSoftInputFromWindow(requireActivity().currentFocus?.windowToken, 0)
_binding = null
}
}

@ -8,9 +8,7 @@ import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import id.ajikamaludin.wallet.R
import id.ajikamaludin.wallet.WalletApplication
import id.ajikamaludin.wallet.database.TransactionDao
import id.ajikamaludin.wallet.databinding.FragmentTransactionListBinding
class TransactionListFragment: Fragment() {

@ -3,12 +3,78 @@ package id.ajikamaludin.wallet.ui
import androidx.lifecycle.*
import id.ajikamaludin.wallet.data.Transaction
import id.ajikamaludin.wallet.database.TransactionDao
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*
class TransactionViewModel(private val transactionDao: TransactionDao): ViewModel() {
val transactions: LiveData<List<Transaction>> = transactionDao.getTransactions().asLiveData()
val amount: LiveData<String> = transactionDao.getTotalAmount().asLiveData()
val expense: LiveData<String> = transactionDao.getTotalExpense().asLiveData()
val income: LiveData<String> = transactionDao.getTotalIncome().asLiveData()
private fun insertTransaction(transaction: Transaction) {
viewModelScope.launch {
transactionDao.insert(transaction)
}
}
private fun getNewTransactionEntry(amount: String, description: String, type: Int): Transaction {
val current = Date()
val formatter = SimpleDateFormat("dd-M-yyyy hh:mm", Locale.US)
val formatted = formatter.format(current)
return Transaction(
amount = amount.toDouble(),
description = description,
type = type,
createdAt = formatted
)
}
fun addTransaction(amount: String, description: String, type: Int) {
val newTransaction = getNewTransactionEntry(amount, description, type)
insertTransaction(newTransaction)
}
fun isEntryValid(amount: String, description: String, type: String): Boolean {
if(amount.isBlank() || type.isBlank()) {
return false
}
return true
}
fun retrieveTransaction(id: Long): LiveData<Transaction> {
return transactionDao.getTransaction(id).asLiveData()
}
private fun getUpdatedTransactionEntry(id: Long, amount: String, description: String, type: Int, createdAt: String): Transaction {
return Transaction(
id = id,
amount = amount.toDouble(),
description = description,
type = type,
createdAt = createdAt
)
}
private fun updateTransaction(transaction: Transaction){
viewModelScope.launch {
transactionDao.update(transaction)
}
}
fun update(id:Long, amount: String, description: String, type: Int, createdAt: String){
val transaction = getUpdatedTransactionEntry(id, amount, description, type, createdAt)
updateTransaction(transaction)
}
fun deleteTransaction(transaction: Transaction) {
viewModelScope.launch {
transactionDao.delete(transaction)
}
}
}
class TransactionViewModelFactory(private val transactionDao: TransactionDao) : ViewModelProvider.Factory {

@ -74,6 +74,17 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/item_type_label" />
<Button
android:id="@+id/delete_action"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:theme="@style/Button.Red"
android:text="@string/delete_action"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/save_action" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>

@ -4,8 +4,8 @@
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="create_transaction">Buat Transaksi</string>
<string name="balance">Saldo</string>
<string name="income">Pengeluaran</string>
<string name="expense">Pemasukan</string>
<string name="expense">Pengeluaran</string>
<string name="income">Pemasukan</string>
<string name="save_action">Simpan</string>
<string name="amount">Jumlah</string>
<string name="description">Deskripsi</string>

@ -16,5 +16,5 @@
<color name="blue_light">#63a4ff</color>
<color name="blue_dark">#004ba0</color>
<color name="red_700">#1976d2</color>
<color name="red_700">#d32f2f</color>
</resources>

@ -4,10 +4,14 @@
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="create_transaction">Create Transaction</string>
<string name="balance">Balance</string>
<string name="income">Expense</string>
<string name="expense">Income</string>
<string name="expense">Expense</string>
<string name="income">Income</string>
<string name="save_action">Save</string>
<string name="amount">Amount</string>
<string name="description">Description</string>
<string name="type">Type</string>
<string name="delete_action">Delete</string>
<string name="delete_question">Are you sure ?</string>
<string name="no">no</string>
<string name="yes">Yes</string>
</resources>

@ -14,4 +14,7 @@
<item name="helperTextTextAppearance">@style/TextAppearance.MaterialComponents.Subtitle1
</item>
</style>
<style name="Button.Red" parent="ThemeOverlay.AppCompat">
<item name="colorPrimary">@color/red_700</item>
</style>
</resources>
Loading…
Cancel
Save