mirror of
https://github.com/hmalik144/Farmr.git
synced 2026-01-31 02:41:49 +00:00
Merge branch 'modern_architecture' into testsuite_expansion
# Conflicts: # app/build.gradle
This commit is contained in:
@@ -70,6 +70,6 @@ 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"
|
||||
/ * SQLite to excel */
|
||||
implementation 'com.ajts.androidmads.SQLite2Excel:library:1.0.2'
|
||||
/ * 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()
|
||||
}
|
||||
@@ -13,11 +13,6 @@ open class BaseRecyclerAdapter<T: Any>(
|
||||
): RecyclerView.Adapter<ViewHolder>() {
|
||||
var list: List<T>? = null
|
||||
|
||||
fun updateData(newList: List<T>) {
|
||||
list = newList
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return if (list.isNullOrEmpty()) {
|
||||
val emptyViewHolder = parent.generateView(emptyViewId)
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -74,8 +74,8 @@ class FilterDataFragment : BaseFragment<MainViewModel>(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<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,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<MainViewModel>(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<MainViewModel>(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<FloatingActionButton>(R.id.fab1).setOnClickListener {
|
||||
navigateToFragment(FragmentAddItem(), name = "additem")
|
||||
@@ -58,8 +77,12 @@ 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>)
|
||||
}
|
||||
if (data is Success) {
|
||||
displayToast(data.successMessage)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
@@ -79,12 +102,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 +110,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 +122,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 +178,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 +226,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,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
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 90 KiB |
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z" />
|
||||
</vector>
|
||||
@@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
@@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z" />
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01,-0.25 1.97,-0.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0,-4.42,-3.58,-8,-8,-8zm0 14c-3.31 0,-6,-2.69,-6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4,-4,-4,-4v3z" />
|
||||
</vector>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout 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">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/recycler">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
</FrameLayout>
|
||||
@@ -23,4 +23,9 @@
|
||||
android:src="@drawable/add"
|
||||
app:backgroundTint="@color/colorPrimary" />
|
||||
|
||||
<include
|
||||
android:visibility="gone"
|
||||
layout="@layout/empty_list_view"
|
||||
android:id="@+id/empty_view"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<resources>
|
||||
<string name="app_name">Farmr</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
<string name="category_ach">Shifts</string>
|
||||
|
||||
<string name="add_item_title">Add Shift</string>
|
||||
<string name="edit_item_title">Edit Shift</string>
|
||||
<string name="delete_item">Delete Shift</string>
|
||||
<string name="insert_item_failed">failed to insert Shift</string>
|
||||
<string name="insert_item_successful">Shift successfully added</string>
|
||||
<string name="update_item_failed">Update Failed</string>
|
||||
@@ -25,11 +23,6 @@
|
||||
<!-- Example General settings -->
|
||||
<string name="pref_header_general">General</string>
|
||||
|
||||
<string name="pref_title_social_recommendations">Enable social recommendations</string>
|
||||
<string name="pref_description_social_recommendations">Recommendations for people to contact
|
||||
based on your message history
|
||||
</string>
|
||||
|
||||
<string name="pref_title_display_name">Display name</string>
|
||||
<string name="pref_default_display_name">John Smith</string>
|
||||
|
||||
|
||||
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