Merge branch 'master' into rounding_bug_fix

This commit is contained in:
2023-09-10 23:19:47 +01:00
18 changed files with 322 additions and 159 deletions

1
.gitignore vendored
View File

@@ -89,6 +89,7 @@ gen-external-apklibs
.idea/assetWizardSettings.xml .idea/assetWizardSettings.xml
.idea/gradle.xml .idea/gradle.xml
.idea/jarRepositories.xml .idea/jarRepositories.xml
.idea/navEditor.xml
# Gem/fastlane # Gem/fastlane
Gemfile.lock Gemfile.lock

View File

@@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'org.jetbrains.kotlin.android' apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'androidx.navigation.safeargs'
def relStorePassword = System.getenv("RELEASE_STORE_PASSWORD") def relStorePassword = System.getenv("RELEASE_STORE_PASSWORD")
def relKeyPassword = System.getenv("RELEASE_KEY_PASSWORD") def relKeyPassword = System.getenv("RELEASE_KEY_PASSWORD")
@@ -49,6 +50,9 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-livedata-core:$LIFECYCLE_VERSION" implementation "androidx.lifecycle:lifecycle-livedata-core:$LIFECYCLE_VERSION"
implementation "androidx.lifecycle:lifecycle-viewmodel:$LIFECYCLE_VERSION" implementation "androidx.lifecycle:lifecycle-viewmodel:$LIFECYCLE_VERSION"
implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.0.0'
/ * Fragment Navigation * /
implementation "androidx.navigation:navigation-fragment-ktx:$NAVIGATION_VERSION"
implementation "androidx.navigation:navigation-ui-ktx:$NAVIGATION_VERSION"
/ * Unit testing * / / * Unit testing * /
testImplementation "junit:junit:$JUNIT_VERSION" testImplementation "junit:junit:$JUNIT_VERSION"
androidTestRuntimeOnly "org.jetbrains.kotlin:kotlin-test-junit:$KOTLIN_VERSION" androidTestRuntimeOnly "org.jetbrains.kotlin:kotlin-test-junit:$KOTLIN_VERSION"

View File

@@ -0,0 +1,46 @@
package com.appttude.h_mal.farmr.base
import android.text.Editable
import android.text.TextWatcher
import android.view.ViewGroup
import android.widget.EditText
import androidx.annotation.LayoutRes
import androidx.core.view.children
open class FormFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int) : BaseFragment<V>(contentLayoutId) {
private val initialFormData = mutableMapOf<Int, String>()
private val formData = mutableMapOf<Int, String>()
fun applyFormListener(view: ViewGroup) {
view.children.forEach {
if (it is EditText) {
initialFormData[it.id] = it.text.trim().toString()
setDataInMap(it.id, it.text.trim().toString())
it.addCustomTextWatch()
} else if (it is ViewGroup) {
applyFormListener(it)
}
}
}
fun didFormChange(): Boolean {
return !(initialFormData.all { (k, v) ->
formData[k] == v
})
}
private fun EditText.addCustomTextWatch() {
addTextChangedListener(object : TextWatcher{
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { }
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
setDataInMap(id, p0.toString())
}
override fun afterTextChanged(p0: Editable?) { }
})
}
private fun setDataInMap(id: Int, text: String) {
formData[id] = text
}
}

View File

@@ -13,6 +13,7 @@ import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.base.BaseFragment import com.appttude.h_mal.farmr.base.BaseFragment
import com.appttude.h_mal.farmr.model.ShiftType import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.model.Success import com.appttude.h_mal.farmr.model.Success
import com.appttude.h_mal.farmr.utils.goBack
import com.appttude.h_mal.farmr.utils.setDatePicker import com.appttude.h_mal.farmr.utils.setDatePicker
import com.appttude.h_mal.farmr.viewmodel.FilterViewModel import com.appttude.h_mal.farmr.viewmodel.FilterViewModel
@@ -33,7 +34,6 @@ class FilterDataFragment : BaseFragment<FilterViewModel>(R.layout.fragment_filte
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setTitle(getString(R.string.title_activity_filter_data))
LocationET = view.findViewById(R.id.filterLocationEditText) LocationET = view.findViewById(R.id.filterLocationEditText)
dateFromET = view.findViewById(R.id.fromdateInEditText) dateFromET = view.findViewById(R.id.fromdateInEditText)
@@ -75,6 +75,16 @@ class FilterDataFragment : BaseFragment<FilterViewModel>(R.layout.fragment_filte
submit.setOnClickListener(this) submit.setOnClickListener(this)
} }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(false)
}
override fun onResume() {
super.onResume()
setTitle(getString(R.string.title_activity_filter_data))
}
override fun onItemSelected( override fun onItemSelected(
parentView: AdapterView<*>?, parentView: AdapterView<*>?,
selectedItemView: View?, selectedItemView: View?,
@@ -100,6 +110,6 @@ class FilterDataFragment : BaseFragment<FilterViewModel>(R.layout.fragment_filte
override fun onSuccess(data: Any?) { override fun onSuccess(data: Any?) {
super.onSuccess(data) super.onSuccess(data)
if (data is Success) popBackStack() if (data is Success) goBack()
} }
} }

View File

@@ -1,7 +1,9 @@
package com.appttude.h_mal.farmr.ui package com.appttude.h_mal.farmr.ui
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.LinearLayout import android.widget.LinearLayout
@@ -9,10 +11,10 @@ import android.widget.RadioButton
import android.widget.RadioGroup import android.widget.RadioGroup
import android.widget.ScrollView import android.widget.ScrollView
import android.widget.TextView import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import com.appttude.h_mal.farmr.R import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.base.BackPressedListener import com.appttude.h_mal.farmr.base.FormFragment
import com.appttude.h_mal.farmr.base.BaseFragment
import com.appttude.h_mal.farmr.model.ShiftType import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.model.Success import com.appttude.h_mal.farmr.model.Success
import com.appttude.h_mal.farmr.utils.ID import com.appttude.h_mal.farmr.utils.ID
@@ -20,16 +22,18 @@ import com.appttude.h_mal.farmr.utils.createDialog
import com.appttude.h_mal.farmr.utils.displayToast import com.appttude.h_mal.farmr.utils.displayToast
import com.appttude.h_mal.farmr.utils.formatAsCurrencyString import com.appttude.h_mal.farmr.utils.formatAsCurrencyString
import com.appttude.h_mal.farmr.utils.formatToTwoDpString import com.appttude.h_mal.farmr.utils.formatToTwoDpString
import com.appttude.h_mal.farmr.utils.goBack
import com.appttude.h_mal.farmr.utils.hide import com.appttude.h_mal.farmr.utils.hide
import com.appttude.h_mal.farmr.utils.popBackStack
import com.appttude.h_mal.farmr.utils.setDatePicker import com.appttude.h_mal.farmr.utils.setDatePicker
import com.appttude.h_mal.farmr.utils.setTimePicker import com.appttude.h_mal.farmr.utils.setTimePicker
import com.appttude.h_mal.farmr.utils.show import com.appttude.h_mal.farmr.utils.show
import com.appttude.h_mal.farmr.utils.validateField import com.appttude.h_mal.farmr.utils.validateField
import com.appttude.h_mal.farmr.viewmodel.SubmissionViewModel import com.appttude.h_mal.farmr.viewmodel.SubmissionViewModel
class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_item), class FragmentAddItem : FormFragment<SubmissionViewModel>(R.layout.fragment_add_item),
RadioGroup.OnCheckedChangeListener, BackPressedListener { RadioGroup.OnCheckedChangeListener {
private lateinit var onBackPressed: OnBackPressedCallback
private lateinit var mHourlyRadioButton: RadioButton private lateinit var mHourlyRadioButton: RadioButton
private lateinit var mPieceRadioButton: RadioButton private lateinit var mPieceRadioButton: RadioButton
@@ -117,61 +121,87 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
setupViewAfterViewCreated() setupViewAfterViewCreated()
} }
private fun setupViewAfterViewCreated() { override fun onCreate(savedInstanceState: Bundle?) {
id = arguments?.getLong(ID) super.onCreate(savedInstanceState)
wholeView.hide() setHasOptionsMenu(false)
// This callback is only called when MyFragment is at least started
val title = when (arguments?.containsKey(ID)) { onBackPressed = object : OnBackPressedCallback(false) {
true -> { override fun handleOnBackPressed() {
// Since we are editing a shift lets load the shift data into the views onBackPressed()
viewModel.getCurrentShift(arguments!!.getLong(ID))?.run {
mLocationEditText.setText(description)
mDateEditText.setText(date)
// Set types
mType = ShiftType.getEnumByType(type)
mDescription = description
mDate = date
mPayRate = rateOfPay
when (ShiftType.getEnumByType(type)) {
ShiftType.HOURLY -> {
mHourlyRadioButton.isChecked = true
mPieceRadioButton.isChecked = false
mTimeInEditText.setText(timeIn)
mTimeOutEditText.setText(timeOut)
mBreakEditText.setText(breakMins.toString())
val durationText = "${duration.formatToTwoDpString()} Hours"
mDurationTextView.text = durationText
// Set fields
mTimeIn = timeIn
mTimeOut = timeOut
mBreaks = breakMins
}
ShiftType.PIECE -> {
mHourlyRadioButton.isChecked = false
mPieceRadioButton.isChecked = true
mUnitEditText.setText(units.formatToTwoDpString())
// Set piece rate units
mUnits = units
}
}
mPayRateEditText.setText(rateOfPay.formatAsCurrencyString())
mTotalPayTextView.text = totalPay.formatAsCurrencyString()
calculateTotalPay()
}
// Return title
getString(R.string.edit_item_title)
} }
}
requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressed)
}
override fun onResume() {
super.onResume()
val title = when (arguments?.containsKey(ID)) {
true -> getString(R.string.edit_item_title)
else -> getString(R.string.add_item_title) else -> getString(R.string.add_item_title)
} }
setTitle(title) setTitle(title)
onBackPressed.isEnabled = true
}
override fun onPause() {
super.onPause()
onBackPressed.isEnabled = false
}
private fun setupViewAfterViewCreated() {
val id = try {
FragmentAddItemArgs.fromBundle(requireArguments()).shiftId
} catch (e: Exception) {
Log.i("Nav Args", "Failed to retrieve args from navigation")
null
}
wholeView.hide()
// Since we are editing a shift lets load the shift data into the views
id?.let { viewModel.getCurrentShift(id) }?.run {
mLocationEditText.setText(description)
mDateEditText.setText(date)
// Set types
mType = ShiftType.getEnumByType(type)
mDescription = description
mDate = date
mPayRate = rateOfPay
when (ShiftType.getEnumByType(type)) {
ShiftType.HOURLY -> {
mHourlyRadioButton.isChecked = true
mPieceRadioButton.isChecked = false
mTimeInEditText.setText(timeIn)
mTimeOutEditText.setText(timeOut)
mBreakEditText.setText(breakMins.toString())
val durationText = "${duration.formatToTwoDpString()} Hours"
mDurationTextView.text = durationText
// Set fields
mTimeIn = timeIn
mTimeOut = timeOut
mBreaks = breakMins
}
ShiftType.PIECE -> {
mHourlyRadioButton.isChecked = false
mPieceRadioButton.isChecked = true
mUnitEditText.setText(units.formatToTwoDpString())
// Set piece rate units
mUnits = units
}
}
mPayRateEditText.setText(rateOfPay.formatAsCurrencyString())
mTotalPayTextView.text = totalPay.formatAsCurrencyString()
calculateTotalPay()
}
applyFormListener(view = view as ViewGroup)
} }
override fun onCheckedChanged(radioGroup: RadioGroup, id: Int) { override fun onCheckedChanged(radioGroup: RadioGroup, id: Int) {
@@ -264,6 +294,7 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
StringBuilder().append(mDuration).append(" hours").toString() StringBuilder().append(mDuration).append(" hours").toString()
mDuration!! * mPayRate mDuration!! * mPayRate
} }
ShiftType.PIECE -> { ShiftType.PIECE -> {
(mUnits ?: 0f) * mPayRate (mUnits ?: 0f) * mPayRate
} }
@@ -272,27 +303,26 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
} }
} }
override fun onBackPressed(): Boolean { fun onBackPressed() {
if (mRadioGroup.checkedRadioButtonId == -1) { if (didFormChange()) {
mActivity?.popBackStack()
} else {
requireContext().createDialog( requireContext().createDialog(
title = "Discard Changes?", title = "Discard Changes?",
message = "Are you sure you want to discard changes?", message = "Are you sure you want to discard changes?",
displayCancel = true, displayCancel = true,
okCallback = { _, _ -> okCallback = { _, _ ->
mActivity?.popBackStack() goBack()
} }
) )
} else {
goBack()
} }
return true
} }
override fun onSuccess(data: Any?) { override fun onSuccess(data: Any?) {
super.onSuccess(data) super.onSuccess(data)
if (data is Success) { if (data is Success) {
displayToast(data.successMessage) displayToast(data.successMessage)
popBackStack() goBack()
} }
} }
} }

View File

@@ -3,13 +3,14 @@ package com.appttude.h_mal.farmr.ui
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import com.appttude.h_mal.farmr.R import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.base.BackPressedListener
import com.appttude.h_mal.farmr.base.BaseFragment import com.appttude.h_mal.farmr.base.BaseFragment
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
import com.appttude.h_mal.farmr.model.Order import com.appttude.h_mal.farmr.model.Order
@@ -17,25 +18,43 @@ import com.appttude.h_mal.farmr.model.Sortable
import com.appttude.h_mal.farmr.model.Success import com.appttude.h_mal.farmr.model.Success
import com.appttude.h_mal.farmr.utils.createDialog import com.appttude.h_mal.farmr.utils.createDialog
import com.appttude.h_mal.farmr.utils.displayToast import com.appttude.h_mal.farmr.utils.displayToast
import com.appttude.h_mal.farmr.utils.hide import com.appttude.h_mal.farmr.utils.navigateTo
import com.appttude.h_mal.farmr.utils.navigateToFragment
import com.appttude.h_mal.farmr.utils.show
import com.appttude.h_mal.farmr.viewmodel.MainViewModel import com.appttude.h_mal.farmr.viewmodel.MainViewModel
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import java.io.File import java.io.File
import kotlin.system.exitProcess import kotlin.system.exitProcess
class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPressedListener { class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main) {
private lateinit var productListView: RecyclerView private lateinit var productListView: RecyclerView
private lateinit var emptyView: View private lateinit var emptyView: View
private lateinit var mAdapter: ShiftListAdapter private lateinit var mAdapter: ShiftListAdapter
private lateinit var onBackPressed: OnBackPressedCallback
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setTitle("Shift List")
// Inflate the layout for this fragment // Inflate the layout for this fragment
setHasOptionsMenu(true) setHasOptionsMenu(true)
// This callback is only called when MyFragment is at least started
onBackPressed = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
onBackPressed()
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressed)
}
override fun onResume() {
super.onResume()
setTitle("Shift List")
onBackPressed.isEnabled = true
}
override fun onPause() {
super.onPause()
onBackPressed.isEnabled = false
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -50,7 +69,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
productListView.adapter = mAdapter productListView.adapter = mAdapter
view.findViewById<FloatingActionButton>(R.id.fab1).setOnClickListener { view.findViewById<FloatingActionButton>(R.id.fab1).setOnClickListener {
navigateToFragment(FragmentAddItem(), name = "additem") navigateTo(R.id.main_to_addItem)
} }
} }
@@ -59,6 +78,11 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
viewModel.refreshLiveData() viewModel.refreshLiveData()
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
// Inflate the menu; this adds items to the action bar if it is present.
inflater.inflate(R.menu.menu_main, menu)
}
override fun onSuccess(data: Any?) { override fun onSuccess(data: Any?) {
super.onSuccess(data) super.onSuccess(data)
if (data is List<*>) { if (data is List<*>) {
@@ -87,7 +111,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
} }
R.id.filter_data -> { R.id.filter_data -> {
navigateToFragment(FilterDataFragment(), name = "filterdata") navigateTo(R.id.main_to_filterData)
return true return true
} }
@@ -170,21 +194,13 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
file file
) )
intent.setDataAndType(excelUri, "application/vnd.ms-excel") intent.setDataAndType(excelUri, "application/vnd.ms-excel")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent) startActivity(intent)
} }
} }
private fun exportDialog() { fun onBackPressed() {
AlertDialog.Builder(context)
.setTitle("Export?")
.setMessage("Exporting current filtered data. Continue?")
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _, _ -> exportData() }.create().show()
}
override fun onBackPressed(): Boolean {
requireContext().createDialog( requireContext().createDialog(
title = "Leave?", title = "Leave?",
message = "Are you sure you want to exit Farmr?", message = "Are you sure you want to exit Farmr?",
@@ -198,6 +214,5 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
exitProcess(0) exitProcess(0)
} }
) )
return true
} }
} }

View File

@@ -13,6 +13,7 @@ import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.utils.CURRENCY import com.appttude.h_mal.farmr.utils.CURRENCY
import com.appttude.h_mal.farmr.utils.formatAsCurrencyString import com.appttude.h_mal.farmr.utils.formatAsCurrencyString
import com.appttude.h_mal.farmr.utils.hide import com.appttude.h_mal.farmr.utils.hide
import com.appttude.h_mal.farmr.utils.navigateTo
import com.appttude.h_mal.farmr.utils.navigateToFragment import com.appttude.h_mal.farmr.utils.navigateToFragment
import com.appttude.h_mal.farmr.utils.show import com.appttude.h_mal.farmr.utils.show
import com.appttude.h_mal.farmr.viewmodel.InfoViewModel import com.appttude.h_mal.farmr.viewmodel.InfoViewModel
@@ -35,7 +36,6 @@ class FurtherInfoFragment : BaseFragment<InfoViewModel>(R.layout.fragment_futher
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setTitle(getString(R.string.further_info_title))
progressBarFI = view.findViewById(R.id.progressBar_info) progressBarFI = view.findViewById(R.id.progressBar_info)
wholeView = view.findViewById(R.id.further_info_view) wholeView = view.findViewById(R.id.further_info_view)
@@ -52,11 +52,24 @@ class FurtherInfoFragment : BaseFragment<InfoViewModel>(R.layout.fragment_futher
hourlyDetailHolder = view.findViewById(R.id.details_hourly_details) hourlyDetailHolder = view.findViewById(R.id.details_hourly_details)
unitsHolder = view.findViewById(R.id.details_units_holder) unitsHolder = view.findViewById(R.id.details_units_holder)
val id = FurtherInfoFragmentArgs.fromBundle(requireArguments()).shiftId
editButton.setOnClickListener { editButton.setOnClickListener {
navigateToFragment(FragmentAddItem(), name = "additem", bundle = arguments!!) val nav = FurtherInfoFragmentDirections.furtherInfoToAddItem(id)
navigateTo(nav)
} }
viewModel.retrieveData(arguments) viewModel.retrieveData(id)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(false)
}
override fun onResume() {
super.onResume()
setTitle(getString(R.string.further_info_title))
} }
override fun onSuccess(data: Any?) { override fun onSuccess(data: Any?) {

View File

@@ -1,19 +1,15 @@
package com.appttude.h_mal.farmr.ui package com.appttude.h_mal.farmr.ui
import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.MenuItem
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.app.ActivityCompat import androidx.navigation.fragment.NavHostFragment
import com.appttude.h_mal.farmr.R import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.base.BackPressedListener
import com.appttude.h_mal.farmr.base.BaseActivity import com.appttude.h_mal.farmr.base.BaseActivity
import com.appttude.h_mal.farmr.utils.popBackStack
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
private lateinit var toolbar: Toolbar private lateinit var toolbar: Toolbar
private lateinit var navHost: NavHostFragment
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -21,26 +17,19 @@ class MainActivity : BaseActivity() {
toolbar = findViewById(R.id.toolbar) toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
val fragmentTransaction = supportFragmentManager.beginTransaction() navHost = supportFragmentManager
fragmentTransaction.replace(R.id.container, FragmentMain()).addToBackStack("main").commit() .findFragmentById(R.id.container) as NavHostFragment
val navController = navHost.navController
navController.setGraph(R.navigation.shift_navigation)
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Inflate the menu; this adds items to the action bar if it is present. // Handle action bar item clicks here. The action bar will
menuInflater.inflate(R.menu.menu_main, menu) // automatically handle clicks on the Home/Up button, so long
return true // as you specify a parent activity in AndroidManifest.xml.
} when (item.itemId) {
android.R.id.home -> onBackPressed()
override fun onBackPressed() {
val currentFragment = supportFragmentManager.findFragmentById(R.id.container)
if (currentFragment is BackPressedListener) {
currentFragment.onBackPressed()
} else {
if (supportFragmentManager.backStackEntryCount > 1) {
popBackStack()
} else {
super.onBackPressed()
}
} }
return super.onOptionsItemSelected(item)
} }
} }

View File

@@ -14,6 +14,9 @@ import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
import com.appttude.h_mal.farmr.model.ShiftType import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.utils.ID import com.appttude.h_mal.farmr.utils.ID
import com.appttude.h_mal.farmr.utils.formatToTwoDpString import com.appttude.h_mal.farmr.utils.formatToTwoDpString
import com.appttude.h_mal.farmr.utils.formatToTwoDp
import com.appttude.h_mal.farmr.utils.generateView
import com.appttude.h_mal.farmr.utils.navigateTo
import com.appttude.h_mal.farmr.utils.navigateToFragment import com.appttude.h_mal.farmr.utils.navigateToFragment
class ShiftListAdapter( class ShiftListAdapter(
@@ -64,23 +67,16 @@ class ShiftListAdapter(
} }
} }
val b: Bundle = Bundle()
b.putLong(ID, data.id)
view.setOnClickListener { view.setOnClickListener {
// Navigate to further info // Navigate to further info
fragment.navigateToFragment( val nav = FragmentMainDirections.mainToFurtherInfo(data.id)
FurtherInfoFragment(), fragment.navigateTo(nav)
bundle = b,
name = "furtherinfo"
)
} }
editView.setOnClickListener { editView.setOnClickListener {
// Navigate to edit // Navigate to edit
fragment.navigateToFragment( val nav = FragmentMainDirections.mainToAddItem(data.id)
FragmentAddItem(), fragment.navigateTo(nav)
bundle = b,
name = "additem"
)
} }
view.setOnLongClickListener { view.setOnLongClickListener {
AlertDialog.Builder(it.context) AlertDialog.Builder(it.context)

View File

@@ -4,4 +4,5 @@ const val LEGACY = "LEGACY_"
const val DATE_FORMAT = "yyyy-MM-dd" const val DATE_FORMAT = "yyyy-MM-dd"
const val TIME_FORMAT = "hh:mm" const val TIME_FORMAT = "hh:mm"
const val ID = "ID" const val ID = "ID"
const val SHIFT_ID = "shiftId"
const val CURRENCY = "£" const val CURRENCY = "£"

View File

@@ -0,0 +1,32 @@
package com.appttude.h_mal.farmr.utils
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.NavDirections
import androidx.navigation.Navigation
import com.appttude.h_mal.farmr.R
fun Fragment.navigateToFragment(newFragment: Fragment) {
childFragmentManager.beginTransaction()
.add(R.id.container, newFragment)
.commit()
}
fun View.navigateTo(navigationId: Int) {
Navigation.findNavController(this).navigate(navigationId)
}
fun View.navigateTo(navDirections: NavDirections) {
Navigation.findNavController(this).navigate(navDirections)
}
fun Fragment.navigateTo(navigationId: Int) {
Navigation.findNavController(requireView()).navigate(navigationId)
}
fun Fragment.navigateTo(navDirections: NavDirections) {
Navigation.findNavController(requireView()).navigate(navDirections)
}
fun Fragment.goBack() = Navigation.findNavController(requireView()).popBackStack()

View File

@@ -10,13 +10,7 @@ class InfoViewModel(
repository: Repository repository: Repository
) : ShiftViewModel(repository) { ) : ShiftViewModel(repository) {
fun retrieveData(bundle: Bundle?) { fun retrieveData(id: Long) {
val id = bundle?.getLong(ID)
if (id == null) {
onError("Failed to retrieve shift")
return
}
val shift = getCurrentShift(id) val shift = getCurrentShift(id)
if (shift == null) { if (shift == null) {
onError("Failed to retrieve shift") onError("Failed to retrieve shift")

View File

@@ -284,6 +284,7 @@ class SubmissionViewModel(
description = description, description = description,
date = date, date = date,
units = units!!, units = units!!,
rateOfPay = rateOfPay, rateOfPay = rateOfPay,
) )
} }

View File

@@ -32,14 +32,17 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<FrameLayout <androidx.fragment.app.FragmentContainerView
android:layout_below="@id/appbar" android:layout_below="@id/appbar"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:name="androidx.navigation.fragment.NavHostFragment"
app:layout_constraintTop_toBottomOf="@id/appbar" app:layout_constraintTop_toBottomOf="@id/appbar"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/container"> app:defaultNavHost="true"
</FrameLayout> android:id="@+id/container"
tools:layout="@layout/fragment_main">
</androidx.fragment.app.FragmentContainerView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/shift_navigation"
app:startDestination="@id/fragmentMain">
<fragment
android:id="@+id/fragmentMain"
android:name="com.appttude.h_mal.farmr.ui.FragmentMain"
android:label="fragment_main"
tools:layout="@layout/fragment_main" >
<action
android:id="@+id/main_to_addItem"
app:destination="@id/fragmentAddItem" />
<action
android:id="@+id/main_to_filterData"
app:destination="@id/filterDataFragment" />
<action
android:id="@+id/main_to_furtherInfo"
app:destination="@id/furtherInfoFragment" />
</fragment>
<fragment
android:id="@+id/fragmentAddItem"
android:name="com.appttude.h_mal.farmr.ui.FragmentAddItem"
android:label="fragment_add_item"
tools:layout="@layout/fragment_add_item">
<argument
android:name="shiftId"
app:argType="long" />
</fragment>
<fragment
android:id="@+id/filterDataFragment"
android:name="com.appttude.h_mal.farmr.ui.FilterDataFragment"
android:label="fragment_filter_data"
tools:layout="@layout/fragment_filter_data" />
<fragment
android:id="@+id/furtherInfoFragment"
android:name="com.appttude.h_mal.farmr.ui.FurtherInfoFragment"
android:label="fragment_futher_info"
tools:layout="@layout/fragment_futher_info" >
<action
android:id="@+id/furtherInfo_to_AddItem"
app:destination="@id/fragmentAddItem" />
<argument
android:name="shiftId"
app:argType="long" />
</fragment>
</navigation>

View File

@@ -17,12 +17,10 @@ class InfoViewModelTest : ShiftViewModelTest<InfoViewModel>() {
// Arrange // Arrange
val id = anyLong() val id = anyLong()
val shift = mockk<ShiftObject>() val shift = mockk<ShiftObject>()
val bundle = mockk<Bundle>()
// Act // Act
every { repository.readSingleShiftFromDatabase(id) }.returns(shift) every { repository.readSingleShiftFromDatabase(id) }.returns(shift)
every { bundle.getLong(ID) }.returns(id) viewModel.retrieveData(id)
viewModel.retrieveData(bundle)
// Assert // Assert
assertIs<ShiftObject>(retrieveCurrentData()) assertIs<ShiftObject>(retrieveCurrentData())
@@ -32,35 +30,14 @@ class InfoViewModelTest : ShiftViewModelTest<InfoViewModel>() {
) )
} }
@Test
fun retrieveData_noValidBundleAndId_unsuccessfulRetrieval() {
// Arrange
val id = anyLong()
val shift = mockk<ShiftObject>()
val bundle = mockk<Bundle>()
// Act
every { repository.readSingleShiftFromDatabase(id) }.returns(shift)
every { bundle.getLong(ID) }.returns(id)
viewModel.retrieveData(null)
// Assert
assertEquals(
retrieveCurrentError(),
"Failed to retrieve shift"
)
}
@Test @Test
fun retrieveData_validBundleNoShift_successfulRetrieval() { fun retrieveData_validBundleNoShift_successfulRetrieval() {
// Arrange // Arrange
val id = anyLong() val id = anyLong()
val bundle = mockk<Bundle>()
// Act // Act
every { repository.readSingleShiftFromDatabase(id) }.returns(null) every { repository.readSingleShiftFromDatabase(id) }.returns(null)
every { bundle.getLong(ID) }.returns(id) viewModel.retrieveData(id)
viewModel.retrieveData(bundle)
// Assert // Assert
assertEquals( assertEquals(

View File

@@ -13,6 +13,7 @@ buildscript {
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
} }
dependencies { dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$NAVIGATION_VERSION"
classpath "com.android.tools.build:gradle:$GRADLE_PLUGIN_VERSION" classpath "com.android.tools.build:gradle:$GRADLE_PLUGIN_VERSION"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
classpath "com.autonomousapps:dependency-analysis-gradle-plugin:$GRADLE_ANALYZE_VERSION" classpath "com.autonomousapps:dependency-analysis-gradle-plugin:$GRADLE_ANALYZE_VERSION"

View File

@@ -8,6 +8,7 @@ MATERIAL_VERSION = 1.0.0
CONSTR_LAYOUT_VERSION = 1.1.3 CONSTR_LAYOUT_VERSION = 1.1.3
LIFECYCLE_VERSION = 2.5.1 LIFECYCLE_VERSION = 2.5.1
VIEWMODEL_VERSION = 2.4.1 VIEWMODEL_VERSION = 2.4.1
NAVIGATION_VERSION = 2.3.2
PREFERENCES_VERSION = 1.2.1 PREFERENCES_VERSION = 1.2.1
MOKITO_INLINE_VERSION = 2.13.0 MOKITO_INLINE_VERSION = 2.13.0
CORE_TEST_VERSION = 2.1.0 CORE_TEST_VERSION = 2.1.0