mirror of
https://github.com/hmalik144/Farmr.git
synced 2026-01-31 02:41:49 +00:00
- Calender view page added
- migrated to tabbed bottom bar - update and clear filters fixed
This commit is contained in:
@@ -88,4 +88,6 @@ dependencies {
|
|||||||
implementation "org.kodein.di:kodein-di-framework-android-core:$KODEIN_VERSION"
|
implementation "org.kodein.di:kodein-di-framework-android-core:$KODEIN_VERSION"
|
||||||
/ * jxl * /
|
/ * jxl * /
|
||||||
implementation "net.sourceforge.jexcelapi:jxl:$JEXCEL_VERSION"
|
implementation "net.sourceforge.jexcelapi:jxl:$JEXCEL_VERSION"
|
||||||
|
/ * calendar view * /
|
||||||
|
implementation 'com.applandeo:material-calendar-view:1.7.0'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,6 @@ abstract class BaseFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int)
|
|||||||
|
|
||||||
var mActivity: BaseActivity? = null
|
var mActivity: BaseActivity? = null
|
||||||
|
|
||||||
private var shortAnimationDuration by Delegates.notNull<Int>()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
mActivity = requireActivity() as BaseActivity
|
mActivity = requireActivity() as BaseActivity
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import com.appttude.h_mal.farmr.utils.show
|
|||||||
abstract class BaseListAdapter<T : Any>(
|
abstract class BaseListAdapter<T : Any>(
|
||||||
diff: DiffUtil.ItemCallback<T>,
|
diff: DiffUtil.ItemCallback<T>,
|
||||||
private val layoutId: Int,
|
private val layoutId: Int,
|
||||||
private val emptyView: View
|
private val emptyView: View?
|
||||||
) : ListAdapter<T, BaseListAdapter.CurrentViewHolder>(diff) {
|
) : ListAdapter<T, BaseListAdapter.CurrentViewHolder>(diff) {
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
override fun onCreateViewHolder(
|
||||||
@@ -49,8 +49,8 @@ abstract class BaseListAdapter<T : Any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkEmpty() {
|
fun checkEmpty() {
|
||||||
if (itemCount == 0) emptyView.show()
|
if (itemCount == 0) emptyView?.show()
|
||||||
else emptyView.hide()
|
else emptyView?.hide()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.appttude.h_mal.farmr.base
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.navigation.NavDirections
|
||||||
|
import com.appttude.h_mal.farmr.model.ViewState
|
||||||
|
import com.appttude.h_mal.farmr.utils.getGenericClassAt
|
||||||
|
import com.appttude.h_mal.farmr.utils.navigateTo
|
||||||
|
import com.appttude.h_mal.farmr.viewmodel.ApplicationViewModelFactory
|
||||||
|
import org.kodein.di.KodeinAware
|
||||||
|
import org.kodein.di.android.x.kodein
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
@Suppress("EmptyMethod", "EmptyMethod")
|
||||||
|
abstract class ChildFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int) :
|
||||||
|
Fragment(contentLayoutId), KodeinAware {
|
||||||
|
|
||||||
|
override val kodein by kodein()
|
||||||
|
private val factory by instance<ApplicationViewModelFactory>()
|
||||||
|
|
||||||
|
lateinit var viewModel: V
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
viewModel =
|
||||||
|
ViewModelProvider(requireParentFragment().requireParentFragment(), factory)[getGenericClassAt<V>(0).java]
|
||||||
|
configureObserver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configureObserver() {
|
||||||
|
viewModel.uiState.observe(viewLifecycleOwner) {
|
||||||
|
when (it) {
|
||||||
|
is ViewState.HasStarted -> onStarted()
|
||||||
|
is ViewState.HasData<*> -> onSuccess(it.data)
|
||||||
|
is ViewState.HasError<*> -> onFailure(it.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in case of starting operation liveData in viewModel
|
||||||
|
*/
|
||||||
|
open fun onStarted() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in case of success or some data emitted from the liveData in viewModel
|
||||||
|
*/
|
||||||
|
open fun onSuccess(data: Any?) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in case of failure or some error emitted from the liveData in viewModel
|
||||||
|
*/
|
||||||
|
open fun onFailure(error: Any?) {}
|
||||||
|
|
||||||
|
|
||||||
|
fun navigateParent(navArg: Any) {
|
||||||
|
val fragment = requireParentFragment().requireParentFragment()
|
||||||
|
when(navArg) {
|
||||||
|
is Int -> (fragment).navigateTo(navArg)
|
||||||
|
is NavDirections -> (fragment).navigateTo(navArg)
|
||||||
|
else -> { throw IOException("${navArg::class} is not a valid navigation argment") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,5 +20,5 @@ interface Repository {
|
|||||||
timeIn: String?,
|
timeIn: String?,
|
||||||
timeOut: String?,
|
timeOut: String?,
|
||||||
type: String?
|
type: String?
|
||||||
)
|
): Boolean
|
||||||
}
|
}
|
||||||
@@ -64,8 +64,8 @@ class RepositoryImpl(
|
|||||||
timeIn: String?,
|
timeIn: String?,
|
||||||
timeOut: String?,
|
timeOut: String?,
|
||||||
type: String?
|
type: String?
|
||||||
) {
|
): Boolean {
|
||||||
preferenceProvider.saveFilteringDetails(description, timeIn, timeOut, type)
|
return preferenceProvider.saveFilteringDetails(description, timeIn, timeOut, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -44,13 +44,13 @@ class PreferenceProvider(
|
|||||||
timeIn: String?,
|
timeIn: String?,
|
||||||
timeOut: String?,
|
timeOut: String?,
|
||||||
type: String?
|
type: String?
|
||||||
) {
|
): Boolean {
|
||||||
preference.edit()
|
return preference.edit()
|
||||||
.putString(DESCRIPTION, description)
|
.putString(DESCRIPTION, description)
|
||||||
.putString(DATE_IN, timeIn)
|
.putString(DATE_IN, timeIn)
|
||||||
.putString(DATE_OUT, timeOut)
|
.putString(DATE_OUT, timeOut)
|
||||||
.putString(TYPE, type)
|
.putString(TYPE, type)
|
||||||
.apply()
|
.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFilteringDetails(): Map<String, String?> {
|
fun getFilteringDetails(): Map<String, String?> {
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.appttude.h_mal.farmr.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.applandeo.materialcalendarview.CalendarView
|
||||||
|
import com.applandeo.materialcalendarview.EventDay
|
||||||
|
import com.appttude.h_mal.farmr.R
|
||||||
|
import com.appttude.h_mal.farmr.base.ChildFragment
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
|
||||||
|
import com.appttude.h_mal.farmr.utils.tryGet
|
||||||
|
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
|
class CalendarFragment : ChildFragment<MainViewModel>(R.layout.fragment_calendar) {
|
||||||
|
private lateinit var shiftListView: RecyclerView
|
||||||
|
private lateinit var calendarView: CalendarView
|
||||||
|
|
||||||
|
private lateinit var mAdapter: ShiftListAdapter
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
shiftListView = view.findViewById(R.id.shifts_available_recycler)
|
||||||
|
calendarView = view.findViewById(R.id.calendarView)
|
||||||
|
|
||||||
|
mAdapter = ShiftListAdapter(this, null, viewModel)
|
||||||
|
shiftListView.adapter = mAdapter
|
||||||
|
|
||||||
|
calendarView.setOnDayClickListener { populateShiftListsForDay(it.calendar) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
viewModel.refreshLiveData()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(data: Any?) {
|
||||||
|
super.onSuccess(data)
|
||||||
|
if (data is List<*>) {
|
||||||
|
val events: List<EventDay>? = viewModel.retrieveEvents()
|
||||||
|
calendarView.setEvents(events)
|
||||||
|
|
||||||
|
tryGet { calendarView.firstSelectedDate }?.let {
|
||||||
|
populateShiftListsForDay(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateShiftListsForDay(calendar: Calendar) {
|
||||||
|
val data: List<ShiftObject>? = viewModel.getShiftsOnTheDay(calendar)
|
||||||
|
mAdapter.submitList(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.appttude.h_mal.farmr.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.appttude.h_mal.farmr.R
|
||||||
|
import com.appttude.h_mal.farmr.base.ChildFragment
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
|
||||||
|
import com.appttude.h_mal.farmr.model.Success
|
||||||
|
import com.appttude.h_mal.farmr.utils.displayToast
|
||||||
|
import com.appttude.h_mal.farmr.utils.navigateTo
|
||||||
|
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
|
||||||
|
|
||||||
|
class FragmentList : ChildFragment<MainViewModel>(R.layout.fragment_list) {
|
||||||
|
private lateinit var productListView: RecyclerView
|
||||||
|
private lateinit var emptyView: View
|
||||||
|
private lateinit var mAdapter: ShiftListAdapter
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
emptyView = view.findViewById(R.id.empty_view)
|
||||||
|
productListView = view.findViewById(R.id.list_item_view)
|
||||||
|
|
||||||
|
mAdapter = ShiftListAdapter(this, emptyView, viewModel)
|
||||||
|
productListView.adapter = mAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
viewModel.refreshLiveData()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(data: Any?) {
|
||||||
|
super.onSuccess(data)
|
||||||
|
if (data is List<*>) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
mAdapter.submitList(data as List<ShiftObject>)
|
||||||
|
} else if (data is Success) {
|
||||||
|
displayToast(data.successMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,29 +9,26 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import com.appttude.h_mal.farmr.R
|
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.data.legacydb.ShiftObject
|
|
||||||
import com.appttude.h_mal.farmr.model.Order
|
import com.appttude.h_mal.farmr.model.Order
|
||||||
import com.appttude.h_mal.farmr.model.Sortable
|
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.createDialog
|
||||||
import com.appttude.h_mal.farmr.utils.displayToast
|
|
||||||
import com.appttude.h_mal.farmr.utils.navigateTo
|
import com.appttude.h_mal.farmr.utils.navigateTo
|
||||||
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
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) {
|
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
|
private lateinit var onBackPressed: OnBackPressedCallback
|
||||||
|
|
||||||
|
lateinit var navView: BottomNavigationView
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
@@ -47,7 +44,6 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main) {
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
setTitle("Shift List")
|
|
||||||
|
|
||||||
onBackPressed.isEnabled = true
|
onBackPressed.isEnabled = true
|
||||||
}
|
}
|
||||||
@@ -55,43 +51,39 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main) {
|
|||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
onBackPressed.isEnabled = false
|
onBackPressed.isEnabled = false
|
||||||
|
|
||||||
|
viewModel.saveBottomBarState(navView.selectedItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
emptyView = view.findViewById(R.id.empty_view)
|
navView = view.findViewById(R.id.bottom_bar)
|
||||||
productListView = view.findViewById(R.id.list_item_view)
|
val navHost = childFragmentManager.findFragmentById(R.id.sub_container) as NavHostFragment
|
||||||
|
|
||||||
mAdapter = ShiftListAdapter(this, emptyView, viewModel)
|
val navController = navHost.navController
|
||||||
productListView.adapter = mAdapter
|
navController.setGraph(R.navigation.home_navigation)
|
||||||
|
|
||||||
|
navView.setOnNavigationItemSelectedListener {
|
||||||
|
setTitle(it.title.toString())
|
||||||
|
true
|
||||||
|
}
|
||||||
|
navView.setupWithNavController(navController)
|
||||||
|
|
||||||
|
viewModel.getBottomBarState()?.let {
|
||||||
|
navView.selectedItemId = it
|
||||||
|
}
|
||||||
|
|
||||||
view.findViewById<FloatingActionButton>(R.id.fab1).setOnClickListener {
|
view.findViewById<FloatingActionButton>(R.id.fab1).setOnClickListener {
|
||||||
navigateTo(R.id.main_to_addItem)
|
navigateTo(R.id.main_to_addItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
|
||||||
super.onStart()
|
|
||||||
viewModel.refreshLiveData()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
inflater.inflate(R.menu.menu_main, menu)
|
inflater.inflate(R.menu.menu_main, menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(data: Any?) {
|
|
||||||
super.onSuccess(data)
|
|
||||||
if (data is List<*>) {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
mAdapter.submitList(data as List<ShiftObject>)
|
|
||||||
}
|
|
||||||
if (data is Success) {
|
|
||||||
displayToast(data.successMessage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.delete_all -> {
|
R.id.delete_all -> {
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ import android.view.ViewGroup
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import com.appttude.h_mal.farmr.R
|
import com.appttude.h_mal.farmr.R
|
||||||
import com.appttude.h_mal.farmr.base.BaseListAdapter
|
import com.appttude.h_mal.farmr.base.BaseListAdapter
|
||||||
|
import com.appttude.h_mal.farmr.base.ChildFragment
|
||||||
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.ShiftType
|
import com.appttude.h_mal.farmr.model.ShiftType
|
||||||
import com.appttude.h_mal.farmr.utils.formatToTwoDpString
|
import com.appttude.h_mal.farmr.utils.formatToTwoDpString
|
||||||
import com.appttude.h_mal.farmr.utils.navigateTo
|
|
||||||
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
||||||
|
|
||||||
|
|
||||||
@@ -23,8 +22,8 @@ const val PIECE_ITEM = 500
|
|||||||
const val HOURLY_ITEM = 501
|
const val HOURLY_ITEM = 501
|
||||||
|
|
||||||
class ShiftListAdapter(
|
class ShiftListAdapter(
|
||||||
private val fragment: Fragment,
|
private val fragment: ChildFragment<*>,
|
||||||
emptyView: View,
|
emptyView: View?,
|
||||||
private val viewModel: MainViewModel
|
private val viewModel: MainViewModel
|
||||||
) : BaseListAdapter<ShiftObject>(diffCallBack, R.layout.list_item_1, emptyView) {
|
) : BaseListAdapter<ShiftObject>(diffCallBack, R.layout.list_item_1, emptyView) {
|
||||||
|
|
||||||
@@ -62,7 +61,7 @@ class ShiftListAdapter(
|
|||||||
view.setOnClickListener {
|
view.setOnClickListener {
|
||||||
// Navigate to further info
|
// Navigate to further info
|
||||||
val nav = FragmentMainDirections.mainToFurtherInfo(data.id)
|
val nav = FragmentMainDirections.mainToFurtherInfo(data.id)
|
||||||
fragment.navigateTo(nav)
|
fragment.navigateParent(nav)
|
||||||
}
|
}
|
||||||
editView.setOnClickListener {
|
editView.setOnClickListener {
|
||||||
//creating a popup menu
|
//creating a popup menu
|
||||||
@@ -76,7 +75,7 @@ class ShiftListAdapter(
|
|||||||
R.id.update -> {
|
R.id.update -> {
|
||||||
// Navigate to edit
|
// Navigate to edit
|
||||||
val nav = FragmentMainDirections.mainToAddItem(data.id)
|
val nav = FragmentMainDirections.mainToAddItem(data.id)
|
||||||
fragment.navigateTo(nav)
|
fragment.navigateParent(nav)
|
||||||
return@setOnMenuItemClickListener true
|
return@setOnMenuItemClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,13 @@ fun String.convertDateString(format: String = DATE_FORMAT): Date? {
|
|||||||
return formatter.parse(this)
|
return formatter.parse(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun String.convertToCalendar(format: String = DATE_FORMAT): Calendar? {
|
||||||
|
val date = convertDateString(format)
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
calendar.time = date ?: return null
|
||||||
|
return calendar
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* turns "HH:mm" into an hour and minutes pair
|
* turns "HH:mm" into an hour and minutes pair
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ fun <CLASS : Any> Any.getGenericClassAt(position: Int): KClass<CLASS> =
|
|||||||
* var s: String?
|
* var s: String?
|
||||||
* i.validate{!i.isNullOrEmpty()} { print("string is empty") }
|
* i.validate{!i.isNullOrEmpty()} { print("string is empty") }
|
||||||
*/
|
*/
|
||||||
inline fun<T: Any?> T.validateField(validate: (T) -> Boolean, onError: () -> Unit) {
|
inline fun <T : Any?> T.validateField(validate: (T) -> Boolean, onError: () -> Unit) {
|
||||||
if (!validate.invoke(this)) {
|
if (!validate.invoke(this)) {
|
||||||
onError.invoke()
|
onError.invoke()
|
||||||
}
|
}
|
||||||
@@ -30,9 +30,25 @@ inline fun<T: Any?> T.validateField(validate: (T) -> Boolean, onError: () -> Uni
|
|||||||
* Returns a list of all elements sorted according to the specified comparator. In order of ascending or descending
|
* Returns a list of all elements sorted according to the specified comparator. In order of ascending or descending
|
||||||
* The sort is stable. It means that equal elements preserve their order relative to each other after sorting.
|
* The sort is stable. It means that equal elements preserve their order relative to each other after sorting.
|
||||||
*/
|
*/
|
||||||
inline fun <T, R : Comparable<R>> Iterable<T>.sortedByOrder(order: Order = Order.ASCENDING, crossinline selector: (T) -> R?): List<T> {
|
inline fun <T, R : Comparable<R>> Iterable<T>.sortedByOrder(
|
||||||
|
order: Order = Order.ASCENDING,
|
||||||
|
crossinline selector: (T) -> R?
|
||||||
|
): List<T> {
|
||||||
return when (order) {
|
return when (order) {
|
||||||
Order.ASCENDING -> sortedWith(compareBy(selector))
|
Order.ASCENDING -> sortedWith(compareBy(selector))
|
||||||
Order.DESCENDING -> sortedWith(compareByDescending(selector))
|
Order.DESCENDING -> sortedWith(compareByDescending(selector))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to retrieve a variable that may throw an exception
|
||||||
|
*
|
||||||
|
* @Returns variable if successful else null
|
||||||
|
*/
|
||||||
|
inline fun <T : Any?> tryGet(validate: () -> T?): T? {
|
||||||
|
return try {
|
||||||
|
validate.invoke()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.navigation.NavDirections
|
import androidx.navigation.NavDirections
|
||||||
import androidx.navigation.Navigation
|
import androidx.navigation.Navigation
|
||||||
import com.appttude.h_mal.farmr.R
|
import com.appttude.h_mal.farmr.R
|
||||||
|
import com.appttude.h_mal.farmr.base.ChildFragment
|
||||||
|
|
||||||
fun Fragment.navigateToFragment(newFragment: Fragment) {
|
fun Fragment.navigateToFragment(newFragment: Fragment) {
|
||||||
childFragmentManager.beginTransaction()
|
childFragmentManager.beginTransaction()
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package com.appttude.h_mal.farmr.viewmodel
|
package com.appttude.h_mal.farmr.viewmodel
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import com.applandeo.materialcalendarview.EventDay
|
||||||
|
import com.appttude.h_mal.farmr.R
|
||||||
import com.appttude.h_mal.farmr.data.Repository
|
import com.appttude.h_mal.farmr.data.Repository
|
||||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
|
||||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_BREAK
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_BREAK
|
||||||
@@ -21,6 +25,7 @@ import com.appttude.h_mal.farmr.model.ShiftType
|
|||||||
import com.appttude.h_mal.farmr.model.Sortable
|
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.convertDateString
|
import com.appttude.h_mal.farmr.utils.convertDateString
|
||||||
|
import com.appttude.h_mal.farmr.utils.convertToCalendar
|
||||||
import com.appttude.h_mal.farmr.utils.formatAsCurrencyString
|
import com.appttude.h_mal.farmr.utils.formatAsCurrencyString
|
||||||
import com.appttude.h_mal.farmr.utils.sortedByOrder
|
import com.appttude.h_mal.farmr.utils.sortedByOrder
|
||||||
import jxl.Workbook
|
import jxl.Workbook
|
||||||
@@ -30,6 +35,7 @@ import jxl.write.WritableWorkbook
|
|||||||
import jxl.write.WriteException
|
import jxl.write.WriteException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
||||||
@@ -43,6 +49,8 @@ class MainViewModel(
|
|||||||
private var mSort: Sortable = Sortable.ID
|
private var mSort: Sortable = Sortable.ID
|
||||||
private var mOrder: Order = Order.ASCENDING
|
private var mOrder: Order = Order.ASCENDING
|
||||||
|
|
||||||
|
private var selectedItemId: Int? = null
|
||||||
|
|
||||||
private val observer = Observer<List<ShiftObject>> {
|
private val observer = Observer<List<ShiftObject>> {
|
||||||
it?.let {
|
it?.let {
|
||||||
val result = it.applyFilters().sortList(mSort, mOrder)
|
val result = it.applyFilters().sortList(mSort, mOrder)
|
||||||
@@ -199,9 +207,8 @@ class MainViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun clearFilters() {
|
fun clearFilters() {
|
||||||
super.setFiltrationDetails(null, null, null, null)
|
val result = super.setFiltrationDetails(null, null, null, null)
|
||||||
onSuccess(Success("Filters have been cleared"))
|
if (result) refreshLiveData()
|
||||||
refreshLiveData()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createExcelSheet(file: File): File? {
|
fun createExcelSheet(file: File): File? {
|
||||||
@@ -283,4 +290,25 @@ class MainViewModel(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun retrieveEvents(): List<EventDay>? {
|
||||||
|
val shiftList = shiftLiveData.value ?: return null
|
||||||
|
return shiftList.applyFilters().mapNotNull {
|
||||||
|
it.date.convertToCalendar()
|
||||||
|
?.let { d -> EventDay(d, R.drawable.baseline_list_alt_24, Color.parseColor("#228B22")) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getShiftsOnTheDay(calendar: Calendar): List<ShiftObject>? {
|
||||||
|
val shiftList = shiftLiveData.value ?: return null
|
||||||
|
return shiftList.filter { it.date.convertToCalendar()?.compareTo(calendar) == 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveBottomBarState(selectedItemId: Int) {
|
||||||
|
this.selectedItemId = selectedItemId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBottomBarState(): Int? {
|
||||||
|
return selectedItemId
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -18,25 +18,13 @@ open class ShiftViewModel(
|
|||||||
*/
|
*/
|
||||||
fun getCurrentShift(id: Long) = repository.readSingleShiftFromDatabase(id)
|
fun getCurrentShift(id: Long) = repository.readSingleShiftFromDatabase(id)
|
||||||
|
|
||||||
/**
|
|
||||||
* Lambda function that will invoke onError(...) on failure
|
|
||||||
* but update live data when successful
|
|
||||||
*/
|
|
||||||
private inline fun doTry(operation: () -> Unit) {
|
|
||||||
try {
|
|
||||||
operation.invoke()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
onError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun setFiltrationDetails(
|
open fun setFiltrationDetails(
|
||||||
description: String?,
|
description: String?,
|
||||||
dateFrom: String?,
|
dateFrom: String?,
|
||||||
dateTo: String?,
|
dateTo: String?,
|
||||||
type: String?
|
type: String?
|
||||||
) {
|
): Boolean {
|
||||||
repository.setFilteringDetailsInPrefs(description, dateFrom, dateTo, type)
|
return repository.setFilteringDetailsInPrefs(description, dateFrom, dateTo, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getFiltrationDetails(): FilterStore {
|
open fun getFiltrationDetails(): FilterStore {
|
||||||
|
|||||||
5
app/src/main/res/drawable/baseline_calendar_month_24.xml
Normal file
5
app/src/main/res/drawable/baseline_calendar_month_24.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M19,4h-1V2h-2v2H8V2H6v2H5C3.89,4 3.01,4.9 3.01,6L3,20c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V6C21,4.9 20.1,4 19,4zM19,20H5V10h14V20zM9,14H7v-2h2V14zM13,14h-2v-2h2V14zM17,14h-2v-2h2V14zM9,18H7v-2h2V18zM13,18h-2v-2h2V18zM17,18h-2v-2h2V18z"/>
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/baseline_list_alt_24.xml
Normal file
5
app/src/main/res/drawable/baseline_list_alt_24.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:autoMirrored="true" android:height="24dp"
|
||||||
|
android:tint="#000000" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M19,5v14L5,19L5,5h14m1.1,-2L3.9,3c-0.5,0 -0.9,0.4 -0.9,0.9v16.2c0,0.4 0.4,0.9 0.9,0.9h16.2c0.4,0 0.9,-0.5 0.9,-0.9L21,3.9c0,-0.5 -0.5,-0.9 -0.9,-0.9zM11,7h6v2h-6L11,7zM11,11h6v2h-6v-2zM11,15h6v2h-6zM7,7h2v2L7,9zM7,11h2v2L7,13zM7,15h2v2L7,17z"/>
|
||||||
|
</vector>
|
||||||
38
app/src/main/res/layout/fragment_calendar.xml
Normal file
38
app/src/main/res/layout/fragment_calendar.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView 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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
tools:context=".ui.CalendarFragment">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin">
|
||||||
|
|
||||||
|
<com.applandeo.materialcalendarview.CalendarView
|
||||||
|
android:id="@+id/calendarView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:headerColor="@color/colorPrimary"
|
||||||
|
app:highlightedDaysLabelsColor="@color/colorPrimary"
|
||||||
|
app:selectionColor="@color/colorAccent"
|
||||||
|
app:type="classic" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/shifts_available_recycler"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_horizontal_margin"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
|
tools:itemCount="5"
|
||||||
|
tools:listitem="@layout/list_cell_hourly" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
24
app/src/main/res/layout/fragment_list.xml
Normal file
24
app/src/main/res/layout/fragment_list.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
tools:context="com.appttude.h_mal.farmr.ui.FragmentList">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/list_item_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/list_cell_hourly">
|
||||||
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:visibility="visible"
|
||||||
|
layout="@layout/empty_list_view"
|
||||||
|
android:id="@+id/empty_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
@@ -1,34 +1,39 @@
|
|||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
tools:context="com.appttude.h_mal.farmr.ui.FragmentMain">
|
tools:context="com.appttude.h_mal.farmr.ui.FragmentMain">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/list_item_view"
|
android:id="@+id/sub_container"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layout_constraintBottom_toTopOf="@id/bottom_bar"
|
||||||
tools:listitem="@layout/list_cell_hourly">
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:layout="@layout/fragment_calendar" />
|
||||||
|
|
||||||
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
android:id="@+id/bottom_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:menu="@menu/bottom_navigation_menu" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fab1"
|
android:id="@+id/fab1"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentRight="true"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
android:layout_alignParentBottom="true"
|
app:layout_constraintBottom_toTopOf="@id/bottom_bar"
|
||||||
android:layout_margin="@dimen/fab_margin"
|
android:layout_margin="@dimen/fab_margin"
|
||||||
|
android:contentDescription="@string/fab"
|
||||||
android:src="@drawable/add"
|
android:src="@drawable/add"
|
||||||
app:backgroundTint="@color/colorPrimary" />
|
app:backgroundTint="@color/colorPrimary" />
|
||||||
|
|
||||||
<include
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:visibility="visible"
|
|
||||||
layout="@layout/empty_list_view"
|
|
||||||
android:id="@+id/empty_view"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|||||||
14
app/src/main/res/menu/bottom_navigation_menu.xml
Normal file
14
app/src/main/res/menu/bottom_navigation_menu.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_list"
|
||||||
|
app:showAsAction="always|withText"
|
||||||
|
android:icon="@drawable/baseline_list_alt_24"
|
||||||
|
android:title="@string/text_label_1"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_calendar"
|
||||||
|
app:showAsAction="always|withText"
|
||||||
|
android:icon="@drawable/baseline_calendar_month_24"
|
||||||
|
android:title="@string/text_label_2"/>
|
||||||
|
</menu>
|
||||||
17
app/src/main/res/navigation/home_navigation.xml
Normal file
17
app/src/main/res/navigation/home_navigation.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?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/nav_list">
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/nav_list"
|
||||||
|
android:name="com.appttude.h_mal.farmr.ui.FragmentList"
|
||||||
|
android:label="fragment_list"
|
||||||
|
tools:layout="@layout/fragment_list" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/nav_calendar"
|
||||||
|
android:name="com.appttude.h_mal.farmr.ui.CalendarFragment"
|
||||||
|
android:label="fragment_calendar"
|
||||||
|
tools:layout="@layout/fragment_calendar" />
|
||||||
|
</navigation>
|
||||||
@@ -103,4 +103,7 @@
|
|||||||
<string name="pound_sign">£</string>
|
<string name="pound_sign">£</string>
|
||||||
<string name="delete_shift">Delete shift</string>
|
<string name="delete_shift">Delete shift</string>
|
||||||
<string name="update_shift">Update Shift</string>
|
<string name="update_shift">Update Shift</string>
|
||||||
|
<string name="fab">Floating action button</string>
|
||||||
|
<string name="text_label_1">Shifts</string>
|
||||||
|
<string name="text_label_2">Calendar</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user