From f6e2966e90dec68bc4670ed2512b2ea37919482e Mon Sep 17 00:00:00 2001 From: Aji Kamaludin Date: Sun, 3 Apr 2022 17:41:30 +0700 Subject: [PATCH] done --- .idea/vcs.xml | 6 + .../ajikamaludin/wallet/data/Transaction.kt | 6 +- .../wallet/database/TransactionDao.kt | 4 +- .../wallet/ui/AddTransactionFragment.kt | 126 +++++++++++++++++- .../wallet/ui/TransactionListFragment.kt | 2 - .../wallet/ui/TransactionViewModel.kt | 66 +++++++++ .../res/layout/fragment_add_transaction.xml | 11 ++ app/src/main/res/values-in-rID/strings.xml | 4 +- app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/strings.xml | 8 +- app/src/main/res/values/style.xml | 3 + 11 files changed, 219 insertions(+), 19 deletions(-) create mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/id/ajikamaludin/wallet/data/Transaction.kt b/app/src/main/java/id/ajikamaludin/wallet/data/Transaction.kt index ea5618e..3663d8b 100644 --- a/app/src/main/java/id/ajikamaludin/wallet/data/Transaction.kt +++ b/app/src/main/java/id/ajikamaludin/wallet/data/Transaction.kt @@ -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) -} \ No newline at end of file +) \ No newline at end of file diff --git a/app/src/main/java/id/ajikamaludin/wallet/database/TransactionDao.kt b/app/src/main/java/id/ajikamaludin/wallet/database/TransactionDao.kt index 81991fe..5ce979a 100644 --- a/app/src/main/java/id/ajikamaludin/wallet/database/TransactionDao.kt +++ b/app/src/main/java/id/ajikamaludin/wallet/database/TransactionDao.kt @@ -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 + @Query("SELECT * FROM transactions WHERE id = :id") + fun getTransaction(id: Long): Flow + } \ No newline at end of file diff --git a/app/src/main/java/id/ajikamaludin/wallet/ui/AddTransactionFragment.kt b/app/src/main/java/id/ajikamaludin/wallet/ui/AddTransactionFragment.kt index 3f9995f..a6e46d5 100644 --- a/app/src/main/java/id/ajikamaludin/wallet/ui/AddTransactionFragment.kt +++ b/app/src/main/java/id/ajikamaludin/wallet/ui/AddTransactionFragment.kt @@ -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 + } + } \ No newline at end of file diff --git a/app/src/main/java/id/ajikamaludin/wallet/ui/TransactionListFragment.kt b/app/src/main/java/id/ajikamaludin/wallet/ui/TransactionListFragment.kt index e0b6afa..2f26dd2 100644 --- a/app/src/main/java/id/ajikamaludin/wallet/ui/TransactionListFragment.kt +++ b/app/src/main/java/id/ajikamaludin/wallet/ui/TransactionListFragment.kt @@ -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() { diff --git a/app/src/main/java/id/ajikamaludin/wallet/ui/TransactionViewModel.kt b/app/src/main/java/id/ajikamaludin/wallet/ui/TransactionViewModel.kt index 84a2f81..2165172 100644 --- a/app/src/main/java/id/ajikamaludin/wallet/ui/TransactionViewModel.kt +++ b/app/src/main/java/id/ajikamaludin/wallet/ui/TransactionViewModel.kt @@ -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> = transactionDao.getTransactions().asLiveData() val amount: LiveData = transactionDao.getTotalAmount().asLiveData() val expense: LiveData = transactionDao.getTotalExpense().asLiveData() val income: LiveData = 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 { + 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 { diff --git a/app/src/main/res/layout/fragment_add_transaction.xml b/app/src/main/res/layout/fragment_add_transaction.xml index 8cb4a10..2795ddf 100644 --- a/app/src/main/res/layout/fragment_add_transaction.xml +++ b/app/src/main/res/layout/fragment_add_transaction.xml @@ -74,6 +74,17 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/item_type_label" /> +