Improve transaction/category editing/creation

This commit is contained in:
William Brawner 2021-05-31 18:04:32 -06:00
parent f4bf721b43
commit 408dda40d9
3 changed files with 114 additions and 67 deletions

View file

@ -36,12 +36,16 @@ class CategoryDetailsFragment : Fragment() {
setHasOptionsMenu(true)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.fragment_category_details, container, false)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? =
inflater.inflate(R.layout.fragment_category_details, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
activity?.title = arguments?.getString(EXTRA_CATEGORY_NAME)
viewModel.state.observe(viewLifecycleOwner, Observer { state ->
viewModel.state.observe(viewLifecycleOwner, { state ->
when (state) {
is AsyncState.Loading -> {
categoryDetails.visibility = View.GONE
@ -52,7 +56,8 @@ class CategoryDetailsFragment : Fragment() {
progressBar.visibility = View.GONE
val category = state.data.category
activity?.title = category.title
val tintColor = if (category.expense) R.color.colorTextRed else R.color.colorTextGreen
val tintColor =
if (category.expense) R.color.colorTextRed else R.color.colorTextGreen
val colorStateList = with(view.context) {
android.content.res.ColorStateList.valueOf(getColor(tintColor))
}
@ -60,8 +65,8 @@ class CategoryDetailsFragment : Fragment() {
categoryProgress.max = category.amount.toInt()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
categoryProgress.setProgress(
state.data.balance.toInt(),
true
state.data.balance.toInt(),
true
)
} else {
categoryProgress.progress = state.data.balance.toInt()
@ -86,14 +91,15 @@ class CategoryDetailsFragment : Fragment() {
}
}
childFragmentManager.beginTransaction()
.replace(R.id.transactionsFragmentContainer, transactionsFragment)
.commit()
.replace(R.id.transactionsFragmentContainer, transactionsFragment)
.commit()
}
}
is AsyncState.Error -> {
categoryDetails.visibility = View.VISIBLE
progressBar.visibility = View.GONE
Toast.makeText(view.context, "Failed to load context", Toast.LENGTH_SHORT).show()
Toast.makeText(view.context, "Failed to load context", Toast.LENGTH_SHORT)
.show()
}
is AsyncState.Exit -> {
findNavController().navigateUp()
@ -106,7 +112,7 @@ class CategoryDetailsFragment : Fragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.action_edit) {
val bundle = Bundle().apply {
putLong(EXTRA_CATEGORY_ID, arguments?.getLong(EXTRA_CATEGORY_ID) ?: -1)
putString(EXTRA_CATEGORY_ID, arguments?.getString(EXTRA_CATEGORY_ID))
}
findNavController().navigate(R.id.addEditCategoryActivity, bundle)
} else if (item.itemId == android.R.id.home) {

View file

@ -46,15 +46,24 @@ class CategoryFormActivity : AppCompatActivity() {
setTitle(state.data.titleRes)
menu?.findItem(R.id.action_delete)?.isVisible = state.data.showDeleteButton
edit_category_name.setText(category.title)
edit_category_amount.setText(String.format("%.02f", (category.amount.toBigDecimal() / 100.toBigDecimal()).toFloat()))
edit_category_amount.setText(
String.format(
"%.02f",
(category.amount.toBigDecimal() / 100.toBigDecimal()).toFloat()
)
)
expense.isChecked = category.expense
income.isChecked = !category.expense
archived.isChecked = category.archived
budgetSpinner.adapter = ArrayAdapter<Budget>(
this@CategoryFormActivity,
android.R.layout.simple_list_item_1,
state.data.budgets
budgetSpinner.adapter = ArrayAdapter(
this@CategoryFormActivity,
android.R.layout.simple_list_item_1,
state.data.budgets
)
val budget = state.data.budgets.firstOrNull { it.id == category.budgetId }
?: viewModel.budgetRepository.currentBudget.value
budgetSpinner.setSelection(state.data.budgets.indexOf(budget))
}
is AsyncState.Error -> {
// TODO: Show error message
@ -86,8 +95,8 @@ class CategoryFormActivity : AppCompatActivity() {
upIntent == null -> throw IllegalStateException("No Parent Activity Intent")
NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot -> {
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(upIntent)
.startActivities()
.addNextIntentWithParentStack(upIntent)
.startActivities()
}
else -> {
NavUtils.navigateUpTo(this, upIntent)
@ -96,14 +105,16 @@ class CategoryFormActivity : AppCompatActivity() {
}
R.id.action_save -> {
if (!validateFields()) return true
viewModel.saveCategory(Category(
viewModel.saveCategory(
Category(
id = id,
title = edit_category_name.text.toString(),
amount = edit_category_amount.text.toLong(),
budgetId = (budgetSpinner.selectedItem as Budget).id!!,
expense = expense.isChecked,
archived = archived.isChecked
))
)
)
}
R.id.action_delete -> {
viewModel.deleteCategoryById(this@CategoryFormActivity.id!!)

View file

@ -29,6 +29,7 @@ import kotlinx.coroutines.launch
import java.math.BigDecimal
import java.util.*
import kotlin.coroutines.CoroutineContext
import kotlin.math.max
class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.Main
@ -52,59 +53,82 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
val accounts = viewModel.getAccounts().toTypedArray()
setCategories()
budgetSpinner.adapter = ArrayAdapter<Budget>(
this@TransactionFormActivity,
android.R.layout.simple_list_item_1,
accounts
this@TransactionFormActivity,
android.R.layout.simple_list_item_1,
accounts
)
container_edit_transaction_type.setOnCheckedChangeListener { _, _ ->
this@TransactionFormActivity.launch {
val budget = budgetSpinner.selectedItem as Budget
setCategories(viewModel.getCategories(budget.id!!, edit_transaction_type_expense.isChecked))
setCategories(
viewModel.getCategories(
budget.id!!,
edit_transaction_type_expense.isChecked
)
)
}
}
budgetSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
this@TransactionFormActivity.launch {
val budget = budgetSpinner.selectedItem as Budget
setCategories(viewModel.getCategories(budget.id!!, edit_transaction_type_expense.isChecked))
setCategories(
viewModel.getCategories(
budget.id!!,
edit_transaction_type_expense.isChecked
)
)
}
}
}
val budgetIndex = if (transaction?.budgetId != null) {
accounts.indexOfFirst { it.id == transaction?.budgetId }
} else {
accounts.indexOfFirst { it.id == viewModel.budgetRepository.currentBudget.value?.id }
}
budgetSpinner.setSelection(max(0, budgetIndex))
loadTransaction()
transactionDate.setOnClickListener {
val currentDate = DateFormat.getDateFormat(this@TransactionFormActivity)
.parse(transactionDate.text.toString()) ?: Date()
.parse(transactionDate.text.toString()) ?: Date()
MaterialDatePicker.Builder.datePicker()
.setSelection(currentDate.time)
.setTheme(R.style.DateTimePickerDialogTheme)
.build()
.also { picker ->
picker.addOnPositiveButtonClickListener {
transactionDate.text = DateFormat.getDateFormat(this@TransactionFormActivity)
.format(Date(it))
}
.setSelection(currentDate.time)
.setTheme(R.style.DateTimePickerDialogTheme)
.build()
.also { picker ->
picker.addOnPositiveButtonClickListener {
transactionDate.text =
DateFormat.getDateFormat(this@TransactionFormActivity)
.format(Date(it))
}
.show(supportFragmentManager, null)
}
.show(supportFragmentManager, null)
}
transactionTime.setOnClickListener {
val currentDate = DateFormat.getTimeFormat(this@TransactionFormActivity)
.parse(transactionTime.text.toString()) ?: Date()
.parse(transactionTime.text.toString()) ?: Date()
TimePickerDialog(
this@TransactionFormActivity,
{ _, hourOfDay, minute ->
val newTime = Date().apply {
hours = hourOfDay
minutes = minute
}
transactionTime.text = DateFormat.getTimeFormat(this@TransactionFormActivity)
.format(newTime)
},
currentDate.hours,
currentDate.minutes,
DateFormat.is24HourFormat(this@TransactionFormActivity)
this@TransactionFormActivity,
{ _, hourOfDay, minute ->
val newTime = Date().apply {
hours = hourOfDay
minutes = minute
}
transactionTime.text =
DateFormat.getTimeFormat(this@TransactionFormActivity)
.format(newTime)
},
currentDate.hours,
currentDate.minutes,
DateFormat.is24HourFormat(this@TransactionFormActivity)
).show()
}
}
@ -145,11 +169,15 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
private fun setCategories(categories: List<Category> = emptyList()) {
val adapter = ArrayAdapter<Category>(
this@TransactionFormActivity,
android.R.layout.simple_list_item_1
this@TransactionFormActivity,
android.R.layout.simple_list_item_1
)
adapter.add(
Category(
title = getString(R.string.uncategorized),
amount = 0, budgetId = ""
)
)
adapter.add(Category(title = getString(R.string.uncategorized),
amount = 0, budgetId = ""))
adapter.addAll(categories)
edit_transaction_category.adapter = adapter
transaction?.categoryId?.let {
@ -177,21 +205,22 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
R.id.action_save -> {
val date = GregorianCalendar.getInstance().apply {
DateFormat.getDateFormat(this@TransactionFormActivity)
.parse(transactionDate.text.toString())
?.let {
time = it
}
.parse(transactionDate.text.toString())
?.let {
time = it
}
DateFormat.getTimeFormat(this@TransactionFormActivity)
.parse(transactionTime.text.toString())
?.let { GregorianCalendar.getInstance().apply { time = it } }
?.let {
set(Calendar.HOUR_OF_DAY, it.get(Calendar.HOUR_OF_DAY))
set(Calendar.MINUTE, it.get(Calendar.MINUTE))
}
.parse(transactionTime.text.toString())
?.let { GregorianCalendar.getInstance().apply { time = it } }
?.let {
set(Calendar.HOUR_OF_DAY, it.get(Calendar.HOUR_OF_DAY))
set(Calendar.MINUTE, it.get(Calendar.MINUTE))
}
}
val categoryId = (edit_transaction_category.selectedItem as? Category)?.id
launch {
viewModel.saveTransaction(Transaction(
viewModel.saveTransaction(
Transaction(
id = id,
budgetId = (budgetSpinner.selectedItem as Budget).id!!,
title = edit_transaction_title.text.toString(),
@ -201,7 +230,8 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
expense = edit_transaction_type_expense.isChecked,
categoryId = categoryId,
createdBy = viewModel.currentUserId!!
))
)
)
onNavigateUp()
}
}
@ -217,14 +247,14 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
override fun onNavigateUp(): Boolean {
val upIntent: Intent = NavUtils.getParentActivityIntent(this)
?: throw IllegalStateException("No Parent Activity Intent")
?: throw IllegalStateException("No Parent Activity Intent")
upIntent.putExtra(MainActivity.EXTRA_OPEN_FRAGMENT, TransactionListFragment.TAG_FRAGMENT)
when {
NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot -> {
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(upIntent)
.startActivities()
.addNextIntentWithParentStack(upIntent)
.startActivities()
}
else -> {
finish()