Merge branch 'master' into navigation_implementation

# Conflicts:
#	app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentAddItem.kt
#	app/src/main/java/com/appttude/h_mal/farmr/ui/MainActivity.kt
#	app/src/main/java/com/appttude/h_mal/farmr/ui/ShiftListAdapter.kt
#	app/src/main/java/com/appttude/h_mal/farmr/utils/Formatting.kt
This commit is contained in:
2023-09-10 17:02:13 +01:00
17 changed files with 192 additions and 61 deletions

View File

@@ -127,4 +127,4 @@ workflows:
only:
- release
requires:
- build-and-test
- run_instrumentation_test

View File

@@ -14,8 +14,8 @@ android {
applicationId "com.appttude.h_mal.farmr"
minSdkVersion MIN_SDK_VERSION
targetSdkVersion TARGET_SDK_VERSION
versionCode 3
versionName "2.1"
versionCode 5
versionName "2.3"
testInstrumentationRunner 'com.appttude.h_mal.farmr.application.TestRunner'
vectorDrawables.useSupportLibrary = true
}

View File

@@ -1,6 +1,5 @@
package com.appttude.h_mal.farmr.ui.robots
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions.scrollTo
import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.model.ShiftType

View File

@@ -0,0 +1,59 @@
package com.appttude.h_mal.farmr.base
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.appttude.h_mal.farmr.utils.hide
import com.appttude.h_mal.farmr.utils.show
abstract class BaseListAdapter<T : Any>(
diff: DiffUtil.ItemCallback<T>,
private val layoutId: Int,
private val emptyView: View
) : ListAdapter<T, BaseListAdapter.CurrentViewHolder>(diff) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CurrentViewHolder {
val currentViewHolder = LayoutInflater
.from(parent.context)
.inflate(layoutId, parent, false)
return CurrentViewHolder(currentViewHolder)
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
checkEmpty()
}
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
checkEmpty()
}
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
checkEmpty()
}
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
checkEmpty()
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
checkEmpty()
}
fun checkEmpty() {
if (itemCount == 0) emptyView.show()
else emptyView.hide()
}
})
}
class CurrentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}

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

@@ -34,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)
@@ -76,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?,

View File

@@ -2,6 +2,7 @@ package com.appttude.h_mal.farmr.ui
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
@@ -12,7 +13,7 @@ import android.widget.TextView
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
@@ -30,7 +31,7 @@ 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),
class FragmentAddItem : FormFragment<SubmissionViewModel>(R.layout.fragment_add_item),
RadioGroup.OnCheckedChangeListener, BackPressedListener {
private lateinit var mHourlyRadioButton: RadioButton
@@ -119,15 +120,29 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
setupViewAfterViewCreated()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(false)
}
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)
}
private fun setupViewAfterViewCreated() {
val id = arguments?.takeIf { it.containsKey(SHIFT_ID) }
?.let { FragmentAddItemArgs.fromBundle(it).shiftId }
wholeView.hide()
val title = id?.let {
if (id != null) {
// Since we are editing a shift lets load the shift data into the views
viewModel.getCurrentShift(id)?.run {
viewModel.getCurrentShift(arguments!!.getLong(ID))?.run {
mLocationEditText.setText(description)
mDateEditText.setText(date)
@@ -167,9 +182,9 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
calculateTotalPay()
}
getString(R.string.edit_item_title)
} ?: getString(R.string.add_item_title)
setTitle(title)
}
applyFormListener(view = view as ViewGroup)
}
override fun onCheckedChanged(radioGroup: RadioGroup, id: Int) {
@@ -272,17 +287,17 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
}
override fun onBackPressed(): Boolean {
if (mRadioGroup.checkedRadioButtonId == -1) {
goBack()
} else {
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
}

View File

@@ -3,6 +3,8 @@ 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.core.content.FileProvider
@@ -34,28 +36,25 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTitle("Shift List")
// Inflate the layout for this fragment
setHasOptionsMenu(true)
}
override fun onResume() {
super.onResume()
setTitle("Shift List")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mAdapter = ShiftListAdapter(this) {
emptyView = view.findViewById(R.id.empty_view)
productListView = view.findViewById(R.id.list_item_view)
mAdapter = ShiftListAdapter(this, emptyView) {
viewModel.deleteShift(it)
}
productListView = view.findViewById(R.id.list_item_view)
productListView.adapter = mAdapter
emptyView = view.findViewById(R.id.empty_view)
mAdapter.registerAdapterDataObserver(object : AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
if (mAdapter.itemCount == 0) emptyView.show()
else emptyView.hide()
}
})
view.findViewById<FloatingActionButton>(R.id.fab1).setOnClickListener {
navigateTo(R.id.main_to_addItem)
@@ -67,6 +66,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<*>) {

View File

@@ -36,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)
@@ -63,6 +62,16 @@ class FurtherInfoFragment : BaseFragment<InfoViewModel>(R.layout.fragment_futher
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?) {
super.onSuccess(data)
if (data is ShiftObject) data.setupView()

View File

@@ -1,8 +1,5 @@
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
@@ -31,12 +28,6 @@ class MainActivity : BaseActivity() {
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 onSupportNavigateUp(): Boolean {
val currentFragment = navHost.parentFragment
return if (currentFragment is BackPressedListener) {
@@ -50,6 +41,7 @@ class MainActivity : BaseActivity() {
}
}
override fun onBackPressed() {
val currentFragment = supportFragmentManager.findFragmentById(R.id.container)
if (currentFragment is BackPressedListener) {

View File

@@ -3,17 +3,17 @@ package com.appttude.h_mal.farmr.ui
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.os.Bundle
import android.view.ViewGroup
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.Fragment
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.base.BaseListAdapter
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
@@ -21,18 +21,12 @@ import com.appttude.h_mal.farmr.utils.navigateToFragment
class ShiftListAdapter(
private val fragment: Fragment,
emptyView: View,
private val longPressCallback: (Long) -> Unit
) : 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)
}
) : BaseListAdapter<ShiftObject>(diffCallBack, R.layout.list_item_1, emptyView) {
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: BaseRecyclerAdapter.CurrentViewHolder, position: Int) {
override fun onBindViewHolder(holder: CurrentViewHolder, position: Int) {
val view = holder.itemView
val data = getItem(position)
@@ -49,7 +43,7 @@ class ShiftListAdapter(
val typeText: String = data.type
val descriptionText: String = data.description
val dateText: String = data.date
val totalPayText: String = data.totalPay.formatToTwoDp().toString()
val totalPayText: String = data.totalPay.formatToTwoDpString()
descriptionTextView.text = descriptionText
dateTextView.text = dateText

View File

@@ -26,7 +26,7 @@ fun Float.formatAsCurrencyString(): String? {
}
fun Float.formatToTwoDpString(): String {
return String.format("%.2f", this)
return toBigDecimal().setScale(2).toString()
}
fun String.dateStringIsValid(): Boolean {

View File

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

View File

@@ -24,8 +24,11 @@
app:backgroundTint="@color/colorPrimary" />
<include
android:visibility="gone"
android:layout_centerInParent="true"
android:visibility="visible"
layout="@layout/empty_list_view"
android:id="@+id/empty_view"/>
android:id="@+id/empty_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>

View File

@@ -23,7 +23,7 @@ platform :android do
desc "Deploy a new version to the Google Play"
lane :deploy do
gradle(task: "clean assembleRelease")
gradle(task: "clean bundle", build_type: "Release")
upload_to_play_store
end
end

View File

@@ -27,8 +27,8 @@ KOTLIN_VERSION = 1.7.10
GRADLE_ANALYZE_VERSION = 1.20.0
# Android configuration
COMPILE_SDK_VERSION = android-31
TARGET_SDK_VERSION = 31
COMPILE_SDK_VERSION = android-33
TARGET_SDK_VERSION = 33
MIN_SDK_VERSION = 21
# Gradle parameters