mirror of
https://github.com/hmalik144/Farmr.git
synced 2025-12-10 02:25:19 +00:00
- Shift list exportation completed
This commit is contained in:
@@ -34,8 +34,6 @@ dependencies {
|
||||
implementation 'androidx.activity:activity-ktx:1.4.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
|
||||
implementation 'com.ajts.androidmads.SQLite2Excel:library:1.0.2'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test:core-ktx:1.4.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||
@@ -48,4 +46,7 @@ dependencies {
|
||||
def kodein_version = "6.2.1"
|
||||
implementation "org.kodein.di:kodein-di-generic-jvm:$kodein_version"
|
||||
implementation "org.kodein.di:kodein-di-framework-android-x:$kodein_version"
|
||||
/ * jxl * /
|
||||
implementation 'net.sourceforge.jexcelapi:jxl:2.6.12'
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,16 @@
|
||||
android:name="com.appttude.h_mal.farmr.data.legacydb.ShiftProvider"
|
||||
android:authorities="com.appttude.h_mal.farmr"
|
||||
android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.appttude.h_mal.farmr.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_path" />
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -7,6 +7,7 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.createViewModelLazy
|
||||
import com.appttude.h_mal.farmr.model.ViewState
|
||||
import com.appttude.h_mal.farmr.utils.getGenericClassAt
|
||||
import com.appttude.h_mal.farmr.utils.popBackStack
|
||||
import com.appttude.h_mal.farmr.viewmodel.ApplicationViewModelFactory
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.android.x.kodein
|
||||
@@ -76,4 +77,6 @@ abstract class BaseFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int)
|
||||
fun setTitle(title: String) {
|
||||
(requireActivity() as BaseActivity<*>).setTitleInActionBar(title)
|
||||
}
|
||||
|
||||
fun popBackStack() = mActivity?.popBackStack()
|
||||
}
|
||||
@@ -39,7 +39,7 @@ data class Shift(
|
||||
timeOut,
|
||||
duration,
|
||||
breakTime,
|
||||
duration,
|
||||
0f,
|
||||
rateOfPay,
|
||||
(duration * rateOfPay).formatToTwoDp()
|
||||
)
|
||||
|
||||
@@ -4,10 +4,6 @@ enum class ShiftType(val type: String){
|
||||
HOURLY("Hourly"),
|
||||
PIECE("Piece Rate");
|
||||
|
||||
fun getEnumByType(type: String): ShiftType {
|
||||
return values().first { it.type == type }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getEnumByType(type: String): ShiftType {
|
||||
return values().first { it.type == type }
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.appttude.h_mal.farmr.model
|
||||
|
||||
data class Success(
|
||||
val successMessage: String
|
||||
)
|
||||
@@ -12,6 +12,7 @@ import androidx.core.widget.doAfterTextChanged
|
||||
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.setDatePicker
|
||||
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
||||
|
||||
@@ -32,7 +33,6 @@ class FilterDataFragment : BaseFragment<MainViewModel>(R.layout.fragment_filter_
|
||||
|
||||
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)
|
||||
@@ -89,4 +89,9 @@ class FilterDataFragment : BaseFragment<MainViewModel>(R.layout.fragment_filter_
|
||||
override fun onClick(p0: View?) {
|
||||
submitFiltrationDetails()
|
||||
}
|
||||
|
||||
override fun onSuccess(data: Any?) {
|
||||
super.onSuccess(data)
|
||||
if (data is Success) popBackStack()
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,10 @@ 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.model.ShiftType
|
||||
import com.appttude.h_mal.farmr.model.Success
|
||||
import com.appttude.h_mal.farmr.utils.ID
|
||||
import com.appttude.h_mal.farmr.utils.createDialog
|
||||
import com.appttude.h_mal.farmr.utils.displayToast
|
||||
import com.appttude.h_mal.farmr.utils.formatToTwoDpString
|
||||
import com.appttude.h_mal.farmr.utils.hide
|
||||
import com.appttude.h_mal.farmr.utils.popBackStack
|
||||
@@ -123,6 +125,13 @@ class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
|
||||
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
|
||||
@@ -132,16 +141,26 @@ class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
|
||||
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.formatToTwoDpString())
|
||||
mTotalPayTextView.text = totalPay.formatToTwoDpString()
|
||||
|
||||
calculateTotalPay()
|
||||
}
|
||||
|
||||
// Return title
|
||||
@@ -268,4 +287,11 @@ class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSuccess(data: Any?) {
|
||||
super.onSuccess(data)
|
||||
if (data is Success) {
|
||||
displayToast(data.successMessage)
|
||||
popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,13 @@ import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.appttude.h_mal.farmr.R
|
||||
import com.appttude.h_mal.farmr.base.BackPressedListener
|
||||
@@ -22,15 +24,17 @@ import com.appttude.h_mal.farmr.utils.createDialog
|
||||
import com.appttude.h_mal.farmr.utils.navigateToFragment
|
||||
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 {
|
||||
lateinit var activity: MainActivity
|
||||
private lateinit var productListView: RecyclerView
|
||||
private lateinit var mAdapter: ShiftListAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setTitle("Shift List")
|
||||
// Inflate the layout for this fragment
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
@@ -58,6 +62,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
|
||||
override fun onSuccess(data: Any?) {
|
||||
super.onSuccess(data)
|
||||
if (data is List<*>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
mAdapter.submitList(data as List<ShiftObject>)
|
||||
}
|
||||
}
|
||||
@@ -79,12 +84,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
|
||||
}
|
||||
|
||||
R.id.filter_data -> {
|
||||
// val fragmentTransaction: FragmentTransaction =
|
||||
// activity.fragmentManager!!.beginTransaction()
|
||||
// fragmentTransaction.replace(R.id.container, FilterDataFragment())
|
||||
// .addToBackStack("filterdata").commit()
|
||||
// Todo: filter shift
|
||||
|
||||
navigateToFragment(FilterDataFragment(), name = "filterdata")
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -92,8 +92,9 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
|
||||
sortData()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.clear_filter -> {
|
||||
// Todo: Apply filter to list
|
||||
viewModel.setFiltrationDetails(null, null, null, null)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -103,7 +104,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
|
||||
.setTitle("Export?")
|
||||
.setMessage("Exporting current filtered data. Continue?")
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> ExportData() }
|
||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> exportData() }
|
||||
.create().show()
|
||||
} else {
|
||||
Toast.makeText(context, "Storage permissions required", Toast.LENGTH_SHORT)
|
||||
@@ -159,157 +160,29 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
|
||||
)
|
||||
}
|
||||
|
||||
private fun ExportData() {
|
||||
// val permission =
|
||||
// ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
// if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||
// Toast.makeText(context, "Storage permissions not granted", Toast.LENGTH_SHORT).show()
|
||||
// return
|
||||
// }
|
||||
// shiftsDbhelper = ShiftsDbHelper(context)
|
||||
// val database = shiftsDbhelper!!.writableDatabase
|
||||
// val projection_export = arrayOf<String?>(
|
||||
// ShiftsEntry.COLUMN_SHIFT_DESCRIPTION,
|
||||
// ShiftsEntry.COLUMN_SHIFT_DATE,
|
||||
// ShiftsEntry.COLUMN_SHIFT_TIME_IN,
|
||||
// ShiftsEntry.COLUMN_SHIFT_TIME_OUT,
|
||||
// ShiftsEntry.COLUMN_SHIFT_BREAK,
|
||||
// ShiftsEntry.COLUMN_SHIFT_DURATION,
|
||||
// ShiftsEntry.COLUMN_SHIFT_TYPE,
|
||||
// ShiftsEntry.COLUMN_SHIFT_UNIT,
|
||||
// ShiftsEntry.COLUMN_SHIFT_PAYRATE,
|
||||
// ShiftsEntry.COLUMN_SHIFT_TOTALPAY
|
||||
// )
|
||||
// val cursor = activity.contentResolver.query(
|
||||
// ShiftsEntry.CONTENT_URI,
|
||||
// projection_export,
|
||||
// activity.selection,
|
||||
// activity.args,
|
||||
// activity.sortOrder
|
||||
// )
|
||||
// database.delete(ShiftsEntry.TABLE_NAME_EXPORT, null, null)
|
||||
// var totalDuration = 0.00f
|
||||
// var totalUnits = 0.00f
|
||||
// var totalPay = 0.00f
|
||||
// try {
|
||||
// while (cursor!!.moveToNext()) {
|
||||
// val descriptionColumnIndex =
|
||||
// cursor.getString(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION))
|
||||
// val dateColumnIndex =
|
||||
// cursor.getString(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_DATE))
|
||||
// val timeInColumnIndex =
|
||||
// cursor.getString(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_TIME_IN))
|
||||
// val timeOutColumnIndex =
|
||||
// cursor.getString(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_TIME_OUT))
|
||||
// val durationColumnIndex =
|
||||
// cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_DURATION))
|
||||
// val breakOutColumnIndex =
|
||||
// cursor.getInt(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_BREAK))
|
||||
// val typeColumnIndex =
|
||||
// cursor.getString(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_TYPE))
|
||||
// val unitColumnIndex =
|
||||
// cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_UNIT))
|
||||
// val payrateColumnIndex =
|
||||
// cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_PAYRATE))
|
||||
// val totalpayColumnIndex =
|
||||
// cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_TOTALPAY))
|
||||
// totalUnits = totalUnits + unitColumnIndex
|
||||
// totalDuration = totalDuration + durationColumnIndex
|
||||
// totalPay = totalPay + totalpayColumnIndex
|
||||
// val values = ContentValues()
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION, descriptionColumnIndex)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_DATE, dateColumnIndex)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_TIME_IN, timeInColumnIndex)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_TIME_OUT, timeOutColumnIndex)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_BREAK, breakOutColumnIndex)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_DURATION, durationColumnIndex)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_TYPE, typeColumnIndex)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_UNIT, unitColumnIndex)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_PAYRATE, payrateColumnIndex)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_TOTALPAY, totalpayColumnIndex)
|
||||
// database.insert(ShiftsEntry.TABLE_NAME_EXPORT, null, values)
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// Log.e("FragmentMain", "ExportData: ", e)
|
||||
// } finally {
|
||||
// val values = ContentValues()
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION, "")
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_DATE, "")
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_TIME_IN, "")
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_TIME_OUT, "")
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_BREAK, "Total duration:")
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_DURATION, totalDuration)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_TYPE, "Total units:")
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_UNIT, totalUnits)
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_PAYRATE, "Total pay:")
|
||||
// values.put(ShiftsEntry.COLUMN_SHIFT_TOTALPAY, totalPay)
|
||||
// database.insert(ShiftsEntry.TABLE_NAME_EXPORT, null, values)
|
||||
// cursor!!.close()
|
||||
// }
|
||||
// val savePath = Environment.getExternalStorageDirectory().toString() + "/ShifttrackerTemp"
|
||||
// val file = File(savePath)
|
||||
// if (!file.exists()) {
|
||||
// file.mkdirs()
|
||||
// }
|
||||
// val sqLiteToExcel = SQLiteToExcel(context, "shifts.db", savePath)
|
||||
// sqLiteToExcel.exportSingleTable(
|
||||
// "shiftsexport",
|
||||
// "shifthistory.xls",
|
||||
// object : ExportListener {
|
||||
// override fun onStart() {}
|
||||
// override fun onCompleted(filePath: String) {
|
||||
// Toast.makeText(context, filePath, Toast.LENGTH_SHORT).show()
|
||||
// val newPath = Uri.parse("file://$savePath/shifthistory.xls")
|
||||
// val builder = VmPolicy.Builder()
|
||||
// StrictMode.setVmPolicy(builder.build())
|
||||
// val emailintent = Intent(Intent.ACTION_SEND)
|
||||
// emailintent.type = "application/vnd.ms-excel"
|
||||
// emailintent.putExtra(Intent.EXTRA_SUBJECT, "historic shifts")
|
||||
// emailintent.putExtra(Intent.EXTRA_TEXT, "I'm email body.")
|
||||
// emailintent.putExtra(Intent.EXTRA_STREAM, newPath)
|
||||
// startActivity(Intent.createChooser(emailintent, "Send Email"))
|
||||
// }
|
||||
//
|
||||
// override fun onError(e: Exception) {
|
||||
// println("Error msg: $e")
|
||||
// Toast.makeText(context, "Failed to Export data", Toast.LENGTH_SHORT).show()
|
||||
// }
|
||||
// })
|
||||
}
|
||||
private fun exportData() {
|
||||
val permission =
|
||||
ActivityCompat.checkSelfPermission(requireActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(context, "Storage permissions not granted", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
fun buildInfoString(
|
||||
totalDuration: Float,
|
||||
countOfTypeH: Int,
|
||||
countOfTypeP: Int,
|
||||
totalUnits: Float,
|
||||
totalPay: Float,
|
||||
lines: Int
|
||||
): String {
|
||||
var textString: String
|
||||
textString = "$lines Shifts"
|
||||
if (countOfTypeH != 0 && countOfTypeP != 0) {
|
||||
textString = "$textString ($countOfTypeH Hourly/$countOfTypeP Piece Rate)"
|
||||
val fileName = "shifthistory.xls"
|
||||
val file = File(requireContext().externalCacheDir, fileName)
|
||||
|
||||
viewModel.createExcelSheet(file)?.let {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
val excelUri = FileProvider.getUriForFile(
|
||||
requireContext(),
|
||||
requireContext().applicationContext.packageName + ".provider",
|
||||
file
|
||||
)
|
||||
intent.setDataAndType(excelUri, "application/vnd.ms-excel")
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
startActivity(intent)
|
||||
}
|
||||
if (countOfTypeH != 0) {
|
||||
textString = """
|
||||
$textString
|
||||
Total Hours: ${String.format("%.2f", totalDuration)}
|
||||
""".trimIndent()
|
||||
}
|
||||
if (countOfTypeP != 0) {
|
||||
textString = """
|
||||
$textString
|
||||
Total Units: ${String.format("%.2f", totalUnits)}
|
||||
""".trimIndent()
|
||||
}
|
||||
if (totalPay != 0f) {
|
||||
textString = """
|
||||
$textString
|
||||
Total Pay: ${"$"}${String.format("%.2f", totalPay)}
|
||||
""".trimIndent()
|
||||
}
|
||||
return textString
|
||||
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
@@ -335,7 +208,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
|
||||
.setTitle("Export?")
|
||||
.setMessage("Exporting current filtered data. Continue?")
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> ExportData() }.create().show()
|
||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> exportData() }.create().show()
|
||||
}
|
||||
|
||||
fun checkStoragePermissions(activity: Activity?): Boolean {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.appttude.h_mal.farmr.utils
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
fun String.formatToTwoDp(): Float {
|
||||
@@ -32,6 +33,11 @@ fun Calendar.getTimeString(): String {
|
||||
return format.format(time)
|
||||
}
|
||||
|
||||
fun String.convertDateString(format: String = DATE_FORMAT): Date? {
|
||||
val formatter = SimpleDateFormat(format, Locale.getDefault())
|
||||
return formatter.parse(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* turns "HH:mm" into an hour and minutes pair
|
||||
*
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
package com.appttude.h_mal.farmr.viewmodel
|
||||
|
||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.appttude.h_mal.farmr.base.BaseViewModel
|
||||
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.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_BREAK
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_DATE
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_DESCRIPTION
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_DURATION
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_PAYRATE
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TIME_IN
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TIME_OUT
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TOTALPAY
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TYPE
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_UNIT
|
||||
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry._ID
|
||||
import com.appttude.h_mal.farmr.data.prefs.DESCRIPTION
|
||||
import com.appttude.h_mal.farmr.data.prefs.TIME_IN
|
||||
import com.appttude.h_mal.farmr.data.prefs.TIME_OUT
|
||||
@@ -15,14 +30,24 @@ import com.appttude.h_mal.farmr.model.Order
|
||||
import com.appttude.h_mal.farmr.model.Shift
|
||||
import com.appttude.h_mal.farmr.model.ShiftType
|
||||
import com.appttude.h_mal.farmr.model.Sortable
|
||||
import com.appttude.h_mal.farmr.model.Success
|
||||
import com.appttude.h_mal.farmr.utils.CURRENCY
|
||||
import com.appttude.h_mal.farmr.utils.calculateDuration
|
||||
import com.appttude.h_mal.farmr.utils.convertDateString
|
||||
import com.appttude.h_mal.farmr.utils.dateStringIsValid
|
||||
import com.appttude.h_mal.farmr.utils.formatToTwoDp
|
||||
import com.appttude.h_mal.farmr.utils.getTimeString
|
||||
import com.appttude.h_mal.farmr.utils.sortedByOrder
|
||||
import com.appttude.h_mal.farmr.utils.timeStringIsValid
|
||||
import jxl.Workbook
|
||||
import jxl.WorkbookSettings
|
||||
import jxl.write.Label
|
||||
import jxl.write.WritableWorkbook
|
||||
import jxl.write.WriteException
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
class MainViewModel(
|
||||
@@ -38,7 +63,8 @@ class MainViewModel(
|
||||
private var mFilterStore: FilterStore? = null
|
||||
|
||||
private val observer = Observer<List<ShiftObject>> {
|
||||
onSuccess(it.sortList(mSort, mOrder))
|
||||
val result = it.applyFilters().sortList(mSort, mOrder)
|
||||
onSuccess(result)
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -47,6 +73,47 @@ class MainViewModel(
|
||||
shiftLiveData.observeForever(observer)
|
||||
}
|
||||
|
||||
private fun List<ShiftObject>.applyFilters(): List<ShiftObject> {
|
||||
val filter = getFiltrationDetails()
|
||||
|
||||
return filter { s ->
|
||||
comparedStrings(filter.type, s.type) &&
|
||||
comparedStringsContains(filter.description, s.description) &&
|
||||
(isBetween(filter.dateFrom, filter.dateTo, s.date) ?: true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun comparedStrings(first: String?, second: String?): Boolean {
|
||||
return when (compareValues(first, second)) {
|
||||
-1, 0, 1 -> true
|
||||
else -> {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun comparedStringsContains(first: String?, second: String?): Boolean {
|
||||
first?.let {
|
||||
(second?.contains(it))?.let { c -> return c }
|
||||
}
|
||||
|
||||
return comparedStrings(first, second)
|
||||
}
|
||||
|
||||
private fun isBetween(fromDate: String?, toDate: String?, compareWith: String): Boolean? {
|
||||
val first = fromDate?.convertDateString()
|
||||
val second = toDate?.convertDateString()
|
||||
|
||||
if (first == null && second == null) return null
|
||||
val compareDate = compareWith.convertDateString() ?: return null
|
||||
|
||||
if (second == null) return compareDate.after(first)
|
||||
if (first == null) return compareDate.before(second)
|
||||
|
||||
return compareDate.after(first) && compareDate.before(second)
|
||||
}
|
||||
|
||||
|
||||
override fun onCleared() {
|
||||
shiftLiveData.removeObserver(observer)
|
||||
super.onCleared()
|
||||
@@ -121,7 +188,7 @@ class MainViewModel(
|
||||
onError("Date format is invalid")
|
||||
return
|
||||
}
|
||||
(rateOfPay.toFloat() >= 0.00).validateField {
|
||||
(rateOfPay >= 0.00).validateField {
|
||||
onError("Rate of pay is invalid")
|
||||
return
|
||||
}
|
||||
@@ -139,7 +206,7 @@ class MainViewModel(
|
||||
}
|
||||
|
||||
doTry {
|
||||
insertShiftIntoDatabase(
|
||||
val result = insertShiftIntoDatabase(
|
||||
ShiftType.HOURLY,
|
||||
description,
|
||||
date,
|
||||
@@ -149,6 +216,8 @@ class MainViewModel(
|
||||
breakMins,
|
||||
null
|
||||
)
|
||||
|
||||
if (result) onSuccess(Success("Shift successfully added"))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -178,7 +247,7 @@ class MainViewModel(
|
||||
}
|
||||
|
||||
doTry {
|
||||
insertShiftIntoDatabase(
|
||||
val result = insertShiftIntoDatabase(
|
||||
type = ShiftType.PIECE,
|
||||
description = description,
|
||||
date = date,
|
||||
@@ -188,6 +257,7 @@ class MainViewModel(
|
||||
null,
|
||||
units = units
|
||||
)
|
||||
if (result) onSuccess(Success("New shift successfully added"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +273,7 @@ class MainViewModel(
|
||||
units: Float? = null,
|
||||
) {
|
||||
description?.let {
|
||||
(it.length < 3).validateField {
|
||||
(it.length > 3).validateField {
|
||||
onError("Description length should be longer")
|
||||
return
|
||||
}
|
||||
@@ -213,7 +283,7 @@ class MainViewModel(
|
||||
return
|
||||
}
|
||||
rateOfPay?.let {
|
||||
(it.toFloat() >= 0.00).validateField {
|
||||
(it >= 0.00).validateField {
|
||||
onError("Rate of pay is invalid")
|
||||
return
|
||||
}
|
||||
@@ -232,13 +302,13 @@ class MainViewModel(
|
||||
onError("Time out format is in correct")
|
||||
return
|
||||
}
|
||||
breakMins?.let { it.toInt() > 0 }?.validateField {
|
||||
breakMins?.let { it >= 0 }?.validateField {
|
||||
onError("Break in minutes is invalid")
|
||||
return
|
||||
}
|
||||
|
||||
doTry {
|
||||
updateShiftInDatabase(
|
||||
val result = updateShiftInDatabase(
|
||||
id,
|
||||
type = type?.let { ShiftType.getEnumByType(it) },
|
||||
description = description,
|
||||
@@ -249,6 +319,8 @@ class MainViewModel(
|
||||
breakMins = breakMins,
|
||||
units = units
|
||||
)
|
||||
|
||||
if (result) onSuccess(Success("Shift successfully updated"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +350,7 @@ class MainViewModel(
|
||||
timeOut: String? = null,
|
||||
breakMins: Int? = null,
|
||||
units: Float? = null,
|
||||
) {
|
||||
): Boolean {
|
||||
val currentShift = repository.readSingleShiftFromDatabase(id)?.copyToShift()
|
||||
?: throw IOException("Cannot update shift as it does not exist")
|
||||
|
||||
@@ -352,7 +424,7 @@ class MainViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
repository.updateShiftIntoDatabase(id, shift)
|
||||
return repository.updateShiftIntoDatabase(id, shift)
|
||||
}
|
||||
|
||||
private fun insertShiftIntoDatabase(
|
||||
@@ -364,7 +436,7 @@ class MainViewModel(
|
||||
timeOut: String?,
|
||||
breakMins: Int?,
|
||||
units: Float?,
|
||||
) {
|
||||
): Boolean {
|
||||
val shift = when (type) {
|
||||
ShiftType.HOURLY -> {
|
||||
if (timeIn.isNullOrBlank() && timeOut.isNullOrBlank()) throw IOException("Time in and time out are null")
|
||||
@@ -392,7 +464,7 @@ class MainViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
repository.insertShiftIntoDatabase(shift)
|
||||
return repository.insertShiftIntoDatabase(shift)
|
||||
}
|
||||
|
||||
|
||||
@@ -404,30 +476,21 @@ class MainViewModel(
|
||||
totalPay: Float,
|
||||
lines: Int
|
||||
): String {
|
||||
var textString: String
|
||||
textString = "$lines Shifts"
|
||||
val stringBuilder = StringBuilder("$lines Shifts").append("\n")
|
||||
|
||||
if (countOfHourly != 0 && countOfPiece != 0) {
|
||||
textString = "$textString ($countOfHourly Hourly/$countOfPiece Piece Rate)"
|
||||
stringBuilder.append(" ($countOfHourly Hourly/$countOfPiece Piece Rate)").append("\n")
|
||||
}
|
||||
if (countOfHourly != 0) {
|
||||
textString = """
|
||||
$textString
|
||||
Total Hours: ${String.format("%.2f", totalDuration)}
|
||||
""".trimIndent()
|
||||
stringBuilder.append("Total Hours: ").append(totalDuration).append("\n")
|
||||
}
|
||||
if (countOfPiece != 0) {
|
||||
textString = """
|
||||
$textString
|
||||
Total Units: ${String.format("%.2f", totalUnits)}
|
||||
""".trimIndent()
|
||||
stringBuilder.append("Total Units: ").append(totalUnits).append("\n")
|
||||
}
|
||||
if (totalPay != 0f) {
|
||||
textString = """
|
||||
$textString
|
||||
Total Pay: ${"$"}${String.format("%.2f", totalPay)}
|
||||
""".trimIndent()
|
||||
stringBuilder.append("Total Pay: ").append(CURRENCY).append(totalPay).append("\n")
|
||||
}
|
||||
return textString
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
|
||||
fun refreshLiveData() {
|
||||
@@ -458,11 +521,12 @@ class MainViewModel(
|
||||
type: String?
|
||||
) {
|
||||
repository.setFilteringDetailsInPrefs(description, dateFrom, dateTo, type)
|
||||
onSuccess(Success("Filter(s) successfully applied"))
|
||||
}
|
||||
|
||||
fun getFiltrationDetails(): FilterStore {
|
||||
val prefs = repository.retrieveFilteringDetailsInPrefs()
|
||||
mFilterStore = FilterStore(
|
||||
mFilterStore = FilterStore(
|
||||
prefs[DESCRIPTION],
|
||||
prefs[TIME_IN],
|
||||
prefs[TIME_OUT],
|
||||
@@ -473,11 +537,89 @@ class MainViewModel(
|
||||
|
||||
fun retrieveDurationText(mTimeIn: String?, mTimeOut: String?, mBreaks: Int?): Float? {
|
||||
try {
|
||||
return calculateDuration(mTimeIn,mTimeOut,mBreaks)
|
||||
}catch (e: IOException) {
|
||||
return calculateDuration(mTimeIn, mTimeOut, mBreaks)
|
||||
} catch (e: IOException) {
|
||||
onError(e)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@RequiresPermission(WRITE_EXTERNAL_STORAGE)
|
||||
fun createExcelSheet(file: File): File? {
|
||||
val wbSettings = WorkbookSettings().apply {
|
||||
locale = Locale("en", "EN")
|
||||
}
|
||||
|
||||
try {
|
||||
val workbook: WritableWorkbook = Workbook.createWorkbook(file, wbSettings)
|
||||
val sheet = workbook.createSheet("Shifts", 0)
|
||||
// Write column headers
|
||||
val headers = listOf(
|
||||
Label(0, 0, _ID),
|
||||
Label(1, 0, COLUMN_SHIFT_TYPE),
|
||||
Label(2, 0, COLUMN_SHIFT_DESCRIPTION),
|
||||
Label(3, 0, COLUMN_SHIFT_DATE),
|
||||
Label(4, 0, COLUMN_SHIFT_TIME_IN),
|
||||
Label(5, 0, COLUMN_SHIFT_TIME_OUT),
|
||||
Label(6, 0, "$COLUMN_SHIFT_BREAK (in mins)"),
|
||||
Label(7, 0, COLUMN_SHIFT_DURATION),
|
||||
Label(8, 0, COLUMN_SHIFT_UNIT),
|
||||
Label(9, 0, COLUMN_SHIFT_PAYRATE),
|
||||
Label(10, 0, COLUMN_SHIFT_TOTALPAY)
|
||||
)
|
||||
// table content
|
||||
val data = shiftLiveData.value
|
||||
if (data.isNullOrEmpty()) {
|
||||
onError("No data to parse into excel file")
|
||||
return null
|
||||
}
|
||||
var currentRow = 0
|
||||
val cells = data.mapIndexed { index, shift ->
|
||||
currentRow += 1
|
||||
listOf(
|
||||
Label(0, currentRow, shift.id.toString()),
|
||||
Label(1, currentRow, shift.type),
|
||||
Label(2, currentRow, shift.description),
|
||||
Label(3, currentRow, shift.date),
|
||||
Label(4, currentRow, shift.timeIn),
|
||||
Label(5, currentRow, shift.timeOut),
|
||||
Label(6, currentRow, shift.breakMins.toString()),
|
||||
Label(7, currentRow, shift.duration.toString()),
|
||||
Label(8, currentRow, shift.units.toString()),
|
||||
Label(9, currentRow, shift.rateOfPay.toString()),
|
||||
Label(10, currentRow, shift.totalPay.toString())
|
||||
)
|
||||
}.flatten()
|
||||
|
||||
currentRow += 1
|
||||
val footer = listOf(
|
||||
Label(0, currentRow, "Total:"),
|
||||
Label(7, currentRow, data.sumOf { it.duration.toDouble() }.toString()),
|
||||
Label(8, currentRow, data.sumOf { it.units.toDouble() }.toString()),
|
||||
Label(10, currentRow, data.sumOf { it.totalPay.toDouble() }.toString())
|
||||
)
|
||||
val content = listOf(headers, cells, footer).flatten()
|
||||
|
||||
// Write content to sheet
|
||||
try {
|
||||
content.forEach { c -> sheet.addCell(c) }
|
||||
} catch (e: WriteException) {
|
||||
onError("Failed to write excel sheet")
|
||||
return null
|
||||
} catch (e: WriteException) {
|
||||
onError("Failed to write excel sheet")
|
||||
return null
|
||||
}
|
||||
|
||||
workbook.write()
|
||||
workbook.close()
|
||||
|
||||
return file
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
onError("Failed to generate excel sheet of shifts")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
4
app/src/main/res/xml/provider_path.xml
Normal file
4
app/src/main/res/xml/provider_path.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<external-path name="external_files" path="."/>
|
||||
</paths>
|
||||
Reference in New Issue
Block a user