diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index fdf8d99..b1077fb 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 8a92332..dafe122 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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'
+
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 12b8ee2..5b13a45 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -37,6 +37,16 @@
android:name="com.appttude.h_mal.farmr.data.legacydb.ShiftProvider"
android:authorities="com.appttude.h_mal.farmr"
android:exported="false" />
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/base/BaseFragment.kt b/app/src/main/java/com/appttude/h_mal/farmr/base/BaseFragment.kt
index affb4b0..a939028 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/base/BaseFragment.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/base/BaseFragment.kt
@@ -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(@LayoutRes contentLayoutId: Int)
fun setTitle(title: String) {
(requireActivity() as BaseActivity<*>).setTitleInActionBar(title)
}
+
+ fun popBackStack() = mActivity?.popBackStack()
}
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/base/BaseRecyclerAdapter.kt b/app/src/main/java/com/appttude/h_mal/farmr/base/BaseRecyclerAdapter.kt
index ab62a76..fd34388 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/base/BaseRecyclerAdapter.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/base/BaseRecyclerAdapter.kt
@@ -13,11 +13,6 @@ open class BaseRecyclerAdapter(
): RecyclerView.Adapter() {
var list: List? = null
- fun updateData(newList: List) {
- list = newList
- notifyDataSetChanged()
- }
-
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return if (list.isNullOrEmpty()) {
val emptyViewHolder = parent.generateView(emptyViewId)
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/model/Shift.kt b/app/src/main/java/com/appttude/h_mal/farmr/model/Shift.kt
index 3d8e5ab..2f87159 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/model/Shift.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/model/Shift.kt
@@ -39,7 +39,7 @@ data class Shift(
timeOut,
duration,
breakTime,
- duration,
+ 0f,
rateOfPay,
(duration * rateOfPay).formatToTwoDp()
)
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/model/ShiftType.kt b/app/src/main/java/com/appttude/h_mal/farmr/model/ShiftType.kt
index b724c19..4087686 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/model/ShiftType.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/model/ShiftType.kt
@@ -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 }
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/model/Sortable.kt b/app/src/main/java/com/appttude/h_mal/farmr/model/Sortable.kt
index 6a0c9a1..f027a92 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/model/Sortable.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/model/Sortable.kt
@@ -7,5 +7,9 @@ enum class Sortable(val label: String) {
DESCRIPTION("Description"),
DURATION("Added"), UNITS("Duration"),
RATEOFPAY("Rate of pay"),
- TOTALPAY("Total Pay")
+ TOTALPAY("Total Pay");
+
+ companion object {
+ val entries = Sortable.values()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/model/Success.kt b/app/src/main/java/com/appttude/h_mal/farmr/model/Success.kt
new file mode 100644
index 0000000..b7b3d9d
--- /dev/null
+++ b/app/src/main/java/com/appttude/h_mal/farmr/model/Success.kt
@@ -0,0 +1,5 @@
+package com.appttude.h_mal.farmr.model
+
+data class Success(
+ val successMessage: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/ui/FilterDataFragment.kt b/app/src/main/java/com/appttude/h_mal/farmr/ui/FilterDataFragment.kt
index ca917f3..745fb68 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/ui/FilterDataFragment.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/ui/FilterDataFragment.kt
@@ -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(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)
@@ -74,8 +74,8 @@ class FilterDataFragment : BaseFragment(R.layout.fragment_filter_
id: Long
) {
type = when (position) {
- 1 -> ShiftType.HOURLY.toString()
- 2 -> ShiftType.PIECE.toString()
+ 1 -> ShiftType.HOURLY.type
+ 2 -> ShiftType.PIECE.type
else -> return
}
}
@@ -89,4 +89,9 @@ class FilterDataFragment : BaseFragment(R.layout.fragment_filter_
override fun onClick(p0: View?) {
submitFiltrationDetails()
}
+
+ override fun onSuccess(data: Any?) {
+ super.onSuccess(data)
+ if (data is Success) popBackStack()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentAddItem.kt b/app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentAddItem.kt
index 1454eb8..eb74f10 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentAddItem.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentAddItem.kt
@@ -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(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(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(R.layout.fragment_add_item),
return true
}
+ override fun onSuccess(data: Any?) {
+ super.onSuccess(data)
+ if (data is Success) {
+ displayToast(data.successMessage)
+ popBackStack()
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentMain.kt b/app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentMain.kt
index b4f557b..f083778 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentMain.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentMain.kt
@@ -6,31 +6,41 @@ 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 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
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.viewmodel.MainViewModel
import com.google.android.material.floatingactionbutton.FloatingActionButton
+import java.io.File
import kotlin.system.exitProcess
+
class FragmentMain : BaseFragment(R.layout.fragment_main), BackPressedListener {
- lateinit var activity: MainActivity
private lateinit var productListView: RecyclerView
+ private lateinit var emptyView: View
private lateinit var mAdapter: ShiftListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ setTitle("Shift List")
// Inflate the layout for this fragment
setHasOptionsMenu(true)
}
@@ -43,6 +53,15 @@ class FragmentMain : BaseFragment(R.layout.fragment_main), BackPr
}
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(R.id.fab1).setOnClickListener {
navigateToFragment(FragmentAddItem(), name = "additem")
@@ -58,8 +77,12 @@ class FragmentMain : BaseFragment(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)
}
+ if (data is Success) {
+ displayToast(data.successMessage)
+ }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -79,12 +102,7 @@ class FragmentMain : BaseFragment(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 +110,9 @@ class FragmentMain : BaseFragment(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 +122,7 @@ class FragmentMain : BaseFragment(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 +178,29 @@ class FragmentMain : BaseFragment(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(
-// 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 +226,7 @@ class FragmentMain : BaseFragment(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 {
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/utils/Formatting.kt b/app/src/main/java/com/appttude/h_mal/farmr/utils/Formatting.kt
index 7be48db..00f9119 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/utils/Formatting.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/utils/Formatting.kt
@@ -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
*
diff --git a/app/src/main/java/com/appttude/h_mal/farmr/viewmodel/MainViewModel.kt b/app/src/main/java/com/appttude/h_mal/farmr/viewmodel/MainViewModel.kt
index 08d1185..bec425b 100644
--- a/app/src/main/java/com/appttude/h_mal/farmr/viewmodel/MainViewModel.kt
+++ b/app/src/main/java/com/appttude/h_mal/farmr/viewmodel/MainViewModel.kt
@@ -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> {
- 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.applyFilters(): List {
+ 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,13 @@ class MainViewModel(
type: String?
) {
repository.setFilteringDetailsInPrefs(description, dateFrom, dateTo, type)
+ onSuccess(Success("Filter(s) successfully applied"))
+ refreshLiveData()
}
fun getFiltrationDetails(): FilterStore {
val prefs = repository.retrieveFilteringDetailsInPrefs()
- mFilterStore = FilterStore(
+ mFilterStore = FilterStore(
prefs[DESCRIPTION],
prefs[TIME_IN],
prefs[TIME_OUT],
@@ -473,11 +538,90 @@ 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
+ if (shiftLiveData.value.isNullOrEmpty()) {
+ onError("No data to parse into excel file")
+ return null
+ }
+ val sortAndOrder = getSortAndOrder()
+ val data = shiftLiveData.value!!.applyFilters().sortList(sortAndOrder.first, sortAndOrder.second)
+ 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
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/farm.jpg b/app/src/main/res/drawable/farm.jpg
deleted file mode 100644
index 290da71..0000000
Binary files a/app/src/main/res/drawable/farm.jpg and /dev/null differ
diff --git a/app/src/main/res/drawable/ic_info_black_24dp.xml b/app/src/main/res/drawable/ic_info_black_24dp.xml
deleted file mode 100644
index 34b8202..0000000
--- a/app/src/main/res/drawable/ic_info_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index ca3826a..0000000
--- a/app/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_launcher_release_background.xml b/app/src/main/res/drawable/ic_launcher_release_background.xml
deleted file mode 100644
index ca3826a..0000000
--- a/app/src/main/res/drawable/ic_launcher_release_background.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml
deleted file mode 100644
index e3400cf..0000000
--- a/app/src/main/res/drawable/ic_notifications_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_sync_black_24dp.xml b/app/src/main/res/drawable/ic_sync_black_24dp.xml
deleted file mode 100644
index 2aef437..0000000
--- a/app/src/main/res/drawable/ic_sync_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/img_i_full.png b/app/src/main/res/drawable/img_i_full.png
deleted file mode 100644
index 97c195c..0000000
Binary files a/app/src/main/res/drawable/img_i_full.png and /dev/null differ
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
deleted file mode 100644
index dfa3dfc..0000000
--- a/app/src/main/res/layout/fragment_home.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
index 681d46f..e9fd9c1 100644
--- a/app/src/main/res/layout/fragment_main.xml
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -23,4 +23,9 @@
android:src="@drawable/add"
app:backgroundTint="@color/colorPrimary" />
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 077fa23..7120641 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,11 +1,9 @@
Farmr
- Settings
Shifts
Add Shift
Edit Shift
- Delete Shift
failed to insert Shift
Shift successfully added
Update Failed
@@ -25,11 +23,6 @@
General
- Enable social recommendations
- Recommendations for people to contact
- based on your message history
-
-
Display name
John Smith
diff --git a/app/src/main/res/xml/provider_path.xml b/app/src/main/res/xml/provider_path.xml
new file mode 100644
index 0000000..4495c28
--- /dev/null
+++ b/app/src/main/res/xml/provider_path.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index f56abf1..4e3983e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@
buildscript {
ext {
- kotlin_version = '1.9.0'
+ kotlin_version = '1.7.10'
}
repositories {
google()