- MVVM refactor

- Kodein DI added
 - Overhaul dirty code in views
 - Bug fixes
This commit is contained in:
2023-08-26 21:43:35 +01:00
parent cd20315b32
commit 22982f1482
10 changed files with 115 additions and 64 deletions

View File

@@ -53,6 +53,6 @@ abstract class BaseActivity<V : BaseViewModel> : AppCompatActivity(), KodeinAwar
}
fun setTitleInActionBar(title: String) {
setTitle(title)
supportActionBar?.title = title
}
}

View File

@@ -1,5 +1,6 @@
package com.appttude.h_mal.farmr.data.legacydb
import android.content.ContentResolver
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
@@ -20,8 +21,7 @@ import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry._ID
import com.appttude.h_mal.farmr.model.Shift
import com.appttude.h_mal.farmr.model.ShiftType
class LegacyDatabase(context: Context) {
private val resolver = context.contentResolver
class LegacyDatabase(private val resolver: ContentResolver) {
private val projection = arrayOf<String?>(
_ID,
@@ -44,7 +44,7 @@ class LegacyDatabase(context: Context) {
val values = ContentValues().apply {
put(COLUMN_SHIFT_TYPE, shift.type.type)
put(COLUMN_SHIFT_DESCRIPTION, shift.description)
put(COLUMN_SHIFT_DATE, shift.description)
put(COLUMN_SHIFT_DATE, shift.date)
put(COLUMN_SHIFT_TIME_IN, shift.timeIn ?: "00:00")
put(COLUMN_SHIFT_TIME_OUT, shift.timeOut ?: "00:00")
put(COLUMN_SHIFT_DURATION, shift.duration ?: 0.00f)
@@ -63,8 +63,9 @@ class LegacyDatabase(context: Context) {
projection,
null, null, null
) ?: return null
cursor.moveToFirst()
val shifts = (0..cursor.count).map { cursor.getShift() }
val shifts = generateSequence { if (cursor.moveToNext()) cursor else null }
.map { it.getShift() }
.toList()
// close cursor after query operations
cursor.close()

View File

@@ -19,7 +19,7 @@ class ShiftApplication: Application(), KodeinAware {
override val kodein = Kodein.lazy {
import(androidXModule(this@ShiftApplication))
bind() from singleton { LegacyDatabase(this@ShiftApplication) }
bind() from singleton { LegacyDatabase(contentResolver) }
bind() from singleton { PreferenceProvider(this@ShiftApplication) }
bind() from singleton { RepositoryImpl(instance(), instance()) }

View File

@@ -51,11 +51,13 @@ class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
private var mDescription: String? = null
private var mTimeIn: String? = null
private var mTimeOut: String? = null
private var mBreaks = 0
private var mUnits = 0f
private var mBreaks: Int? = null
private var mUnits: Float? = null
private var mPayRate = 0f
private var mType: ShiftType? = null
private var mDuration: Float = 0f
private var mDuration: Float? = null
private var id: Long? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -97,7 +99,7 @@ class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
calculateTotalPay()
}
mUnitEditText.doAfterTextChanged {
it.toString().toFloatOrNull()?.let { u -> mPayRate = u }
it.toString().toFloatOrNull()?.let { u -> mUnits = u }
calculateTotalPay()
}
mPayRateEditText.doAfterTextChanged {
@@ -113,6 +115,8 @@ class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
}
private fun setupViewAfterViewCreated() {
id = arguments?.getLong(ID)
val title = when (arguments?.containsKey(ID)) {
true -> {
// Since we are editing a shift lets load the shift data into the views
@@ -158,6 +162,7 @@ class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
hourlyDataView.show()
durationHolder.show()
}
R.id.piecerate -> {
mType = ShiftType.PIECE
wholeView.show()
@@ -183,13 +188,48 @@ class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
}
if (mPieceRadioButton.isChecked) {
mUnits.validateField({!it.isNaN()}) {
mUnits.validateField({ it != null && it >= 0 }) {
onFailure("Units field cannot be empty")
return
}
viewModel.insertPieceRateShift(mDescription!!, mDate!!, mUnits, mPayRate)
if (id != null) {
// update
viewModel.updateShift(
id!!,
description = mDescription,
date = mDate,
units = mUnits,
rateOfPay = mPayRate
)
} else {
// insert
viewModel.insertPieceRateShift(mDescription!!, mDate!!, mUnits!!, mPayRate)
}
} else if (mHourlyRadioButton.isChecked) {
viewModel.insertHourlyShift(mDescription!!, mDate!!, mPayRate, mTimeIn, mTimeOut, mBreaks)
if (id != null) {
// update
viewModel.updateShift(
id!!,
description = mDescription,
date = mDate,
rateOfPay = mPayRate,
timeIn = mTimeIn,
timeOut = mTimeOut,
breakMins = mBreaks
)
} else {
// insert
viewModel.insertHourlyShift(
mDescription!!,
mDate!!,
mPayRate,
mTimeIn,
mTimeOut,
mBreaks
)
}
}
}
@@ -199,11 +239,13 @@ class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
ShiftType.HOURLY -> {
// Calculate duration before total pay calculation
mDuration = viewModel.retrieveDurationText(mTimeIn, mTimeOut, mBreaks) ?: return
mDurationTextView.text = StringBuilder().append(mDuration).append(" hours").toString()
mDuration * mPayRate
mDurationTextView.text =
StringBuilder().append(mDuration).append(" hours").toString()
mDuration!! * mPayRate
}
ShiftType.PIECE -> {
mUnits * mPayRate
(mUnits ?: 0f) * mPayRate
}
}
mTotalPayTextView.text = total.formatToTwoDpString()

View File

@@ -27,7 +27,7 @@ import kotlin.system.exitProcess
class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPressedListener {
lateinit var activity: MainActivity
private lateinit var productListView: RecyclerView
private lateinit var mAdapter: ShiftRecyclerAdapter
private lateinit var mAdapter: ShiftListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -38,7 +38,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mAdapter = ShiftRecyclerAdapter(this) {
mAdapter = ShiftListAdapter(this) {
viewModel.deleteShift(it)
}
productListView = view.findViewById(R.id.list_item_view)
@@ -49,10 +49,16 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
}
}
override fun onStart() {
super.onStart()
viewModel.refreshLiveData()
}
override fun onSuccess(data: Any?) {
super.onSuccess(data)
if (data is List<*>) {
mAdapter.updateData(data as List<ShiftObject>)
mAdapter.submitList(data as List<ShiftObject>)
}
}

View File

@@ -22,10 +22,6 @@ import kotlin.system.exitProcess
class MainActivity : BaseActivity<MainViewModel>() {
private lateinit var toolbar: Toolbar
var selection: String? = null
var args: Array<String>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_view)
@@ -57,11 +53,6 @@ class MainActivity : BaseActivity<MainViewModel>() {
}
}
fun setActionBarTitle(title: String?) {
toolbar.title = title
}
// Storage Permissions
private val REQUEST_EXTERNAL_STORAGE = 1
private val PERMISSIONS_STORAGE = arrayOf(

View File

@@ -2,26 +2,36 @@ package com.appttude.h_mal.farmr.ui
import android.app.AlertDialog
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.base.BaseRecyclerAdapter
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.utils.ID
import com.appttude.h_mal.farmr.utils.generateView
import com.appttude.h_mal.farmr.utils.navigateToFragment
class ShiftRecyclerAdapter(
class ShiftListAdapter(
private val fragment: Fragment,
private val longPressCallback: (Long) -> Unit
) : BaseRecyclerAdapter<ShiftObject>(
emptyViewId = R.layout.empty_list_view,
currentViewId = R.layout.list_item_1
) {
override fun bindCurrentView(view: View, position: Int, data: ShiftObject) {
) : ListAdapter<ShiftObject, BaseRecyclerAdapter.CurrentViewHolder>(diffCallBack) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): BaseRecyclerAdapter.CurrentViewHolder {
val currentViewHolder = parent.generateView(R.layout.list_item_1)
return BaseRecyclerAdapter.CurrentViewHolder(currentViewHolder)
}
override fun onBindViewHolder(holder: BaseRecyclerAdapter.CurrentViewHolder, position: Int) {
val view = holder.itemView
val data = getItem(position)
val descriptionTextView: TextView = view.findViewById(R.id.location)
val dateTextView: TextView = view.findViewById(R.id.date)
val totalPay: TextView = view.findViewById(R.id.total_pay)
@@ -89,16 +99,15 @@ class ShiftRecyclerAdapter(
}
}
// override fun getItemId(position: Int): Long {
// return if (list.isNullOrEmpty()) {
// RecyclerView.NO_ID
// } else {
// list!![position].id
// }
//
// }
//
// override fun setHasStableIds(hasStableIds: Boolean) {
// super.setHasStableIds(true)
// }
companion object {
val diffCallBack = object : DiffUtil.ItemCallback<ShiftObject>() {
override fun areItemsTheSame(oldItem: ShiftObject, newItem: ShiftObject): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: ShiftObject, newItem: ShiftObject): Boolean {
return oldItem == newItem
}
}
}
}

View File

@@ -20,11 +20,11 @@ fun Float.formatToTwoDpString(): String {
}
fun String.dateStringIsValid(): Boolean {
return DATE_FORMAT.toPattern().matcher(this).matches()
return "([0-9]{4})-([0-9]{2})-([0-9]{2})".toPattern().matcher(this).matches()
}
fun String.timeStringIsValid(): Boolean {
return TIME_FORMAT.toPattern().matcher(this).matches()
return "^([0-1]?[0-9]|2[0-3]):[0-5][0-9]\$".toPattern().matcher(this).matches()
}
fun Calendar.getTimeString(): String {

View File

@@ -173,5 +173,7 @@ fun EditText.setDatePicker(onSelected: (String) -> Unit) {
}, mYear, mMonth, mDay
)
mDatePicker.setTitle("Select date")
setOnClickListener {
mDatePicker.show()
}
}

View File

@@ -160,7 +160,7 @@ class MainViewModel(
rateOfPay: Float
) {
// Validate inputs from the edit texts
(description.length < 3).validateField {
(description.length > 3).validateField {
onError("Description length should be longer")
return
}
@@ -196,11 +196,11 @@ class MainViewModel(
type: String? = null,
description: String? = null,
date: String? = null,
rateOfPay: String? = null,
rateOfPay: Float? = null,
timeIn: String? = null,
timeOut: String? = null,
breakMins: String? = null,
units: String? = null,
breakMins: Int? = null,
units: Float? = null,
) {
description?.let {
(it.length < 3).validateField {
@@ -243,11 +243,11 @@ class MainViewModel(
type = type?.let { ShiftType.getEnumByType(it) },
description = description,
date = date,
rateOfPay = rateOfPay?.toFloatOrNull(),
rateOfPay = rateOfPay,
timeIn = timeIn,
timeOut = timeOut,
breakMins = breakMins?.toIntOrNull(),
units = units?.toFloatOrNull()
breakMins = breakMins,
units = units
)
}
}
@@ -430,7 +430,7 @@ class MainViewModel(
return textString
}
private fun refreshLiveData() {
fun refreshLiveData() {
_shiftLiveData.postValue(repository.readShiftsFromDatabase())
}
@@ -471,7 +471,7 @@ class MainViewModel(
return mFilterStore!!
}
fun retrieveDurationText(mTimeIn: String?, mTimeOut: String?, mBreaks: Int): Float? {
fun retrieveDurationText(mTimeIn: String?, mTimeOut: String?, mBreaks: Int?): Float? {
try {
return calculateDuration(mTimeIn,mTimeOut,mBreaks)
}catch (e: IOException) {