Merge pull request #42 from hmalik144/master

version 6 - 2.4
This commit is contained in:
2023-09-10 23:32:27 +01:00
committed by GitHub
19 changed files with 326 additions and 162 deletions

1
.gitignore vendored
View File

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

View File

@@ -1,5 +1,6 @@
apply plugin: 'com.android.application'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'androidx.navigation.safeargs'
def relStorePassword = System.getenv("RELEASE_STORE_PASSWORD")
def relKeyPassword = System.getenv("RELEASE_KEY_PASSWORD")
@@ -13,8 +14,8 @@ android {
applicationId "com.appttude.h_mal.farmr"
minSdkVersion MIN_SDK_VERSION
targetSdkVersion TARGET_SDK_VERSION
versionCode 5
versionName "2.3"
versionCode 6
versionName "2.4"
testInstrumentationRunner 'com.appttude.h_mal.farmr.application.TestRunner'
vectorDrawables.useSupportLibrary = true
}
@@ -49,6 +50,9 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-livedata-core:$LIFECYCLE_VERSION"
implementation "androidx.lifecycle:lifecycle-viewmodel:$LIFECYCLE_VERSION"
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 * /
testImplementation "junit:junit:$JUNIT_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.model.ShiftType
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.viewmodel.FilterViewModel
@@ -33,7 +34,6 @@ class FilterDataFragment : BaseFragment<FilterViewModel>(R.layout.fragment_filte
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setTitle(getString(R.string.title_activity_filter_data))
LocationET = view.findViewById(R.id.filterLocationEditText)
dateFromET = view.findViewById(R.id.fromdateInEditText)
@@ -75,6 +75,16 @@ class FilterDataFragment : BaseFragment<FilterViewModel>(R.layout.fragment_filte
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(
parentView: AdapterView<*>?,
selectedItemView: View?,
@@ -100,6 +110,6 @@ class FilterDataFragment : BaseFragment<FilterViewModel>(R.layout.fragment_filte
override fun onSuccess(data: Any?) {
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
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
@@ -9,10 +11,10 @@ import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.ScrollView
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.core.widget.doAfterTextChanged
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.FormFragment
import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.model.Success
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.formatAsCurrencyString
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.popBackStack
import com.appttude.h_mal.farmr.utils.setDatePicker
import com.appttude.h_mal.farmr.utils.setTimePicker
import com.appttude.h_mal.farmr.utils.show
import com.appttude.h_mal.farmr.utils.validateField
import com.appttude.h_mal.farmr.viewmodel.SubmissionViewModel
class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_item),
RadioGroup.OnCheckedChangeListener, BackPressedListener {
class FragmentAddItem : FormFragment<SubmissionViewModel>(R.layout.fragment_add_item),
RadioGroup.OnCheckedChangeListener {
private lateinit var onBackPressed: OnBackPressedCallback
private lateinit var mHourlyRadioButton: RadioButton
private lateinit var mPieceRadioButton: RadioButton
@@ -117,61 +121,87 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
setupViewAfterViewCreated()
}
private fun setupViewAfterViewCreated() {
id = arguments?.getLong(ID)
wholeView.hide()
val title = when (arguments?.containsKey(ID)) {
true -> {
// Since we are editing a shift lets load the shift data into the views
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)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(false)
// 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()
val title = when (arguments?.containsKey(ID)) {
true -> getString(R.string.edit_item_title)
else -> getString(R.string.add_item_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) {
@@ -264,6 +294,7 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
StringBuilder().append(mDuration).append(" hours").toString()
mDuration!! * mPayRate
}
ShiftType.PIECE -> {
(mUnits ?: 0f) * mPayRate
}
@@ -272,27 +303,26 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
}
}
override fun onBackPressed(): Boolean {
if (mRadioGroup.checkedRadioButtonId == -1) {
mActivity?.popBackStack()
} else {
fun onBackPressed() {
if (didFormChange()) {
requireContext().createDialog(
title = "Discard Changes?",
message = "Are you sure you want to discard changes?",
displayCancel = true,
okCallback = { _, _ ->
mActivity?.popBackStack()
goBack()
}
)
} else {
goBack()
}
return true
}
override fun onSuccess(data: Any?) {
super.onSuccess(data)
if (data is Success) {
displayToast(data.successMessage)
popBackStack()
goBack()
}
}
}

View File

@@ -3,13 +3,14 @@ package com.appttude.h_mal.farmr.ui
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.core.content.FileProvider
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
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.data.legacydb.ShiftObject
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.utils.createDialog
import com.appttude.h_mal.farmr.utils.displayToast
import com.appttude.h_mal.farmr.utils.hide
import com.appttude.h_mal.farmr.utils.navigateToFragment
import com.appttude.h_mal.farmr.utils.show
import com.appttude.h_mal.farmr.utils.navigateTo
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
import com.google.android.material.floatingactionbutton.FloatingActionButton
import java.io.File
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 emptyView: View
private lateinit var mAdapter: ShiftListAdapter
private lateinit var onBackPressed: OnBackPressedCallback
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTitle("Shift List")
// Inflate the layout for this fragment
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?) {
@@ -50,7 +69,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
productListView.adapter = mAdapter
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()
}
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?) {
super.onSuccess(data)
if (data is List<*>) {
@@ -87,7 +111,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
}
R.id.filter_data -> {
navigateToFragment(FilterDataFragment(), name = "filterdata")
navigateTo(R.id.main_to_filterData)
return true
}
@@ -170,21 +194,13 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
file
)
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)
}
}
private fun exportDialog() {
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 {
fun onBackPressed() {
requireContext().createDialog(
title = "Leave?",
message = "Are you sure you want to exit Farmr?",
@@ -198,6 +214,5 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
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.formatAsCurrencyString
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.InfoViewModel
@@ -35,7 +36,6 @@ class FurtherInfoFragment : BaseFragment<InfoViewModel>(R.layout.fragment_futher
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setTitle(getString(R.string.further_info_title))
progressBarFI = view.findViewById(R.id.progressBar_info)
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)
unitsHolder = view.findViewById(R.id.details_units_holder)
val id = FurtherInfoFragmentArgs.fromBundle(requireArguments()).shiftId
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?) {

View File

@@ -1,19 +1,15 @@
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.view.Menu
import android.view.MenuItem
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.base.BackPressedListener
import com.appttude.h_mal.farmr.base.BaseActivity
import com.appttude.h_mal.farmr.utils.popBackStack
class MainActivity : BaseActivity() {
private lateinit var toolbar: Toolbar
private lateinit var navHost: NavHostFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -21,26 +17,19 @@ class MainActivity : BaseActivity() {
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val fragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.container, FragmentMain()).addToBackStack("main").commit()
navHost = supportFragmentManager
.findFragmentById(R.id.container) as NavHostFragment
val navController = navHost.navController
navController.setGraph(R.navigation.shift_navigation)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
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()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
when (item.itemId) {
android.R.id.home -> 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.utils.ID
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
class ShiftListAdapter(
@@ -64,23 +67,16 @@ class ShiftListAdapter(
}
}
val b: Bundle = Bundle()
b.putLong(ID, data.id)
view.setOnClickListener {
// Navigate to further info
fragment.navigateToFragment(
FurtherInfoFragment(),
bundle = b,
name = "furtherinfo"
)
val nav = FragmentMainDirections.mainToFurtherInfo(data.id)
fragment.navigateTo(nav)
}
editView.setOnClickListener {
// Navigate to edit
fragment.navigateToFragment(
FragmentAddItem(),
bundle = b,
name = "additem"
)
val nav = FragmentMainDirections.mainToAddItem(data.id)
fragment.navigateTo(nav)
}
view.setOnLongClickListener {
AlertDialog.Builder(it.context)

View File

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

View File

@@ -1,6 +1,7 @@
package com.appttude.h_mal.farmr.utils
import java.io.IOException
import java.math.RoundingMode
import java.text.NumberFormat
import java.text.SimpleDateFormat
import java.util.Calendar
@@ -26,7 +27,7 @@ fun Float.formatAsCurrencyString(): String? {
}
fun Float.formatToTwoDpString(): String {
return toBigDecimal().setScale(2).toString()
return toBigDecimal().setScale(2, RoundingMode.HALF_DOWN).toString()
}
fun String.dateStringIsValid(): Boolean {

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
) : ShiftViewModel(repository) {
fun retrieveData(bundle: Bundle?) {
val id = bundle?.getLong(ID)
if (id == null) {
onError("Failed to retrieve shift")
return
}
fun retrieveData(id: Long) {
val shift = getCurrentShift(id)
if (shift == null) {
onError("Failed to retrieve shift")

View File

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

View File

@@ -32,14 +32,17 @@
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
<androidx.fragment.app.FragmentContainerView
android:layout_below="@id/appbar"
android:layout_width="0dp"
android:layout_height="0dp"
android:name="androidx.navigation.fragment.NavHostFragment"
app:layout_constraintTop_toBottomOf="@id/appbar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/container">
</FrameLayout>
app:defaultNavHost="true"
android:id="@+id/container"
tools:layout="@layout/fragment_main">
</androidx.fragment.app.FragmentContainerView>
</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
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(bundle)
viewModel.retrieveData(id)
// Assert
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
fun retrieveData_validBundleNoShift_successfulRetrieval() {
// Arrange
val id = anyLong()
val bundle = mockk<Bundle>()
// Act
every { repository.readSingleShiftFromDatabase(id) }.returns(null)
every { bundle.getLong(ID) }.returns(id)
viewModel.retrieveData(bundle)
viewModel.retrieveData(id)
// Assert
assertEquals(

View File

@@ -13,6 +13,7 @@ buildscript {
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$NAVIGATION_VERSION"
classpath "com.android.tools.build:gradle:$GRADLE_PLUGIN_VERSION"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_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
LIFECYCLE_VERSION = 2.5.1
VIEWMODEL_VERSION = 2.4.1
NAVIGATION_VERSION = 2.3.2
PREFERENCES_VERSION = 1.2.1
MOKITO_INLINE_VERSION = 2.13.0
CORE_TEST_VERSION = 2.1.0