mirror of
https://github.com/hmalik144/Farmr.git
synced 2026-03-18 07:25:55 +00:00
- MVVM refactor
- Kodein DI added - Overhaul dirty code in views
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'org.jetbrains.kotlin.android'
|
apply plugin: 'org.jetbrains.kotlin.android'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 31
|
||||||
@@ -29,8 +30,22 @@ dependencies {
|
|||||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||||
implementation 'com.google.android.material:material:1.0.0'
|
implementation 'com.google.android.material:material:1.0.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
implementation 'androidx.fragment:fragment-ktx:1.4.0'
|
||||||
|
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'
|
implementation 'com.ajts.androidmads.SQLite2Excel:library:1.0.2'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test:core-ktx:1.4.0'
|
androidTestImplementation 'androidx.test:core-ktx:1.4.0'
|
||||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||||
|
/ * Room database * /
|
||||||
|
def room_version = "2.4.3"
|
||||||
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
|
/ *Kodein Dependency Injection * /
|
||||||
|
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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,20 @@ package com.appttude.h_mal.farmr.data
|
|||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import androidx.test.rule.provider.ProviderTestRule
|
import androidx.test.rule.provider.ProviderTestRule
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.CONTENT_AUTHORITY
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.CONTENT_AUTHORITY
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_BREAK
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_BREAK
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_DATE
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_DATE
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_DESCRIPTION
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_DESCRIPTION
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_DURATION
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_DURATION
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_PAYRATE
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_PAYRATE
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TIME_IN
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TIME_IN
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TIME_OUT
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TIME_OUT
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TOTALPAY
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TOTALPAY
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TYPE
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_TYPE
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_UNIT
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_SHIFT_UNIT
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry.CONTENT_URI
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.CONTENT_URI
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry._ID
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry._ID
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftProvider
|
||||||
import junit.framework.TestCase.assertEquals
|
import junit.framework.TestCase.assertEquals
|
||||||
import junit.framework.TestCase.assertNull
|
import junit.framework.TestCase.assertNull
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
@@ -75,6 +76,8 @@ class ShiftProviderTest {
|
|||||||
// Assert
|
// Assert
|
||||||
val item = contentResolver.query(CONTENT_URI, projection, null, null, null)
|
val item = contentResolver.query(CONTENT_URI, projection, null, null, null)
|
||||||
item?.takeIf { it.moveToNext() }?.run {
|
item?.takeIf { it.moveToNext() }?.run {
|
||||||
|
val id = getLong(getColumnIndexOrThrow(_ID))
|
||||||
|
|
||||||
val descriptionColumnIndex = getString(getColumnIndexOrThrow(COLUMN_SHIFT_DESCRIPTION))
|
val descriptionColumnIndex = getString(getColumnIndexOrThrow(COLUMN_SHIFT_DESCRIPTION))
|
||||||
val dateColumnIndex = getString(getColumnIndexOrThrow(COLUMN_SHIFT_DATE))
|
val dateColumnIndex = getString(getColumnIndexOrThrow(COLUMN_SHIFT_DATE))
|
||||||
val timeInColumnIndex = getString(getColumnIndexOrThrow(COLUMN_SHIFT_TIME_IN))
|
val timeInColumnIndex = getString(getColumnIndexOrThrow(COLUMN_SHIFT_TIME_IN))
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".di.ShiftApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
|
|
||||||
<!-- Splash screen -->
|
<!-- Splash screen -->
|
||||||
<activity
|
<activity
|
||||||
android:name="com.appttude.h_mal.farmr.SplashScreen"
|
android:name="com.appttude.h_mal.farmr.ui.SplashScreen"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@android:style/Theme.Black.NoTitleBar"
|
android:theme="@android:style/Theme.Black.NoTitleBar"
|
||||||
tools:ignore="LockedOrientationActivity"
|
tools:ignore="LockedOrientationActivity"
|
||||||
@@ -27,13 +28,13 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="com.appttude.h_mal.farmr.MainActivity"
|
android:name="com.appttude.h_mal.farmr.ui.MainActivity"
|
||||||
android:parentActivityName="com.appttude.h_mal.farmr.SplashScreen"
|
android:parentActivityName="com.appttude.h_mal.farmr.ui.SplashScreen"
|
||||||
android:theme="@style/AppTheme.NoActionBar"
|
android:theme="@style/AppTheme.NoActionBar"
|
||||||
android:exported="true"/>
|
android:exported="true"/>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="com.appttude.h_mal.farmr.data.ShiftProvider"
|
android:name="com.appttude.h_mal.farmr.data.legacydb.ShiftProvider"
|
||||||
android:authorities="com.appttude.h_mal.farmr"
|
android:authorities="com.appttude.h_mal.farmr"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
package com.appttude.h_mal.farmr
|
|
||||||
|
|
||||||
import android.app.DatePickerDialog
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.ArrayAdapter
|
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.Spinner
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry
|
|
||||||
import java.text.MessageFormat
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Calendar
|
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
class FilterDataFragment : Fragment() {
|
|
||||||
private val spinnerList: Array<String> = arrayOf("", "Hourly", "Piece Rate")
|
|
||||||
private val listArgs: MutableList<String> = ArrayList()
|
|
||||||
private var LocationET: EditText? = null
|
|
||||||
private var dateFromET: EditText? = null
|
|
||||||
private var dateToET: EditText? = null
|
|
||||||
private var typeSpinner: Spinner? = null
|
|
||||||
lateinit var mcurrentDate: Calendar
|
|
||||||
private lateinit var activity: MainActivity
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?): View {
|
|
||||||
val activity = (activity)
|
|
||||||
|
|
||||||
// Inflate the layout for this fragment
|
|
||||||
val rootView: View = inflater.inflate(R.layout.fragment_filter_data, container, false)
|
|
||||||
activity.setActionBarTitle(getString(R.string.title_activity_filter_data))
|
|
||||||
mcurrentDate = Calendar.getInstance()
|
|
||||||
LocationET = rootView.findViewById<View>(R.id.filterLocationEditText) as EditText?
|
|
||||||
dateFromET = rootView.findViewById<View>(R.id.fromdateInEditText) as EditText?
|
|
||||||
dateToET = rootView.findViewById<View>(R.id.filterDateOutEditText) as EditText?
|
|
||||||
typeSpinner = rootView.findViewById<View>(R.id.TypeFilterEditText) as Spinner?
|
|
||||||
|
|
||||||
|
|
||||||
if (activity.selection != null && activity.selection!!.contains(" AND " + ShiftsEntry.COLUMN_SHIFT_DESCRIPTION + " LIKE ?")) {
|
|
||||||
var str: String = activity.args!!.get(2)
|
|
||||||
str = str.replace("%", "")
|
|
||||||
LocationET!!.setText(str)
|
|
||||||
}
|
|
||||||
if (activity.selection != null && !(activity.args!!.get(0) == "2000-01-01")) {
|
|
||||||
dateFromET!!.setText(activity.args!!.get(0))
|
|
||||||
}
|
|
||||||
if ((activity.selection != null) && (activity.args != null) && !(activity.args!![1] == (mcurrentDate.get(Calendar.YEAR).toString() + "-"
|
|
||||||
+ String.format("%02d", (mcurrentDate.get(Calendar.MONTH) + 1)) + "-"
|
|
||||||
+ String.format("%02d", mcurrentDate.get(Calendar.DAY_OF_MONTH))).toString())) {
|
|
||||||
dateToET!!.setText(activity.args!![1])
|
|
||||||
}
|
|
||||||
dateFromET!!.setOnClickListener { //To show current date in the datepicker
|
|
||||||
var mYear: Int = mcurrentDate.get(Calendar.YEAR)
|
|
||||||
var mMonth: Int = mcurrentDate.get(Calendar.MONTH)
|
|
||||||
var mDay: Int = mcurrentDate.get(Calendar.DAY_OF_MONTH)
|
|
||||||
if (!(dateFromET!!.text.toString() == "")) {
|
|
||||||
val dateString: String = dateFromET!!.text.toString().trim()
|
|
||||||
mYear = dateString.substring(0, 4).toInt()
|
|
||||||
mMonth = dateString.substring(5, 7).toInt()
|
|
||||||
if (mMonth == 1) {
|
|
||||||
mMonth = 0
|
|
||||||
} else {
|
|
||||||
mMonth = mMonth - 1
|
|
||||||
}
|
|
||||||
mDay = dateString.substring(8).toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
val mDatePicker = DatePickerDialog((context)!!, { datepicker, selectedyear, selectedmonth, selectedday ->
|
|
||||||
val input = MessageFormat.format("{0}-{1}-{2}", selectedyear, String.format("%02d", (selectedmonth + 1)), String.format("%02d", selectedday))
|
|
||||||
dateFromET!!.setText(input)
|
|
||||||
}, mYear, mMonth, mDay)
|
|
||||||
mDatePicker.setTitle("Select date")
|
|
||||||
mDatePicker.show()
|
|
||||||
}
|
|
||||||
dateToET!!.setOnClickListener { //To show current date in the datepicker
|
|
||||||
val mcurrentDate: Calendar = Calendar.getInstance()
|
|
||||||
var mYear: Int = mcurrentDate.get(Calendar.YEAR)
|
|
||||||
var mMonth: Int = mcurrentDate.get(Calendar.MONTH)
|
|
||||||
var mDay: Int = mcurrentDate.get(Calendar.DAY_OF_MONTH)
|
|
||||||
if (!(dateToET!!.text.toString() == "")) {
|
|
||||||
val dateString: String = dateToET!!.text.toString().trim({ it <= ' ' })
|
|
||||||
mYear = dateString.substring(0, 4).toInt()
|
|
||||||
mMonth = dateString.substring(5, 7).toInt()
|
|
||||||
if (mMonth == 1) {
|
|
||||||
mMonth = 0
|
|
||||||
} else {
|
|
||||||
mMonth -= 1
|
|
||||||
}
|
|
||||||
mDay = dateString.substring(8).toInt()
|
|
||||||
}
|
|
||||||
val mDatePicker = DatePickerDialog((context)!!, { datepicker, selectedyear, selectedmonth, selectedday ->
|
|
||||||
val input = MessageFormat.format("{0}-{1}-{2}", selectedyear, String.format("%02d", (selectedmonth + 1)), String.format("%02d", selectedday))
|
|
||||||
dateToET!!.setText(input)
|
|
||||||
}, mYear, mMonth, mDay)
|
|
||||||
mDatePicker.setTitle("Select date")
|
|
||||||
mDatePicker.show()
|
|
||||||
}
|
|
||||||
val adapter: ArrayAdapter<String> = ArrayAdapter((context)!!, android.R.layout.simple_spinner_dropdown_item, spinnerList)
|
|
||||||
typeSpinner!!.adapter = adapter
|
|
||||||
if (activity.selection != null && activity.selection!!.contains(" AND " + ShiftsEntry.COLUMN_SHIFT_TYPE + " IS ?")) {
|
|
||||||
val spinnerPosition: Int = adapter.getPosition(activity.args!!.get(activity.args!!.size - 1))
|
|
||||||
typeSpinner!!.setSelection(spinnerPosition)
|
|
||||||
}
|
|
||||||
val submit: Button = rootView.findViewById<View>(R.id.submitFiltered) as Button
|
|
||||||
submit.setOnClickListener {
|
|
||||||
BuildQuery()
|
|
||||||
activity.args = listArgs.toTypedArray<String>()
|
|
||||||
FragmentMain.NEW_LOADER = 1
|
|
||||||
activity.fragmentManager!!.popBackStack()
|
|
||||||
}
|
|
||||||
return rootView
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun BuildQuery() {
|
|
||||||
val dateQuery: String = ShiftsEntry.COLUMN_SHIFT_DATE + " BETWEEN ? AND ?"
|
|
||||||
val descQuery: String = " AND " + ShiftsEntry.COLUMN_SHIFT_DESCRIPTION + " LIKE ?"
|
|
||||||
val typeQuery: String = " AND " + ShiftsEntry.COLUMN_SHIFT_TYPE + " IS ?"
|
|
||||||
var dateFrom = "2000-01-01"
|
|
||||||
val c: Date = Calendar.getInstance().time
|
|
||||||
val df = SimpleDateFormat("yyyy-MM-dd")
|
|
||||||
var dateTo: String = df.format(c)
|
|
||||||
if (!TextUtils.isEmpty(dateFromET!!.text.toString().trim())) {
|
|
||||||
dateFrom = dateFromET!!.text.toString().trim()
|
|
||||||
}
|
|
||||||
if (!TextUtils.isEmpty(dateToET!!.text.toString().trim())) {
|
|
||||||
dateTo = dateToET!!.text.toString().trim()
|
|
||||||
}
|
|
||||||
activity.selection = dateQuery
|
|
||||||
listArgs.add(dateFrom)
|
|
||||||
listArgs.add(dateTo)
|
|
||||||
if (!TextUtils.isEmpty(LocationET!!.text.toString().trim())) {
|
|
||||||
activity.selection = activity.selection + descQuery
|
|
||||||
listArgs.add("%" + LocationET!!.text.toString().trim() + "%")
|
|
||||||
}
|
|
||||||
if (!(typeSpinner!!.selectedItem.toString() == "")) {
|
|
||||||
activity.selection = activity.selection + typeQuery
|
|
||||||
listArgs.add(typeSpinner!!.selectedItem.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,507 +0,0 @@
|
|||||||
package com.appttude.h_mal.farmr
|
|
||||||
|
|
||||||
import android.app.DatePickerDialog
|
|
||||||
import android.app.DatePickerDialog.OnDateSetListener
|
|
||||||
import android.app.TimePickerDialog
|
|
||||||
import android.app.TimePickerDialog.OnTimeSetListener
|
|
||||||
import android.content.ContentValues
|
|
||||||
import android.database.Cursor
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.Editable
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.text.TextWatcher
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.DatePicker
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.ProgressBar
|
|
||||||
import android.widget.RadioButton
|
|
||||||
import android.widget.RadioGroup
|
|
||||||
import android.widget.ScrollView
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.TimePicker
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.loader.app.LoaderManager
|
|
||||||
import androidx.loader.content.CursorLoader
|
|
||||||
import androidx.loader.content.Loader
|
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry
|
|
||||||
import java.util.Calendar
|
|
||||||
|
|
||||||
class FragmentAddItem : Fragment(), LoaderManager.LoaderCallbacks<Cursor?> {
|
|
||||||
lateinit var activity: MainActivity
|
|
||||||
|
|
||||||
private var mCurrentProductUri: Uri? = null
|
|
||||||
private var mRadioButtonOne: RadioButton? = null
|
|
||||||
private var mRadioButtonTwo: RadioButton? = null
|
|
||||||
private var mLocationEditText: EditText? = null
|
|
||||||
private var mDateEditText: EditText? = null
|
|
||||||
private var mDurationTextView: TextView? = null
|
|
||||||
private var mTimeInEditText: EditText? = null
|
|
||||||
private var mTimeOutEditText: EditText? = null
|
|
||||||
private var mBreakEditText: EditText? = null
|
|
||||||
private var mUnitEditText: EditText? = null
|
|
||||||
private var mPayRateEditText: EditText? = null
|
|
||||||
private var mTotalPayTextView: TextView? = null
|
|
||||||
private var hourlyDataView: LinearLayout? = null
|
|
||||||
private var unitsHolder: LinearLayout? = null
|
|
||||||
private var durationHolder: LinearLayout? = null
|
|
||||||
private var wholeView: LinearLayout? = null
|
|
||||||
private var scrollView: ScrollView? = null
|
|
||||||
private var progressBarAI: ProgressBar? = null
|
|
||||||
private var mBreaks = 0
|
|
||||||
private var mDay = 0
|
|
||||||
private var mMonth = 0
|
|
||||||
private var mYear = 0
|
|
||||||
private var mHoursIn = 0
|
|
||||||
private var mMinutesIn = 0
|
|
||||||
private var mHoursOut = 0
|
|
||||||
private var mMinutesOut = 0
|
|
||||||
private var mUnits = 0f
|
|
||||||
private var mPayRate = 0f
|
|
||||||
private var mType: String? = null
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?): View? {
|
|
||||||
// Inflate the layout for this fragment
|
|
||||||
val rootView = inflater.inflate(R.layout.fragment_add_item, container, false)
|
|
||||||
setHasOptionsMenu(true)
|
|
||||||
activity = (requireActivity() as MainActivity)
|
|
||||||
|
|
||||||
progressBarAI = rootView.findViewById<View>(R.id.pd_ai) as ProgressBar
|
|
||||||
scrollView = rootView.findViewById<View>(R.id.total_view) as ScrollView
|
|
||||||
mRadioGroup = rootView.findViewById<View>(R.id.rg) as RadioGroup
|
|
||||||
mRadioButtonOne = rootView.findViewById<View>(R.id.hourly) as RadioButton
|
|
||||||
mRadioButtonTwo = rootView.findViewById<View>(R.id.piecerate) as RadioButton
|
|
||||||
mLocationEditText = rootView.findViewById<View>(R.id.locationEditText) as EditText
|
|
||||||
mDateEditText = rootView.findViewById<View>(R.id.dateEditText) as EditText
|
|
||||||
mTimeInEditText = rootView.findViewById<View>(R.id.timeInEditText) as EditText
|
|
||||||
mBreakEditText = rootView.findViewById<View>(R.id.breakEditText) as EditText
|
|
||||||
mTimeOutEditText = rootView.findViewById<View>(R.id.timeOutEditText) as EditText
|
|
||||||
mDurationTextView = rootView.findViewById<View>(R.id.ShiftDuration) as TextView
|
|
||||||
mUnitEditText = rootView.findViewById<View>(R.id.unitET) as EditText
|
|
||||||
mPayRateEditText = rootView.findViewById<View>(R.id.payrateET) as EditText
|
|
||||||
mTotalPayTextView = rootView.findViewById<View>(R.id.totalpayval) as TextView
|
|
||||||
hourlyDataView = rootView.findViewById<View>(R.id.hourly_data_holder) as LinearLayout
|
|
||||||
unitsHolder = rootView.findViewById<View>(R.id.units_holder) as LinearLayout
|
|
||||||
durationHolder = rootView.findViewById<View>(R.id.duration_holder) as LinearLayout
|
|
||||||
wholeView = rootView.findViewById<View>(R.id.whole_view) as LinearLayout
|
|
||||||
mPayRate = 0.0f
|
|
||||||
mUnits = 0.0f
|
|
||||||
val b = arguments
|
|
||||||
if (b != null) {
|
|
||||||
mCurrentProductUri = Uri.parse(b.getString("uri"))
|
|
||||||
}
|
|
||||||
if (mCurrentProductUri == null) {
|
|
||||||
activity.setActionBarTitle(getString(R.string.add_item_title))
|
|
||||||
wholeView!!.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
activity.setActionBarTitle(getString(R.string.edit_item_title))
|
|
||||||
loaderManager.initLoader(EXISTING_PRODUCT_LOADER, null, this)
|
|
||||||
}
|
|
||||||
mBreakEditText!!.hint = "insert break in minutes"
|
|
||||||
mRadioGroup!!.setOnCheckedChangeListener(RadioGroup.OnCheckedChangeListener { radioGroup, i ->
|
|
||||||
if (mRadioButtonOne!!.isChecked) {
|
|
||||||
mType = mRadioButtonOne!!.text.toString()
|
|
||||||
wholeView!!.visibility = View.VISIBLE
|
|
||||||
unitsHolder!!.visibility = View.GONE
|
|
||||||
hourlyDataView!!.visibility = View.VISIBLE
|
|
||||||
durationHolder!!.visibility = View.VISIBLE
|
|
||||||
} else if (mRadioButtonTwo!!.isChecked) {
|
|
||||||
mType = mRadioButtonTwo!!.text.toString()
|
|
||||||
wholeView!!.visibility = View.VISIBLE
|
|
||||||
unitsHolder!!.visibility = View.VISIBLE
|
|
||||||
hourlyDataView!!.visibility = View.GONE
|
|
||||||
durationHolder!!.visibility = View.GONE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mDateEditText!!.setOnClickListener(object : View.OnClickListener {
|
|
||||||
override fun onClick(v: View) {
|
|
||||||
//To show current date in the datepicker
|
|
||||||
if (TextUtils.isEmpty(mDateEditText!!.text.toString().trim { it <= ' ' })) {
|
|
||||||
val mcurrentDate = Calendar.getInstance()
|
|
||||||
mYear = mcurrentDate[Calendar.YEAR]
|
|
||||||
mMonth = mcurrentDate[Calendar.MONTH]
|
|
||||||
mDay = mcurrentDate[Calendar.DAY_OF_MONTH]
|
|
||||||
} else {
|
|
||||||
val dateString = mDateEditText!!.text.toString().trim { it <= ' ' }
|
|
||||||
mYear = dateString.substring(0, 4).toInt()
|
|
||||||
mMonth = dateString.substring(5, 7).toInt()
|
|
||||||
if (mMonth == 1) {
|
|
||||||
mMonth = 0
|
|
||||||
} else {
|
|
||||||
mMonth = mMonth - 1
|
|
||||||
}
|
|
||||||
mDay = dateString.substring(8).toInt()
|
|
||||||
}
|
|
||||||
val mDatePicker = DatePickerDialog((context)!!, object : OnDateSetListener {
|
|
||||||
override fun onDateSet(datepicker: DatePicker, selectedyear: Int, selectedmonth: Int, selectedday: Int) {
|
|
||||||
var selectedmonth = selectedmonth
|
|
||||||
mDateEditText!!.setText(
|
|
||||||
(selectedyear.toString() + "-"
|
|
||||||
+ String.format("%02d", (selectedmonth + 1.also { selectedmonth = it })) + "-"
|
|
||||||
+ String.format("%02d", selectedday))
|
|
||||||
)
|
|
||||||
setDate(selectedyear, selectedmonth, selectedday)
|
|
||||||
}
|
|
||||||
}, mYear, mMonth, mDay)
|
|
||||||
mDatePicker.setTitle("Select date")
|
|
||||||
mDatePicker.show()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mTimeInEditText!!.setOnClickListener(object : View.OnClickListener {
|
|
||||||
override fun onClick(v: View) {
|
|
||||||
if ((mTimeInEditText!!.text.toString() == "")) {
|
|
||||||
val mcurrentTime = Calendar.getInstance()
|
|
||||||
mHoursIn = mcurrentTime[Calendar.HOUR_OF_DAY]
|
|
||||||
mMinutesIn = mcurrentTime[Calendar.MINUTE]
|
|
||||||
} else {
|
|
||||||
mHoursIn = (mTimeInEditText!!.text.toString().subSequence(0, 2)).toString().toInt()
|
|
||||||
mMinutesIn = (mTimeInEditText!!.text.toString().subSequence(3, 5)).toString().toInt()
|
|
||||||
}
|
|
||||||
val mTimePicker: TimePickerDialog
|
|
||||||
mTimePicker = TimePickerDialog(context, object : OnTimeSetListener {
|
|
||||||
override fun onTimeSet(timePicker: TimePicker, selectedHour: Int, selectedMinute: Int) {
|
|
||||||
val ddTime = String.format("%02d", selectedHour) + ":" + String.format("%02d", selectedMinute)
|
|
||||||
setTime(selectedMinute, selectedHour)
|
|
||||||
mTimeInEditText!!.setText(ddTime)
|
|
||||||
}
|
|
||||||
}, mHoursIn, mMinutesIn, true) //Yes 24 hour time
|
|
||||||
mTimePicker.setTitle("Select Time")
|
|
||||||
mTimePicker.show()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mTimeOutEditText!!.setOnClickListener(object : View.OnClickListener {
|
|
||||||
override fun onClick(v: View) {
|
|
||||||
if ((mTimeOutEditText!!.text.toString() == "")) {
|
|
||||||
val mcurrentTime = Calendar.getInstance()
|
|
||||||
mHoursOut = mcurrentTime[Calendar.HOUR_OF_DAY]
|
|
||||||
mMinutesOut = mcurrentTime[Calendar.MINUTE]
|
|
||||||
} else {
|
|
||||||
mHoursOut = (mTimeOutEditText!!.text.toString().subSequence(0, 2)).toString().toInt()
|
|
||||||
mMinutesOut = (mTimeOutEditText!!.text.toString().subSequence(3, 5)).toString().toInt()
|
|
||||||
}
|
|
||||||
val mTimePicker: TimePickerDialog
|
|
||||||
mTimePicker = TimePickerDialog(context, object : OnTimeSetListener {
|
|
||||||
override fun onTimeSet(timePicker: TimePicker, selectedHour: Int, selectedMinute: Int) {
|
|
||||||
val ddTime = String.format("%02d", selectedHour) + ":" + String.format("%02d", selectedMinute)
|
|
||||||
setTime2(selectedMinute, selectedHour)
|
|
||||||
mTimeOutEditText!!.setText(ddTime)
|
|
||||||
}
|
|
||||||
}, mHoursOut, mMinutesOut, true) //Yes 24 hour time
|
|
||||||
mTimePicker.setTitle("Select Time")
|
|
||||||
mTimePicker.show()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mTimeInEditText!!.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
|
||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, aft: Int) {}
|
|
||||||
override fun afterTextChanged(s: Editable) {
|
|
||||||
setDuration()
|
|
||||||
calculateTotalPay()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mTimeOutEditText!!.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
|
||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, aft: Int) {}
|
|
||||||
override fun afterTextChanged(s: Editable) {
|
|
||||||
setDuration()
|
|
||||||
calculateTotalPay()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mBreakEditText!!.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
|
||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, aft: Int) {}
|
|
||||||
override fun afterTextChanged(s: Editable) {
|
|
||||||
setDuration()
|
|
||||||
calculateTotalPay()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mUnitEditText!!.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
|
||||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
|
||||||
override fun afterTextChanged(editable: Editable) {
|
|
||||||
// if(mRadioButtonTwo.isChecked()) {
|
|
||||||
// mUnits = 0.0f;
|
|
||||||
// if (!mUnitEditText.getText().toString().equals("")){
|
|
||||||
// mUnits = Float.parseFloat(mUnitEditText.getText().toString());
|
|
||||||
// }
|
|
||||||
// mPayRate = 0.0f;
|
|
||||||
// if (!mPayRateEditText.getText().toString().equals("")){
|
|
||||||
// mPayRate = Float.parseFloat(mPayRateEditText.getText().toString());
|
|
||||||
// }
|
|
||||||
// Float total = mPayRate * mUnits;
|
|
||||||
// mTotalPayTextView.setText(String.valueOf(total));
|
|
||||||
// }
|
|
||||||
calculateTotalPay()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mPayRateEditText!!.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
|
||||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
|
||||||
override fun afterTextChanged(editable: Editable) {
|
|
||||||
calculateTotalPay()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
val SubmitProduct = rootView.findViewById<View>(R.id.submit) as Button
|
|
||||||
SubmitProduct.setOnClickListener(object : View.OnClickListener {
|
|
||||||
override fun onClick(view: View) {
|
|
||||||
saveProduct()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return rootView
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun calculateTotalPay() {
|
|
||||||
var total = 0.0f
|
|
||||||
mPayRate = 0.0f
|
|
||||||
if (mRadioButtonTwo!!.isChecked) {
|
|
||||||
mUnits = 0.0f
|
|
||||||
if (mUnitEditText!!.text.toString() != "") {
|
|
||||||
mUnits = mUnitEditText!!.text.toString().toFloat()
|
|
||||||
}
|
|
||||||
if (mPayRateEditText!!.text.toString() != "") {
|
|
||||||
mPayRate = mPayRateEditText!!.text.toString().toFloat()
|
|
||||||
}
|
|
||||||
total = mPayRate * mUnits
|
|
||||||
mTotalPayTextView!!.text = total.toString()
|
|
||||||
} else if (mRadioButtonOne!!.isChecked) {
|
|
||||||
if (mPayRateEditText!!.text.toString() != "") {
|
|
||||||
mPayRate = mPayRateEditText!!.text.toString().toFloat()
|
|
||||||
total = mPayRate * calculateDuration(mHoursIn, mMinutesIn, mHoursOut, mMinutesOut, mBreaks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mTotalPayTextView!!.text = String.format("%.2f", total)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater)
|
|
||||||
menu.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun saveProduct() {
|
|
||||||
val typeString = mType
|
|
||||||
val descriptionString = mLocationEditText!!.text.toString().trim { it <= ' ' }
|
|
||||||
if (TextUtils.isEmpty(descriptionString)) {
|
|
||||||
Toast.makeText(context, "please insert Location", Toast.LENGTH_SHORT).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val dateString = mDateEditText!!.text.toString().trim { it <= ' ' }
|
|
||||||
if (TextUtils.isEmpty(dateString)) {
|
|
||||||
Toast.makeText(context, "please insert Date", Toast.LENGTH_SHORT).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var timeInString: String = ""
|
|
||||||
var timeOutString: String = ""
|
|
||||||
var breaks = 0
|
|
||||||
var units = 0f
|
|
||||||
var duration = 0f
|
|
||||||
var payRate = 0f
|
|
||||||
val payRateString = mPayRateEditText!!.text.toString().trim { it <= ' ' }
|
|
||||||
if (!TextUtils.isEmpty(payRateString)) {
|
|
||||||
payRate = payRateString.toFloat()
|
|
||||||
}
|
|
||||||
var totalPay = 0f
|
|
||||||
if ((typeString == "Hourly")) {
|
|
||||||
timeInString = mTimeInEditText!!.text.toString().trim { it <= ' ' }
|
|
||||||
if (TextUtils.isEmpty(timeInString)) {
|
|
||||||
Toast.makeText(context, "please insert Time in", Toast.LENGTH_SHORT).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
timeOutString = mTimeOutEditText!!.text.toString().trim { it <= ' ' }
|
|
||||||
if (TextUtils.isEmpty(timeOutString)) {
|
|
||||||
Toast.makeText(context, "please insert Time out", Toast.LENGTH_SHORT).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val breakMins = mBreakEditText!!.text.toString().trim { it <= ' ' }
|
|
||||||
if (!TextUtils.isEmpty(breakMins)) {
|
|
||||||
breaks = breakMins.toInt()
|
|
||||||
if ((breaks.toFloat() / 60) > calculateDurationWithoutBreak(mHoursIn, mMinutesIn, mHoursOut, mMinutesOut)) {
|
|
||||||
Toast.makeText(context, "Break larger than duration", Toast.LENGTH_SHORT).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
duration = calculateDuration(mHoursIn, mMinutesIn, mHoursOut, mMinutesOut, breaks)
|
|
||||||
totalPay = duration * payRate
|
|
||||||
} else if ((typeString == "Piece Rate")) {
|
|
||||||
val unitsString = mUnitEditText!!.text.toString().trim { it <= ' ' }
|
|
||||||
if (TextUtils.isEmpty(unitsString) || unitsString.toFloat() <= 0) {
|
|
||||||
Toast.makeText(context, "Insert Units", Toast.LENGTH_SHORT).show()
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
units = unitsString.toFloat()
|
|
||||||
}
|
|
||||||
duration = 0f
|
|
||||||
totalPay = units * payRate
|
|
||||||
}
|
|
||||||
val values = ContentValues()
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_TYPE, typeString)
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION, descriptionString)
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_DATE, dateString)
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_TIME_IN, timeInString)
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_TIME_OUT, timeOutString)
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_DURATION, duration)
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_BREAK, breaks)
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_UNIT, units)
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_PAYRATE, payRate)
|
|
||||||
values.put(ShiftsEntry.COLUMN_SHIFT_TOTALPAY, totalPay)
|
|
||||||
if (mCurrentProductUri == null) {
|
|
||||||
val newUri = activity.contentResolver.insert(ShiftsEntry.CONTENT_URI, values)
|
|
||||||
if (newUri == null) {
|
|
||||||
Toast.makeText(context, getString(R.string.insert_item_failed),
|
|
||||||
Toast.LENGTH_SHORT).show()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(context, getString(R.string.insert_item_successful),
|
|
||||||
Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val rowsAffected = activity.contentResolver.update(mCurrentProductUri!!, values, null, null)
|
|
||||||
if (rowsAffected == 0) {
|
|
||||||
Toast.makeText(context, getString(R.string.update_item_failed),
|
|
||||||
Toast.LENGTH_SHORT).show()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(context, getString(R.string.update_item_successful),
|
|
||||||
Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(activity.fragmentManager)!!.popBackStack("main", 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setDuration() {
|
|
||||||
val mcurrentTime = Calendar.getInstance()
|
|
||||||
mBreaks = 0
|
|
||||||
if (mBreakEditText!!.text.toString() != "") {
|
|
||||||
mBreaks = mBreakEditText!!.text.toString().toInt()
|
|
||||||
}
|
|
||||||
if ((mTimeOutEditText!!.text.toString() == "")) {
|
|
||||||
mHoursOut = mcurrentTime[Calendar.HOUR_OF_DAY]
|
|
||||||
mMinutesOut = mcurrentTime[Calendar.MINUTE]
|
|
||||||
} else {
|
|
||||||
mHoursOut = (mTimeOutEditText!!.text.toString().subSequence(0, 2)).toString().toInt()
|
|
||||||
mMinutesOut = (mTimeOutEditText!!.text.toString().subSequence(3, 5)).toString().toInt()
|
|
||||||
}
|
|
||||||
if ((mTimeInEditText!!.text.toString() == "")) {
|
|
||||||
mHoursIn = mcurrentTime[Calendar.HOUR_OF_DAY]
|
|
||||||
mMinutesIn = mcurrentTime[Calendar.MINUTE]
|
|
||||||
} else {
|
|
||||||
mHoursIn = (mTimeInEditText!!.text.toString().subSequence(0, 2)).toString().toInt()
|
|
||||||
mMinutesIn = (mTimeInEditText!!.text.toString().subSequence(3, 5)).toString().toInt()
|
|
||||||
}
|
|
||||||
mDurationTextView!!.text = calculateDuration(mHoursIn, mMinutesIn, mHoursOut, mMinutesOut, mBreaks).toString() + " hours"
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setDate(year: Int, month: Int, day: Int) {
|
|
||||||
mYear = year
|
|
||||||
mMonth = month
|
|
||||||
mDay = day
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setTime(minutes: Int, hours: Int) {
|
|
||||||
mMinutesIn = minutes
|
|
||||||
mHoursIn = hours
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setTime2(minutes: Int, hours: Int) {
|
|
||||||
mMinutesOut = minutes
|
|
||||||
mHoursOut = hours
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun calculateDuration(hoursIn: Int, minutesIn: Int, hoursOut: Int, minutesOut: Int, breaks: Int): Float {
|
|
||||||
val duration: Float
|
|
||||||
if (hoursOut > hoursIn) {
|
|
||||||
duration = ((hoursOut.toFloat() + (minutesOut.toFloat() / 60)) - (hoursIn.toFloat() + (minutesIn.toFloat() / 60))) - (breaks.toFloat() / 60)
|
|
||||||
} else {
|
|
||||||
duration = (((hoursOut.toFloat() + (minutesOut.toFloat() / 60)) - (hoursIn.toFloat() + (minutesIn.toFloat() / 60))) - (breaks.toFloat() / 60) + 24)
|
|
||||||
}
|
|
||||||
val s = String.format("%.2f", duration)
|
|
||||||
return s.toFloat()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun calculateDurationWithoutBreak(hoursIn: Int, minutesIn: Int, hoursOut: Int, minutesOut: Int): Float {
|
|
||||||
val duration: Float
|
|
||||||
if (hoursOut > hoursIn) {
|
|
||||||
duration = ((hoursOut.toFloat() + (minutesOut.toFloat() / 60)) - (hoursIn.toFloat() + (minutesIn.toFloat() / 60)))
|
|
||||||
} else {
|
|
||||||
duration = (((hoursOut.toFloat() + (minutesOut.toFloat() / 60)) - (hoursIn.toFloat() + (minutesIn.toFloat() / 60))) + 24)
|
|
||||||
}
|
|
||||||
val s = String.format("%.2f", duration)
|
|
||||||
return s.toFloat()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor?> {
|
|
||||||
progressBarAI!!.visibility = View.VISIBLE
|
|
||||||
scrollView!!.visibility = View.GONE
|
|
||||||
val projection = arrayOf<String?>(
|
|
||||||
ShiftsEntry._ID,
|
|
||||||
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_PAYRATE,
|
|
||||||
ShiftsEntry.COLUMN_SHIFT_UNIT,
|
|
||||||
ShiftsEntry.COLUMN_SHIFT_TOTALPAY)
|
|
||||||
return CursorLoader((context)!!, (mCurrentProductUri)!!,
|
|
||||||
projection, null, null, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoaderReset(loader: Loader<Cursor?>) {}
|
|
||||||
|
|
||||||
override fun onLoadFinished(loader: Loader<Cursor?>, cursor: Cursor?) {
|
|
||||||
progressBarAI!!.visibility = View.GONE
|
|
||||||
scrollView!!.visibility = View.VISIBLE
|
|
||||||
if (cursor == null || cursor.count < 1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
val descriptionColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION)
|
|
||||||
val dateColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_DATE)
|
|
||||||
val timeInColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_TIME_IN)
|
|
||||||
val timeOutColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_TIME_OUT)
|
|
||||||
val breakColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_BREAK)
|
|
||||||
val durationColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_DURATION)
|
|
||||||
val typeColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_TYPE)
|
|
||||||
val unitColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_UNIT)
|
|
||||||
val payrateColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_PAYRATE)
|
|
||||||
val totalPayColumnIndex = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_TOTALPAY)
|
|
||||||
val type = cursor.getString(typeColumnIndex)
|
|
||||||
val description = cursor.getString(descriptionColumnIndex)
|
|
||||||
val date = cursor.getString(dateColumnIndex)
|
|
||||||
val timeIn = cursor.getString(timeInColumnIndex)
|
|
||||||
val timeOut = cursor.getString(timeOutColumnIndex)
|
|
||||||
val breaks = cursor.getInt(breakColumnIndex)
|
|
||||||
val duration = cursor.getFloat(durationColumnIndex)
|
|
||||||
val unit = cursor.getFloat(unitColumnIndex)
|
|
||||||
val payrate = cursor.getFloat(payrateColumnIndex)
|
|
||||||
val totalPay = cursor.getFloat(totalPayColumnIndex)
|
|
||||||
mLocationEditText!!.setText(description)
|
|
||||||
mDateEditText!!.setText(date)
|
|
||||||
if ((type == "Hourly") || (type == "hourly")) {
|
|
||||||
mRadioButtonOne!!.isChecked = true
|
|
||||||
mRadioButtonTwo!!.isChecked = false
|
|
||||||
mTimeInEditText!!.setText(timeIn)
|
|
||||||
mTimeOutEditText!!.setText(timeOut)
|
|
||||||
mBreakEditText!!.setText(Integer.toString(breaks))
|
|
||||||
mDurationTextView!!.text = String.format("%.2f", duration) + " Hours"
|
|
||||||
} else if ((type == "Piece Rate")) {
|
|
||||||
mRadioButtonOne!!.isChecked = false
|
|
||||||
mRadioButtonTwo!!.isChecked = true
|
|
||||||
mUnitEditText!!.setText(java.lang.Float.toString(unit))
|
|
||||||
}
|
|
||||||
mPayRateEditText!!.setText(String.format("%.2f", payrate))
|
|
||||||
mTotalPayTextView!!.text = String.format("%.2f", totalPay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val EXISTING_PRODUCT_LOADER = 0
|
|
||||||
var mRadioGroup: RadioGroup? = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,459 +0,0 @@
|
|||||||
package com.appttude.h_mal.farmr
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.ContentValues
|
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.database.Cursor
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.Environment
|
|
||||||
import android.os.StrictMode
|
|
||||||
import android.os.StrictMode.VmPolicy
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.ListView
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.FragmentTransaction
|
|
||||||
import androidx.loader.app.LoaderManager
|
|
||||||
import androidx.loader.content.CursorLoader
|
|
||||||
import androidx.loader.content.Loader
|
|
||||||
import com.ajts.androidmads.library.SQLiteToExcel
|
|
||||||
import com.ajts.androidmads.library.SQLiteToExcel.ExportListener
|
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry
|
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsDbHelper
|
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class FragmentMain : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
|
|
||||||
var mCursorAdapter: ShiftsCursorAdapter? = null
|
|
||||||
var shiftsDbhelper: ShiftsDbHelper? = null
|
|
||||||
lateinit var defaultLoaderCallback: LoaderManager.LoaderCallbacks<Cursor>
|
|
||||||
lateinit var activity: MainActivity
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?): View? {
|
|
||||||
// Inflate the layout for this fragment
|
|
||||||
val rootView = inflater.inflate(R.layout.fragment_main, container, false)
|
|
||||||
setHasOptionsMenu(true)
|
|
||||||
activity = (requireActivity() as MainActivity)
|
|
||||||
|
|
||||||
activity.setActionBarTitle(getString(R.string.app_name))
|
|
||||||
activity.filter = activity.getSharedPreferences("PREFS", 0)
|
|
||||||
activity.sortOrder = activity.filter?.getString("Filter", null)
|
|
||||||
defaultLoaderCallback = this
|
|
||||||
val productListView = rootView.findViewById<View>(R.id.list_item_view) as ListView
|
|
||||||
val emptyView = rootView.findViewById<View>(R.id.empty_view)
|
|
||||||
productListView.emptyView = emptyView
|
|
||||||
mCursorAdapter = ShiftsCursorAdapter(activity, null)
|
|
||||||
productListView.adapter = mCursorAdapter
|
|
||||||
loaderManager.initLoader(DEFAULT_LOADER, null, defaultLoaderCallback)
|
|
||||||
val fab = rootView.findViewById<FloatingActionButton>(R.id.fab1)
|
|
||||||
fab.setOnClickListener {
|
|
||||||
val fragmentTransaction: FragmentTransaction = activity.fragmentManager!!.beginTransaction()
|
|
||||||
fragmentTransaction.replace(R.id.container, FragmentAddItem()).addToBackStack("additem").commit()
|
|
||||||
}
|
|
||||||
return rootView
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
when (item.itemId) {
|
|
||||||
R.id.delete_all -> {
|
|
||||||
deleteAllProducts()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.help -> {
|
|
||||||
AlertDialog.Builder(context)
|
|
||||||
.setTitle("Help & Support:")
|
|
||||||
.setView(R.layout.dialog_layout)
|
|
||||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> }.create().show()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.filter_data -> {
|
|
||||||
val fragmentTransaction: FragmentTransaction = activity.fragmentManager!!.beginTransaction()
|
|
||||||
fragmentTransaction.replace(R.id.container, FilterDataFragment()).addToBackStack("filterdata").commit()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.sort_data -> {
|
|
||||||
sortData()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.clear_filter -> {
|
|
||||||
activity.args = null
|
|
||||||
activity.selection = null
|
|
||||||
NEW_LOADER = 0
|
|
||||||
loaderManager.restartLoader(DEFAULT_LOADER, null, this)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.export_data -> {
|
|
||||||
if (checkStoragePermissions(activity)) {
|
|
||||||
AlertDialog.Builder(context)
|
|
||||||
.setTitle("Export?")
|
|
||||||
.setMessage("Exporting current filtered data. Continue?")
|
|
||||||
.setNegativeButton(android.R.string.no, null)
|
|
||||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> ExportData() }.create().show()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(context, "Storage permissions required", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.action_favorite -> {
|
|
||||||
AlertDialog.Builder(context)
|
|
||||||
.setTitle("Info:")
|
|
||||||
.setMessage(retrieveInfo())
|
|
||||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> }.create().show()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sortData() {
|
|
||||||
val grpname = arrayOf("Added", "Date", "Name")
|
|
||||||
val sortQuery = arrayOf<String?>("")
|
|
||||||
var checkedItem = -1
|
|
||||||
if (activity.sortOrder != null && activity.sortOrder!!.contains(ShiftsEntry._ID)) {
|
|
||||||
checkedItem = 0
|
|
||||||
sortQuery[0] = ShiftsEntry._ID
|
|
||||||
} else if (activity.sortOrder != null && activity.sortOrder!!.contains(ShiftsEntry.COLUMN_SHIFT_DATE)) {
|
|
||||||
checkedItem = 1
|
|
||||||
sortQuery[0] = ShiftsEntry.COLUMN_SHIFT_DATE
|
|
||||||
} else if (activity.sortOrder != null && activity.sortOrder!!.contains(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION)) {
|
|
||||||
checkedItem = 2
|
|
||||||
sortQuery[0] = ShiftsEntry.COLUMN_SHIFT_DESCRIPTION
|
|
||||||
}
|
|
||||||
val alt_bld = AlertDialog.Builder(context)
|
|
||||||
//alt_bld.setIcon(R.drawable.icon);
|
|
||||||
alt_bld.setTitle("Sort by:")
|
|
||||||
alt_bld.setSingleChoiceItems(grpname, checkedItem, DialogInterface.OnClickListener { dialog, item ->
|
|
||||||
when (item) {
|
|
||||||
0 -> {
|
|
||||||
sortQuery[0] = ShiftsEntry._ID
|
|
||||||
return@OnClickListener
|
|
||||||
}
|
|
||||||
|
|
||||||
1 -> {
|
|
||||||
sortQuery[0] = ShiftsEntry.COLUMN_SHIFT_DATE
|
|
||||||
return@OnClickListener
|
|
||||||
}
|
|
||||||
|
|
||||||
2 -> sortQuery[0] = ShiftsEntry.COLUMN_SHIFT_DESCRIPTION
|
|
||||||
}
|
|
||||||
}).setPositiveButton("Ascending") { dialog, id ->
|
|
||||||
activity.sortOrder = sortQuery[0] + " ASC"
|
|
||||||
activity.filter!!.edit().putString("Filter", activity.sortOrder).apply()
|
|
||||||
loaderManager.restartLoader(DEFAULT_LOADER, null, defaultLoaderCallback)
|
|
||||||
dialog.dismiss()
|
|
||||||
}.setNegativeButton("Descending") { dialog, id ->
|
|
||||||
activity.sortOrder = sortQuery[0] + " DESC"
|
|
||||||
activity.filter!!.edit().putString("Filter", activity.sortOrder).apply()
|
|
||||||
loaderManager.restartLoader(DEFAULT_LOADER, null, defaultLoaderCallback)
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
val alert = alt_bld.create()
|
|
||||||
alert.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun deleteAllProducts() {
|
|
||||||
AlertDialog.Builder(context)
|
|
||||||
.setTitle("Delete?")
|
|
||||||
.setMessage("Are you sure you want to delete all date?")
|
|
||||||
.setNegativeButton(android.R.string.no, null)
|
|
||||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 ->
|
|
||||||
val rowsDeleted = activity.contentResolver.delete(ShiftsEntry.CONTENT_URI, null, null)
|
|
||||||
Toast.makeText(context, "$rowsDeleted Items Deleted", Toast.LENGTH_SHORT).show()
|
|
||||||
}.create().show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
if (NEW_LOADER > DEFAULT_LOADER) {
|
|
||||||
loaderManager.restartLoader(DEFAULT_LOADER, null, defaultLoaderCallback)
|
|
||||||
println("reloading loader")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 retrieveInfo(): String {
|
|
||||||
val projection = arrayOf<String?>(
|
|
||||||
ShiftsEntry._ID,
|
|
||||||
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,
|
|
||||||
activity.selection,
|
|
||||||
activity.args,
|
|
||||||
activity.sortOrder)
|
|
||||||
var totalDuration = 0.0f
|
|
||||||
var countOfTypeH = 0
|
|
||||||
var countOfTypeP = 0
|
|
||||||
var totalUnits = 0f
|
|
||||||
var totalPay = 0f
|
|
||||||
var lines = 0
|
|
||||||
try {
|
|
||||||
while (cursor!!.moveToNext()) {
|
|
||||||
val durationColumnIndex = cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_DURATION))
|
|
||||||
val typeColumnIndex = cursor.getString(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_TYPE))
|
|
||||||
val unitColumnIndex = cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_UNIT))
|
|
||||||
val totalpayColumnIndex = cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_TOTALPAY))
|
|
||||||
totalDuration = totalDuration + durationColumnIndex
|
|
||||||
if (typeColumnIndex.contains("Hourly")) {
|
|
||||||
countOfTypeH = countOfTypeH + 1
|
|
||||||
} else if (typeColumnIndex.contains("Piece")) {
|
|
||||||
countOfTypeP = countOfTypeP + 1
|
|
||||||
}
|
|
||||||
totalUnits = totalUnits + unitColumnIndex
|
|
||||||
totalPay = totalPay + totalpayColumnIndex
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (cursor != null && cursor.count > 0) {
|
|
||||||
lines = cursor.count
|
|
||||||
cursor.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buildInfoString(totalDuration, countOfTypeH, countOfTypeP, totalUnits, totalPay, lines)
|
|
||||||
}
|
|
||||||
|
|
||||||
@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)"
|
|
||||||
}
|
|
||||||
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 onCreateLoader(i: Int, bundle: Bundle?): Loader<Cursor> {
|
|
||||||
val projection = arrayOf<String?>(
|
|
||||||
ShiftsEntry._ID,
|
|
||||||
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_PAYRATE,
|
|
||||||
ShiftsEntry.COLUMN_SHIFT_UNIT,
|
|
||||||
ShiftsEntry.COLUMN_SHIFT_TOTALPAY)
|
|
||||||
return CursorLoader(context!!,
|
|
||||||
ShiftsEntry.CONTENT_URI,
|
|
||||||
projection,
|
|
||||||
activity.selection,
|
|
||||||
activity.args,
|
|
||||||
activity.sortOrder)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
|
|
||||||
mCursorAdapter!!.swapCursor(cursor)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoaderReset(loader: Loader<Cursor>) {
|
|
||||||
mCursorAdapter!!.swapCursor(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
||||||
println("request code$requestCode")
|
|
||||||
if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
|
|
||||||
if (grantResults.size > 0
|
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
exportDialog()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(context, "Storage Permissions denied", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun exportDialog() {
|
|
||||||
AlertDialog.Builder(context)
|
|
||||||
.setTitle("Export?")
|
|
||||||
.setMessage("Exporting current filtered data. Continue?")
|
|
||||||
.setNegativeButton(android.R.string.no, null)
|
|
||||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> ExportData() }.create().show()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1
|
|
||||||
private const val DEFAULT_LOADER = 0
|
|
||||||
var NEW_LOADER = 0
|
|
||||||
|
|
||||||
// // Storage Permissions
|
|
||||||
// private static final int REQUEST_EXTERNAL_STORAGE = 1;
|
|
||||||
// private static String[] PERMISSIONS_STORAGE = {
|
|
||||||
// Manifest.permission.READ_EXTERNAL_STORAGE,
|
|
||||||
// Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
// };
|
|
||||||
// /**
|
|
||||||
// * Checks if the app has permission to write to device storage
|
|
||||||
// *
|
|
||||||
// * If the app does not has permission then the user will be prompted to grant permissions
|
|
||||||
// *
|
|
||||||
// * @param activity
|
|
||||||
// */
|
|
||||||
// public static void verifyStoragePermissions(Activity activity) {
|
|
||||||
// // Check if we have write permission
|
|
||||||
// int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
|
||||||
//
|
|
||||||
// if (permission != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
// // We don't have permission so prompt the user
|
|
||||||
// ActivityCompat.requestPermissions(
|
|
||||||
// activity,
|
|
||||||
// PERMISSIONS_STORAGE,
|
|
||||||
// REQUEST_EXTERNAL_STORAGE
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
fun checkStoragePermissions(activity: Activity?): Boolean {
|
|
||||||
var status = false
|
|
||||||
val permission = ActivityCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
if (permission == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
status = true
|
|
||||||
}
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
package com.appttude.h_mal.farmr
|
|
||||||
|
|
||||||
import android.database.Cursor
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.ProgressBar
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.FragmentTransaction
|
|
||||||
import androidx.loader.app.LoaderManager
|
|
||||||
import androidx.loader.content.CursorLoader
|
|
||||||
import androidx.loader.content.Loader
|
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry
|
|
||||||
|
|
||||||
class FurtherInfoFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
|
|
||||||
private var typeTV: TextView? = null
|
|
||||||
private var descriptionTV: TextView? = null
|
|
||||||
private var dateTV: TextView? = null
|
|
||||||
private var times: TextView? = null
|
|
||||||
private var breakTV: TextView? = null
|
|
||||||
private var durationTV: TextView? = null
|
|
||||||
private var unitsTV: TextView? = null
|
|
||||||
private var payRateTV: TextView? = null
|
|
||||||
private var totalPayTV: TextView? = null
|
|
||||||
private var hourlyDetailHolder: LinearLayout? = null
|
|
||||||
private var unitsHolder: LinearLayout? = null
|
|
||||||
private var wholeView: LinearLayout? = null
|
|
||||||
private var progressBarFI: ProgressBar? = null
|
|
||||||
private var editButton: Button? = null
|
|
||||||
private var CurrentUri: Uri? = null
|
|
||||||
|
|
||||||
lateinit var activity: MainActivity
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?): View {
|
|
||||||
// Inflate the layout for this fragment
|
|
||||||
val rootView: View = inflater.inflate(R.layout.fragment_futher_info, container, false)
|
|
||||||
setHasOptionsMenu(true)
|
|
||||||
activity = (requireActivity() as MainActivity)
|
|
||||||
activity.setActionBarTitle(getString(R.string.further_info_title))
|
|
||||||
|
|
||||||
progressBarFI = rootView.findViewById<View>(R.id.progressBar_info) as ProgressBar?
|
|
||||||
wholeView = rootView.findViewById<View>(R.id.further_info_view) as LinearLayout?
|
|
||||||
typeTV = rootView.findViewById<View>(R.id.details_shift) as TextView?
|
|
||||||
descriptionTV = rootView.findViewById<View>(R.id.details_desc) as TextView?
|
|
||||||
dateTV = rootView.findViewById<View>(R.id.details_date) as TextView?
|
|
||||||
times = rootView.findViewById<View>(R.id.details_time) as TextView?
|
|
||||||
breakTV = rootView.findViewById<View>(R.id.details_breaks) as TextView?
|
|
||||||
durationTV = rootView.findViewById<View>(R.id.details_duration) as TextView?
|
|
||||||
unitsTV = rootView.findViewById<View>(R.id.details_units) as TextView?
|
|
||||||
payRateTV = rootView.findViewById<View>(R.id.details_pay_rate) as TextView?
|
|
||||||
totalPayTV = rootView.findViewById<View>(R.id.details_totalpay) as TextView?
|
|
||||||
editButton = rootView.findViewById<View>(R.id.details_edit) as Button?
|
|
||||||
hourlyDetailHolder = rootView.findViewById<View>(R.id.details_hourly_details) as LinearLayout?
|
|
||||||
unitsHolder = rootView.findViewById<View>(R.id.details_units_holder) as LinearLayout?
|
|
||||||
val b: Bundle? = arguments
|
|
||||||
CurrentUri = Uri.parse(b!!.getString("uri"))
|
|
||||||
loaderManager.initLoader(DEFAULT_LOADER, null, this)
|
|
||||||
editButton!!.setOnClickListener(object : View.OnClickListener {
|
|
||||||
override fun onClick(view: View) {
|
|
||||||
val fragmentTransaction: FragmentTransaction = (activity.fragmentManager)!!.beginTransaction()
|
|
||||||
val fragment: Fragment = FragmentAddItem()
|
|
||||||
fragment.arguments = b
|
|
||||||
fragmentTransaction.replace(R.id.container, fragment).addToBackStack("additem").commit()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return rootView
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater)
|
|
||||||
menu.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor?> {
|
|
||||||
progressBarFI!!.visibility = View.VISIBLE
|
|
||||||
wholeView!!.visibility = View.GONE
|
|
||||||
val projection: Array<String?> = arrayOf(
|
|
||||||
ShiftsEntry._ID,
|
|
||||||
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_PAYRATE,
|
|
||||||
ShiftsEntry.COLUMN_SHIFT_UNIT,
|
|
||||||
ShiftsEntry.COLUMN_SHIFT_TOTALPAY)
|
|
||||||
return CursorLoader((context)!!, (CurrentUri)!!,
|
|
||||||
projection, null, null, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
|
|
||||||
progressBarFI!!.visibility = View.GONE
|
|
||||||
wholeView!!.visibility = View.VISIBLE
|
|
||||||
if (cursor == null || cursor.count < 1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
val descriptionColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION)
|
|
||||||
val dateColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_DATE)
|
|
||||||
val timeInColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_TIME_IN)
|
|
||||||
val timeOutColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_TIME_OUT)
|
|
||||||
val breakColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_BREAK)
|
|
||||||
val durationColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_DURATION)
|
|
||||||
val typeColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_TYPE)
|
|
||||||
val unitColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_UNIT)
|
|
||||||
val payrateColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_PAYRATE)
|
|
||||||
val totalPayColumnIndex: Int = cursor.getColumnIndex(ShiftsEntry.COLUMN_SHIFT_TOTALPAY)
|
|
||||||
val type: String = cursor.getString(typeColumnIndex)
|
|
||||||
val description: String = cursor.getString(descriptionColumnIndex)
|
|
||||||
val date: String = cursor.getString(dateColumnIndex)
|
|
||||||
val timeIn: String = cursor.getString(timeInColumnIndex)
|
|
||||||
val timeOut: String = cursor.getString(timeOutColumnIndex)
|
|
||||||
val breaks: Int = cursor.getInt(breakColumnIndex)
|
|
||||||
val duration: Float = cursor.getFloat(durationColumnIndex)
|
|
||||||
val unit: Float = cursor.getFloat(unitColumnIndex)
|
|
||||||
val payrate: Float = cursor.getFloat(payrateColumnIndex)
|
|
||||||
val totalPay: Float = cursor.getFloat(totalPayColumnIndex)
|
|
||||||
var durationString: String = ShiftsCursorAdapter.Companion.timeValues(duration).get(0) + " Hours " + ShiftsCursorAdapter.Companion.timeValues(duration).get(1) + " Minutes "
|
|
||||||
if (breaks != 0) {
|
|
||||||
durationString = durationString + " (+ " + Integer.toString(breaks) + " minutes break)"
|
|
||||||
}
|
|
||||||
typeTV!!.text = type
|
|
||||||
descriptionTV!!.text = description
|
|
||||||
dateTV!!.text = date
|
|
||||||
var totalPaid: String? = ""
|
|
||||||
val currency: String = "$"
|
|
||||||
if ((type == "Hourly")) {
|
|
||||||
hourlyDetailHolder!!.visibility = View.VISIBLE
|
|
||||||
unitsHolder!!.visibility = View.GONE
|
|
||||||
times!!.text = timeIn + " - " + timeOut
|
|
||||||
breakTV!!.text = Integer.toString(breaks) + "mins"
|
|
||||||
durationTV!!.text = durationString
|
|
||||||
totalPaid = (String.format("%.2f", duration) + " Hours @ " + currency + String.format("%.2f", payrate) + " per Hour" + "\n"
|
|
||||||
+ "Equals: " + currency + String.format("%.2f", totalPay))
|
|
||||||
} else if ((type == "Piece Rate")) {
|
|
||||||
hourlyDetailHolder!!.visibility = View.GONE
|
|
||||||
unitsHolder!!.visibility = View.VISIBLE
|
|
||||||
unitsTV!!.text = String.format("%.2f", unit)
|
|
||||||
totalPaid = (String.format("%.2f", unit) + " Units @ " + currency + String.format("%.2f", payrate) + " per Unit" + "\n"
|
|
||||||
+ "Equals: " + currency + String.format("%.2f", totalPay))
|
|
||||||
}
|
|
||||||
payRateTV!!.text = String.format("%.2f", payrate)
|
|
||||||
totalPayTV!!.text = totalPaid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoaderReset(loader: Loader<Cursor>) {}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val DEFAULT_LOADER: Int = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
package com.appttude.h_mal.farmr
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.View
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.widget.Toolbar
|
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
|
||||||
var filter: SharedPreferences? = null
|
|
||||||
var context: Context? = null
|
|
||||||
var sortOrder: String? = null
|
|
||||||
var selection: String? = null
|
|
||||||
var args: Array<String>? = null
|
|
||||||
private var toolbar: Toolbar? = null
|
|
||||||
var fragmentManager: FragmentManager? = null
|
|
||||||
private var currentFragment: String? = null
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setContentView(R.layout.main_view)
|
|
||||||
verifyStoragePermissions(this)
|
|
||||||
toolbar = findViewById<View>(R.id.toolbar) as Toolbar
|
|
||||||
setSupportActionBar(toolbar)
|
|
||||||
fragmentManager = supportFragmentManager
|
|
||||||
val fragmentTransaction = fragmentManager?.beginTransaction()
|
|
||||||
fragmentTransaction?.replace(R.id.container, FragmentMain())?.addToBackStack("main")?.commit()
|
|
||||||
fragmentManager?.addOnBackStackChangedListener {
|
|
||||||
val f = fragmentManager?.fragments
|
|
||||||
val frag = f?.get(0)
|
|
||||||
currentFragment = frag?.javaClass?.simpleName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
|
||||||
menuInflater.inflate(R.menu.menu_main, menu)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBackPressed() {
|
|
||||||
when (currentFragment) {
|
|
||||||
"FragmentMain" -> {
|
|
||||||
AlertDialog.Builder(this)
|
|
||||||
.setTitle("Leave?")
|
|
||||||
.setMessage("Are you sure you want to exit Farmr?")
|
|
||||||
.setNegativeButton(android.R.string.no, null)
|
|
||||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 ->
|
|
||||||
val intent = Intent(Intent.ACTION_MAIN)
|
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
|
||||||
intent.addCategory(Intent.CATEGORY_HOME)
|
|
||||||
startActivity(intent)
|
|
||||||
finish()
|
|
||||||
System.exit(0)
|
|
||||||
}.create().show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
"FragmentAddItem" -> {
|
|
||||||
if (FragmentAddItem.Companion.mRadioGroup!!.checkedRadioButtonId == -1) {
|
|
||||||
fragmentManager!!.popBackStack()
|
|
||||||
} else {
|
|
||||||
AlertDialog.Builder(this)
|
|
||||||
.setTitle("Discard Changes?")
|
|
||||||
.setMessage("Are you sure you want to discard changes?")
|
|
||||||
.setNegativeButton(android.R.string.no, null)
|
|
||||||
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> fragmentManager!!.popBackStack() }.create().show()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> if (fragmentManager!!.backStackEntryCount > 1) {
|
|
||||||
fragmentManager!!.popBackStack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setActionBarTitle(title: String?) {
|
|
||||||
toolbar!!.title = title
|
|
||||||
}
|
|
||||||
|
|
||||||
// Storage Permissions
|
|
||||||
private val REQUEST_EXTERNAL_STORAGE = 1
|
|
||||||
private val PERMISSIONS_STORAGE = arrayOf(
|
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the app has permission to write to device storage
|
|
||||||
*
|
|
||||||
* If the app does not has permission then the user will be prompted to grant permissions
|
|
||||||
*
|
|
||||||
* @param activity
|
|
||||||
*/
|
|
||||||
fun verifyStoragePermissions(activity: Activity?) {
|
|
||||||
// Check if we have write permission
|
|
||||||
val permission = ActivityCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
// We don't have permission so prompt the user
|
|
||||||
ActivityCompat.requestPermissions(
|
|
||||||
activity,
|
|
||||||
PERMISSIONS_STORAGE,
|
|
||||||
REQUEST_EXTERNAL_STORAGE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
package com.appttude.h_mal.farmr
|
|
||||||
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.ContentUris
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.database.Cursor
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.View.OnLongClickListener
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.CursorAdapter
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.fragment.app.FragmentTransaction
|
|
||||||
import com.appttude.h_mal.farmr.data.ShiftProvider
|
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry
|
|
||||||
import kotlin.math.floor
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by h_mal on 26/12/2017.
|
|
||||||
*/
|
|
||||||
class ShiftsCursorAdapter constructor(private val activity: MainActivity, c: Cursor?) : CursorAdapter(activity, c, 0) {
|
|
||||||
private var mContext: Context? = null
|
|
||||||
var shiftProvider: ShiftProvider? = null
|
|
||||||
override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
|
|
||||||
return LayoutInflater.from(context).inflate(R.layout.list_item_1, parent, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bindView(view: View, context: Context, cursor: Cursor) {
|
|
||||||
mContext = context
|
|
||||||
val descriptionTextView: TextView = view.findViewById<View>(R.id.location) as TextView
|
|
||||||
val dateTextView: TextView = view.findViewById<View>(R.id.date) as TextView
|
|
||||||
val totalPay: TextView = view.findViewById<View>(R.id.total_pay) as TextView
|
|
||||||
val hoursView: TextView = view.findViewById<View>(R.id.hours) as TextView
|
|
||||||
val h: TextView = view.findViewById<View>(R.id.h) as TextView
|
|
||||||
val minutesView: TextView = view.findViewById<View>(R.id.minutes) as TextView
|
|
||||||
val m: TextView = view.findViewById<View>(R.id.m) as TextView
|
|
||||||
val editView: ImageView = view.findViewById<View>(R.id.imageView) as ImageView
|
|
||||||
h.text = "h"
|
|
||||||
m.text = "m"
|
|
||||||
val typeColumnIndex: String = cursor.getString(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_TYPE))
|
|
||||||
val descriptionColumnIndex: String = cursor.getString(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION))
|
|
||||||
val dateColumnIndex: String = cursor.getString(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_DATE))
|
|
||||||
val durationColumnIndex: Float = cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_DURATION))
|
|
||||||
val unitsColumnIndex: Float = cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_UNIT))
|
|
||||||
val totalpayColumnIndex: Float = cursor.getFloat(cursor.getColumnIndexOrThrow(ShiftsEntry.COLUMN_SHIFT_TOTALPAY))
|
|
||||||
descriptionTextView.text = descriptionColumnIndex
|
|
||||||
dateTextView.text = newDate(dateColumnIndex)
|
|
||||||
totalPay.text = String.format("%.2f", totalpayColumnIndex)
|
|
||||||
if ((typeColumnIndex == "Piece Rate") && durationColumnIndex == 0f) {
|
|
||||||
hoursView.text = unitsColumnIndex.toString()
|
|
||||||
h.text = ""
|
|
||||||
minutesView.text = ""
|
|
||||||
m.text = "pcs"
|
|
||||||
} else // if(typeColumnIndex.equals("Hourly") || typeColumnIndex.equals("hourly"))
|
|
||||||
{
|
|
||||||
hoursView.text = timeValues(durationColumnIndex).get(0)
|
|
||||||
minutesView.text = timeValues(durationColumnIndex).get(1)
|
|
||||||
}
|
|
||||||
val ID: Long = cursor.getLong(cursor.getColumnIndexOrThrow(ShiftsEntry._ID))
|
|
||||||
val currentProductUri: Uri = ContentUris.withAppendedId(ShiftsEntry.CONTENT_URI, ID)
|
|
||||||
val b: Bundle = Bundle()
|
|
||||||
b.putString("uri", currentProductUri.toString())
|
|
||||||
view.setOnClickListener { // activity.clickOnViewItem(ID);
|
|
||||||
val fragmentTransaction: FragmentTransaction = (activity.fragmentManager)!!.beginTransaction()
|
|
||||||
val fragment2: FurtherInfoFragment = FurtherInfoFragment()
|
|
||||||
fragment2.arguments = b
|
|
||||||
fragmentTransaction.replace(R.id.container, fragment2).addToBackStack("furtherinfo").commit()
|
|
||||||
}
|
|
||||||
editView.setOnClickListener {
|
|
||||||
val fragmentTransaction: FragmentTransaction = (activity.fragmentManager)!!.beginTransaction()
|
|
||||||
val fragment3: FragmentAddItem = FragmentAddItem()
|
|
||||||
fragment3.arguments = b
|
|
||||||
fragmentTransaction.replace(R.id.container, fragment3).addToBackStack("additem").commit()
|
|
||||||
}
|
|
||||||
view.setOnLongClickListener {
|
|
||||||
println("long click: $ID")
|
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(mContext)
|
|
||||||
builder.setMessage("Are you sure you want to delete")
|
|
||||||
builder.setPositiveButton("delete") { dialog, id -> deleteProduct(ID) }
|
|
||||||
builder.setNegativeButton("cancel") { dialog, id ->
|
|
||||||
dialog?.dismiss()
|
|
||||||
}
|
|
||||||
val alertDialog: AlertDialog = builder.create()
|
|
||||||
alertDialog.show()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun deleteProduct(id: Long) {
|
|
||||||
val args: Array<String> = arrayOf(id.toString())
|
|
||||||
// String whereClause = String.format(ShiftsEntry._ID + " in (%s)", new Object[] { TextUtils.join(",", Collections.nCopies(args.length, "?")) }); //for deleting multiple lines
|
|
||||||
mContext!!.contentResolver.delete(ShiftsEntry.CONTENT_URI, ShiftsEntry._ID + "=?", args)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun newDate(dateString: String): String {
|
|
||||||
var returnString: String? = "01/01/2010"
|
|
||||||
val year: String = dateString.substring(0, 4)
|
|
||||||
val month: String = dateString.substring(5, 7)
|
|
||||||
val day: String = dateString.substring(8)
|
|
||||||
returnString = "$day-$month-$year"
|
|
||||||
return returnString
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun timeValues(duration: Float): Array<String> {
|
|
||||||
val hours: Int = floor(duration.toDouble()).toInt()
|
|
||||||
val minutes: Int = ((duration - hours) * 60).toInt()
|
|
||||||
val hoursString: String = hours.toString() + ""
|
|
||||||
val minutesString: String = String.format("%02d", minutes)
|
|
||||||
return arrayOf(hoursString, minutesString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.appttude.h_mal.farmr.base
|
||||||
|
|
||||||
|
interface BackPressedListener {
|
||||||
|
fun onBackPressed(): Boolean
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.appttude.h_mal.farmr.base
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.ViewModelLazy
|
||||||
|
import com.appttude.h_mal.farmr.utils.displayToast
|
||||||
|
import com.appttude.h_mal.farmr.utils.getGenericClassAt
|
||||||
|
import com.appttude.h_mal.farmr.viewmodel.ApplicationViewModelFactory
|
||||||
|
import org.kodein.di.KodeinAware
|
||||||
|
import org.kodein.di.android.kodein
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
|
||||||
|
abstract class BaseActivity<V : BaseViewModel> : AppCompatActivity(), KodeinAware {
|
||||||
|
|
||||||
|
override val kodein by kodein()
|
||||||
|
private val factory by instance<ApplicationViewModelFactory>()
|
||||||
|
|
||||||
|
val viewModel: V by getViewModel()
|
||||||
|
|
||||||
|
private fun getViewModel(): Lazy<V> =
|
||||||
|
ViewModelLazy(getGenericClassAt(0), storeProducer = { viewModelStore },
|
||||||
|
factoryProducer = { factory } )
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a loading view which to be shown during async operations
|
||||||
|
*
|
||||||
|
* #setOnClickListener(null) is an ugly work around to prevent under being clicked during
|
||||||
|
* loading
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun <A : AppCompatActivity> startActivity(activity: Class<A>) {
|
||||||
|
val intent = Intent(this, activity)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in case of success or some data emitted from the liveData in viewModel
|
||||||
|
*/
|
||||||
|
open fun onStarted() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in case of success or some data emitted from the liveData in viewModel
|
||||||
|
*/
|
||||||
|
open fun onSuccess(data: Any?) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in case of failure or some error emitted from the liveData in viewModel
|
||||||
|
*/
|
||||||
|
open fun onFailure(error: Any?) {
|
||||||
|
if (error is String) displayToast(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTitleInActionBar(title: String) {
|
||||||
|
setTitle(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.appttude.h_mal.farmr.base
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.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.viewmodel.ApplicationViewModelFactory
|
||||||
|
import org.kodein.di.KodeinAware
|
||||||
|
import org.kodein.di.android.x.kodein
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
@Suppress("EmptyMethod", "EmptyMethod")
|
||||||
|
abstract class BaseFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int) :
|
||||||
|
Fragment(contentLayoutId), KodeinAware {
|
||||||
|
|
||||||
|
override val kodein by kodein()
|
||||||
|
private val factory by instance<ApplicationViewModelFactory>()
|
||||||
|
|
||||||
|
val viewModel: V by getActivityViewModel()
|
||||||
|
|
||||||
|
private fun getActivityViewModel() = createViewModelLazy<V>(
|
||||||
|
getGenericClassAt(0),
|
||||||
|
{ requireActivity().viewModelStore },
|
||||||
|
{ factory })
|
||||||
|
|
||||||
|
var mActivity: BaseActivity<*>? = null
|
||||||
|
|
||||||
|
private var shortAnimationDuration by Delegates.notNull<Int>()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
mActivity = requireActivity() as BaseActivity<*>
|
||||||
|
configureObserver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configureObserver() {
|
||||||
|
viewModel.uiState.observe(viewLifecycleOwner) {
|
||||||
|
when (it) {
|
||||||
|
is ViewState.HasStarted -> onStarted()
|
||||||
|
is ViewState.HasData<*> -> onSuccess(it.data)
|
||||||
|
is ViewState.HasError<*> -> onFailure(it.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in case of starting operation liveData in viewModel
|
||||||
|
*/
|
||||||
|
open fun onStarted() {
|
||||||
|
mActivity?.onStarted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in case of success or some data emitted from the liveData in viewModel
|
||||||
|
*/
|
||||||
|
open fun onSuccess(data: Any?) {
|
||||||
|
mActivity?.onSuccess(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in case of failure or some error emitted from the liveData in viewModel
|
||||||
|
*/
|
||||||
|
open fun onFailure(error: Any?) {
|
||||||
|
mActivity?.onFailure(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTitle(title: String) {
|
||||||
|
(requireActivity() as BaseActivity<*>).setTitleInActionBar(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.appttude.h_mal.farmr.base
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||||
|
import com.appttude.h_mal.farmr.utils.generateView
|
||||||
|
|
||||||
|
open class BaseRecyclerAdapter<T: Any>(
|
||||||
|
@LayoutRes private val emptyViewId: Int,
|
||||||
|
@LayoutRes private val currentViewId: Int
|
||||||
|
): 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)
|
||||||
|
EmptyViewHolder(emptyViewHolder)
|
||||||
|
} else {
|
||||||
|
val currentViewHolder = parent.generateView(currentViewId)
|
||||||
|
CurrentViewHolder(currentViewHolder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return if (list.isNullOrEmpty()) 1 else list!!.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
when (holder) {
|
||||||
|
is EmptyViewHolder -> bindEmptyView(holder.itemView)
|
||||||
|
is CurrentViewHolder -> bindCurrentView(holder.itemView, position, list!![position])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun bindEmptyView(view: View) {}
|
||||||
|
open fun bindCurrentView(view: View, position: Int, data: T) {}
|
||||||
|
|
||||||
|
class EmptyViewHolder(itemView: View): ViewHolder(itemView)
|
||||||
|
class CurrentViewHolder(itemView: View): ViewHolder(itemView)
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.appttude.h_mal.farmr.base
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.appttude.h_mal.farmr.model.ViewState
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
|
open class BaseViewModel: ViewModel() {
|
||||||
|
|
||||||
|
private val _uiState = MutableLiveData<ViewState>()
|
||||||
|
val uiState: LiveData<ViewState> = _uiState
|
||||||
|
|
||||||
|
|
||||||
|
fun onStart() {
|
||||||
|
_uiState.postValue(ViewState.HasStarted)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Any> onSuccess(result: T) {
|
||||||
|
_uiState.postValue(ViewState.HasData(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun <E : Any> onError(error: E) {
|
||||||
|
_uiState.postValue(ViewState.HasError(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.appttude.h_mal.farmr.data
|
||||||
|
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
|
||||||
|
import com.appttude.h_mal.farmr.model.Order
|
||||||
|
import com.appttude.h_mal.farmr.model.Shift
|
||||||
|
import com.appttude.h_mal.farmr.model.Sortable
|
||||||
|
|
||||||
|
interface Repository {
|
||||||
|
fun insertShiftIntoDatabase(shift: Shift): Boolean
|
||||||
|
fun updateShiftIntoDatabase(id: Long, shift: Shift): Boolean
|
||||||
|
fun readShiftsFromDatabase(): List<ShiftObject>?
|
||||||
|
fun readSingleShiftFromDatabase(id: Long): ShiftObject?
|
||||||
|
fun deleteSingleShiftFromDatabase(id: Long): Boolean
|
||||||
|
fun deleteAllShiftsFromDatabase(): Boolean
|
||||||
|
fun retrieveSortAndOrderFromPref(): Pair<Sortable?, Order?>
|
||||||
|
fun setSortAndOrderFromPref(sortable: Sortable, order: Order)
|
||||||
|
fun retrieveFilteringDetailsInPrefs(): Map<String, String?>
|
||||||
|
fun setFilteringDetailsInPrefs(
|
||||||
|
description: String?,
|
||||||
|
timeIn: String?,
|
||||||
|
timeOut: String?,
|
||||||
|
type: String?
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.appttude.h_mal.farmr.data
|
||||||
|
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.LegacyDatabase
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
|
||||||
|
import com.appttude.h_mal.farmr.data.prefs.PreferenceProvider
|
||||||
|
import com.appttude.h_mal.farmr.model.Order
|
||||||
|
import com.appttude.h_mal.farmr.model.Shift
|
||||||
|
import com.appttude.h_mal.farmr.model.Sortable
|
||||||
|
|
||||||
|
class RepositoryImpl(
|
||||||
|
private val legacyDatabase: LegacyDatabase,
|
||||||
|
private val preferenceProvider: PreferenceProvider
|
||||||
|
): Repository {
|
||||||
|
override fun insertShiftIntoDatabase(shift: Shift): Boolean {
|
||||||
|
return legacyDatabase.insertShiftDataIntoDatabase(shift) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateShiftIntoDatabase(id: Long, shift: Shift): Boolean {
|
||||||
|
return legacyDatabase.updateShiftDataIntoDatabase(
|
||||||
|
id = id,
|
||||||
|
typeString = shift.type.type,
|
||||||
|
descriptionString = shift.description,
|
||||||
|
dateString = shift.date,
|
||||||
|
timeInString = shift.timeIn ?: "",
|
||||||
|
timeOutString = shift.timeOut ?: "",
|
||||||
|
duration = shift.duration ?: 0f,
|
||||||
|
breaks = shift.breakMins ?: 0,
|
||||||
|
units = shift.units ?: 0f,
|
||||||
|
payRate = shift.rateOfPay,
|
||||||
|
totalPay = shift.totalPay
|
||||||
|
) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readShiftsFromDatabase(): List<ShiftObject>? {
|
||||||
|
return legacyDatabase.readShiftsFromDatabase()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readSingleShiftFromDatabase(id: Long): ShiftObject? {
|
||||||
|
return legacyDatabase.readSingleShiftWithId(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteSingleShiftFromDatabase(id: Long): Boolean {
|
||||||
|
return legacyDatabase.deleteSingleShift(id) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteAllShiftsFromDatabase(): Boolean {
|
||||||
|
return legacyDatabase.deleteAllShiftsInDatabase() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retrieveSortAndOrderFromPref(): Pair<Sortable?, Order?> {
|
||||||
|
return preferenceProvider.getSortableAndOrder()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSortAndOrderFromPref(sortable: Sortable, order: Order) {
|
||||||
|
preferenceProvider.saveSortableAndOrder(sortable, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retrieveFilteringDetailsInPrefs(): Map<String, String?> {
|
||||||
|
return preferenceProvider.getFilteringDetails()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setFilteringDetailsInPrefs(
|
||||||
|
description: String?,
|
||||||
|
timeIn: String?,
|
||||||
|
timeOut: String?,
|
||||||
|
type: String?
|
||||||
|
) {
|
||||||
|
preferenceProvider.saveFilteringDetails(description, timeIn, timeOut, type)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package com.appttude.h_mal.farmr.data.legacydb
|
||||||
|
|
||||||
|
import android.content.ContentUris
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.net.Uri
|
||||||
|
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.CONTENT_URI
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry._ID
|
||||||
|
import com.appttude.h_mal.farmr.model.Shift
|
||||||
|
import com.appttude.h_mal.farmr.model.ShiftType
|
||||||
|
|
||||||
|
class LegacyDatabase(context: Context) {
|
||||||
|
private val resolver = context.contentResolver
|
||||||
|
|
||||||
|
private val projection = arrayOf<String?>(
|
||||||
|
_ID,
|
||||||
|
COLUMN_SHIFT_DESCRIPTION,
|
||||||
|
COLUMN_SHIFT_DATE,
|
||||||
|
COLUMN_SHIFT_TIME_IN,
|
||||||
|
COLUMN_SHIFT_TIME_OUT,
|
||||||
|
COLUMN_SHIFT_BREAK,
|
||||||
|
COLUMN_SHIFT_DURATION,
|
||||||
|
COLUMN_SHIFT_TYPE,
|
||||||
|
COLUMN_SHIFT_UNIT,
|
||||||
|
COLUMN_SHIFT_PAYRATE,
|
||||||
|
COLUMN_SHIFT_TOTALPAY
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create
|
||||||
|
fun insertShiftDataIntoDatabase(
|
||||||
|
shift: Shift
|
||||||
|
): Uri? {
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(COLUMN_SHIFT_TYPE, shift.type.type)
|
||||||
|
put(COLUMN_SHIFT_DESCRIPTION, shift.description)
|
||||||
|
put(COLUMN_SHIFT_DATE, shift.description)
|
||||||
|
put(COLUMN_SHIFT_TIME_IN, shift.timeIn ?: "00:00")
|
||||||
|
put(COLUMN_SHIFT_TIME_OUT, shift.timeOut ?: "00:00")
|
||||||
|
put(COLUMN_SHIFT_DURATION, shift.duration ?: 0.00f)
|
||||||
|
put(COLUMN_SHIFT_BREAK, shift.breakMins ?: 0)
|
||||||
|
put(COLUMN_SHIFT_UNIT, shift.units ?: 0.00f)
|
||||||
|
put(COLUMN_SHIFT_PAYRATE, shift.rateOfPay)
|
||||||
|
put(COLUMN_SHIFT_TOTALPAY, shift.totalPay)
|
||||||
|
}
|
||||||
|
return resolver.insert(CONTENT_URI, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read
|
||||||
|
fun readShiftsFromDatabase(): List<ShiftObject>? {
|
||||||
|
val cursor = resolver.query(
|
||||||
|
CONTENT_URI,
|
||||||
|
projection,
|
||||||
|
null, null, null
|
||||||
|
) ?: return null
|
||||||
|
cursor.moveToFirst()
|
||||||
|
val shifts = (0..cursor.count).map { cursor.getShift() }
|
||||||
|
// close cursor after query operations
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
return shifts
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readSingleShiftWithId(id: Long): ShiftObject? {
|
||||||
|
val itemUri: Uri = ContentUris.withAppendedId(CONTENT_URI, id)
|
||||||
|
|
||||||
|
val cursor = resolver.query(
|
||||||
|
itemUri,
|
||||||
|
projection,
|
||||||
|
null, null, null
|
||||||
|
) ?: return null
|
||||||
|
cursor.moveToFirst()
|
||||||
|
|
||||||
|
val shift = cursor.takeIf { it.moveToFirst() }?.run { getShift() } ?: return null
|
||||||
|
cursor.close()
|
||||||
|
return shift
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update
|
||||||
|
fun updateShiftDataIntoDatabase(
|
||||||
|
id: Long,
|
||||||
|
typeString: String,
|
||||||
|
descriptionString: String,
|
||||||
|
dateString: String,
|
||||||
|
timeInString: String,
|
||||||
|
timeOutString: String,
|
||||||
|
duration: Float,
|
||||||
|
breaks: Int,
|
||||||
|
units: Float,
|
||||||
|
payRate: Float,
|
||||||
|
totalPay: Float,
|
||||||
|
): Int {
|
||||||
|
val itemUri: Uri = ContentUris.withAppendedId(CONTENT_URI, id)
|
||||||
|
|
||||||
|
val values = ContentValues().apply {
|
||||||
|
put(COLUMN_SHIFT_TYPE, typeString)
|
||||||
|
put(COLUMN_SHIFT_DESCRIPTION, descriptionString)
|
||||||
|
put(COLUMN_SHIFT_DATE, dateString)
|
||||||
|
put(COLUMN_SHIFT_TIME_IN, timeInString)
|
||||||
|
put(COLUMN_SHIFT_TIME_OUT, timeOutString)
|
||||||
|
put(COLUMN_SHIFT_DURATION, duration)
|
||||||
|
put(COLUMN_SHIFT_BREAK, breaks)
|
||||||
|
put(COLUMN_SHIFT_UNIT, units)
|
||||||
|
put(COLUMN_SHIFT_PAYRATE, payRate)
|
||||||
|
put(COLUMN_SHIFT_TOTALPAY, totalPay)
|
||||||
|
}
|
||||||
|
return resolver.update(itemUri, values, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
fun deleteAllShiftsInDatabase(): Int {
|
||||||
|
return resolver.delete(CONTENT_URI, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteSingleShift(id: Long): Int {
|
||||||
|
val args: Array<String> = arrayOf(id.toString())
|
||||||
|
return resolver.delete(CONTENT_URI, "$_ID=?", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Cursor.getShift(): ShiftObject = run {
|
||||||
|
val id = getLong(getColumnIndexOrThrow(_ID))
|
||||||
|
val descriptionColumnIndex = getString(
|
||||||
|
getColumnIndexOrThrow(
|
||||||
|
COLUMN_SHIFT_DESCRIPTION
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val dateColumnIndex = getString(getColumnIndexOrThrow(COLUMN_SHIFT_DATE))
|
||||||
|
val timeInColumnIndex =
|
||||||
|
getString(getColumnIndexOrThrow(COLUMN_SHIFT_TIME_IN))
|
||||||
|
val timeOutColumnIndex =
|
||||||
|
getString(getColumnIndexOrThrow(COLUMN_SHIFT_TIME_OUT))
|
||||||
|
val durationColumnIndex =
|
||||||
|
getFloat(getColumnIndexOrThrow(COLUMN_SHIFT_DURATION))
|
||||||
|
val breakOutColumnIndex =
|
||||||
|
getInt(getColumnIndexOrThrow(COLUMN_SHIFT_BREAK))
|
||||||
|
val typeColumnIndex = getString(getColumnIndexOrThrow(COLUMN_SHIFT_TYPE))
|
||||||
|
val unitColumnIndex = getFloat(getColumnIndexOrThrow(COLUMN_SHIFT_UNIT))
|
||||||
|
val payrateColumnIndex =
|
||||||
|
getFloat(getColumnIndexOrThrow(COLUMN_SHIFT_PAYRATE))
|
||||||
|
val totalpayColumnIndex =
|
||||||
|
getFloat(getColumnIndexOrThrow(COLUMN_SHIFT_TOTALPAY))
|
||||||
|
|
||||||
|
ShiftObject(
|
||||||
|
id = id,
|
||||||
|
type = typeColumnIndex,
|
||||||
|
description = descriptionColumnIndex,
|
||||||
|
date = dateColumnIndex,
|
||||||
|
timeIn = timeInColumnIndex,
|
||||||
|
timeOut = timeOutColumnIndex,
|
||||||
|
duration = durationColumnIndex,
|
||||||
|
breakMins = breakOutColumnIndex,
|
||||||
|
units = unitColumnIndex,
|
||||||
|
rateOfPay = payrateColumnIndex,
|
||||||
|
totalPay = totalpayColumnIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.appttude.h_mal.farmr.data.legacydb
|
||||||
|
|
||||||
|
import com.appttude.h_mal.farmr.model.Shift
|
||||||
|
import com.appttude.h_mal.farmr.model.ShiftType
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
data class ShiftObject(
|
||||||
|
val id: Long,
|
||||||
|
val type: String,
|
||||||
|
val description: String,
|
||||||
|
val date: String,
|
||||||
|
val timeIn: String,
|
||||||
|
val timeOut: String,
|
||||||
|
val duration: Float,
|
||||||
|
val breakMins: Int,
|
||||||
|
val units: Float,
|
||||||
|
val rateOfPay: Float,
|
||||||
|
val totalPay: Float
|
||||||
|
) {
|
||||||
|
fun copyToShift() = Shift(ShiftType.getEnumByType(type), description, date, timeIn, timeOut, duration, breakMins, units, rateOfPay, totalPay)
|
||||||
|
|
||||||
|
fun getHoursMinutesPairFromDuration(): Pair<String, String> {
|
||||||
|
val hours: Int = floor(duration).toInt()
|
||||||
|
val minutes: Int = ((duration - hours) * 60).toInt()
|
||||||
|
return Pair(hours.toString(), minutes.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
package com.appttude.h_mal.farmr.data
|
package com.appttude.h_mal.farmr.data.legacydb
|
||||||
|
|
||||||
import android.content.ContentProvider
|
import android.content.ContentProvider
|
||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
|
||||||
import android.content.UriMatcher
|
import android.content.UriMatcher
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.VisibleForTesting
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by h_mal on 26/12/2017.
|
* Created by h_mal on 26/12/2017.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.appttude.h_mal.farmr.data
|
package com.appttude.h_mal.farmr.data.legacydb
|
||||||
|
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.appttude.h_mal.farmr.data
|
package com.appttude.h_mal.farmr.data.legacydb
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
import com.appttude.h_mal.farmr.data.ShiftsContract.ShiftsEntry
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by h_mal on 26/12/2017.
|
* Created by h_mal on 26/12/2017.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.appttude.h_mal.farmr.data.prefs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.appttude.h_mal.farmr.model.Order
|
||||||
|
import com.appttude.h_mal.farmr.model.Sortable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared preferences to save & load last timestamp
|
||||||
|
*/
|
||||||
|
const val SORT = "SORT"
|
||||||
|
const val ORDER = "ORDER"
|
||||||
|
|
||||||
|
const val DESCRIPTION = "DESCRIPTION"
|
||||||
|
const val TIME_IN = "TIME_IN"
|
||||||
|
const val TIME_OUT = "TIME_OUT"
|
||||||
|
const val TYPE = "TYPE"
|
||||||
|
|
||||||
|
class PreferenceProvider(
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val appContext = context.applicationContext
|
||||||
|
|
||||||
|
private val preference: SharedPreferences
|
||||||
|
get() = PreferenceManager.getDefaultSharedPreferences(appContext)
|
||||||
|
|
||||||
|
fun saveSortableAndOrder(sortable: Sortable, order: Order) {
|
||||||
|
preference.edit()
|
||||||
|
.putString(SORT, sortable.label)
|
||||||
|
.putString(ORDER, order.label)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSortableAndOrder(): Pair<Sortable?, Order?> {
|
||||||
|
val sort = preference.getString(SORT, null)?.let { Sortable.valueOf(it) }
|
||||||
|
val order = preference.getString(ORDER, null)?.let { Order.valueOf(it) }
|
||||||
|
|
||||||
|
return Pair(sort, order)
|
||||||
|
}
|
||||||
|
fun saveFilteringDetails(
|
||||||
|
description: String?,
|
||||||
|
timeIn: String?,
|
||||||
|
timeOut: String?,
|
||||||
|
type: String?
|
||||||
|
) {
|
||||||
|
preference.edit()
|
||||||
|
.putString(DESCRIPTION, description)
|
||||||
|
.putString(TIME_IN, timeIn)
|
||||||
|
.putString(TIME_OUT, timeOut)
|
||||||
|
.putString(TYPE, type)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFilteringDetails(): Map<String, String?> {
|
||||||
|
return mapOf(
|
||||||
|
Pair(DESCRIPTION, preference.getString(DESCRIPTION, null)),
|
||||||
|
Pair(TIME_IN, preference.getString(TIME_IN, null)),
|
||||||
|
Pair(TIME_OUT, preference.getString(TIME_OUT, null)),
|
||||||
|
Pair(TYPE, preference.getString(TYPE, null))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.appttude.h_mal.farmr.di
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import com.appttude.h_mal.farmr.data.RepositoryImpl
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.LegacyDatabase
|
||||||
|
import com.appttude.h_mal.farmr.data.prefs.PreferenceProvider
|
||||||
|
import com.appttude.h_mal.farmr.viewmodel.ApplicationViewModelFactory
|
||||||
|
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
||||||
|
import org.kodein.di.Kodein
|
||||||
|
import org.kodein.di.KodeinAware
|
||||||
|
import org.kodein.di.android.x.androidXModule
|
||||||
|
import org.kodein.di.generic.bind
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
import org.kodein.di.generic.provider
|
||||||
|
import org.kodein.di.generic.singleton
|
||||||
|
|
||||||
|
class ShiftApplication: Application(), KodeinAware {
|
||||||
|
// Kodein creation of modules to be retrieve within the app
|
||||||
|
override val kodein = Kodein.lazy {
|
||||||
|
import(androidXModule(this@ShiftApplication))
|
||||||
|
|
||||||
|
bind() from singleton { LegacyDatabase(this@ShiftApplication) }
|
||||||
|
bind() from singleton { PreferenceProvider(this@ShiftApplication) }
|
||||||
|
bind() from singleton { RepositoryImpl(instance(), instance()) }
|
||||||
|
|
||||||
|
bind() from provider { ApplicationViewModelFactory(instance()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.appttude.h_mal.farmr.model
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class EntityItem(
|
||||||
|
@PrimaryKey(autoGenerate = false)
|
||||||
|
val id: String,
|
||||||
|
val shift: Shift
|
||||||
|
)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.appttude.h_mal.farmr.model
|
||||||
|
|
||||||
|
data class FilterStore(
|
||||||
|
val description: String?,
|
||||||
|
val dateFrom: String?,
|
||||||
|
val dateTo: String?,
|
||||||
|
val type: String?
|
||||||
|
)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.appttude.h_mal.farmr.model
|
||||||
|
|
||||||
|
enum class Order(val label: String) {
|
||||||
|
ASCENDING("Ascending"), DESCENDING("Descending")
|
||||||
|
}
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
package com.appttude.h_mal.farmr.model
|
package com.appttude.h_mal.farmr.model
|
||||||
|
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
|
||||||
|
import com.appttude.h_mal.farmr.utils.calculateDuration
|
||||||
|
import com.appttude.h_mal.farmr.utils.convertTimeStringToHourMinutesPair
|
||||||
|
import com.appttude.h_mal.farmr.utils.formatToTwoDp
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
data class Shift(
|
data class Shift(
|
||||||
val type: ShiftType,
|
val type: ShiftType,
|
||||||
val description: String,
|
val description: String,
|
||||||
@@ -11,4 +17,52 @@ data class Shift(
|
|||||||
val units: Float?,
|
val units: Float?,
|
||||||
val rateOfPay: Float,
|
val rateOfPay: Float,
|
||||||
val totalPay: Float
|
val totalPay: Float
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
// Invocation for Hourly
|
||||||
|
operator fun invoke(
|
||||||
|
description: String,
|
||||||
|
date: String,
|
||||||
|
timeIn: String,
|
||||||
|
timeOut: String,
|
||||||
|
breakMins: Int? = null,
|
||||||
|
rateOfPay: Float
|
||||||
|
): Shift {
|
||||||
|
val breakTime = breakMins ?: 0
|
||||||
|
val duration = calculateDuration(timeIn, timeOut, breakTime)
|
||||||
|
|
||||||
|
return Shift(
|
||||||
|
ShiftType.HOURLY,
|
||||||
|
description,
|
||||||
|
date,
|
||||||
|
timeIn,
|
||||||
|
timeOut,
|
||||||
|
duration,
|
||||||
|
breakTime,
|
||||||
|
duration,
|
||||||
|
rateOfPay,
|
||||||
|
(duration * rateOfPay).formatToTwoDp()
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun invoke(
|
||||||
|
description: String,
|
||||||
|
date: String,
|
||||||
|
units: Float,
|
||||||
|
rateOfPay: Float
|
||||||
|
) = Shift(
|
||||||
|
ShiftType.PIECE,
|
||||||
|
description,
|
||||||
|
date,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
0f,
|
||||||
|
0,
|
||||||
|
units,
|
||||||
|
rateOfPay,
|
||||||
|
(units * rateOfPay).formatToTwoDp()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,5 +2,15 @@ package com.appttude.h_mal.farmr.model
|
|||||||
|
|
||||||
enum class ShiftType(val type: String){
|
enum class ShiftType(val type: String){
|
||||||
HOURLY("Hourly"),
|
HOURLY("Hourly"),
|
||||||
PIECE("Piece Rate")
|
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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
11
app/src/main/java/com/appttude/h_mal/farmr/model/Sortable.kt
Normal file
11
app/src/main/java/com/appttude/h_mal/farmr/model/Sortable.kt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package com.appttude.h_mal.farmr.model
|
||||||
|
|
||||||
|
enum class Sortable(val label: String) {
|
||||||
|
ID("Default"),
|
||||||
|
TYPE("Shift Type"),
|
||||||
|
DATE("Date"),
|
||||||
|
DESCRIPTION("Description"),
|
||||||
|
DURATION("Added"), UNITS("Duration"),
|
||||||
|
RATEOFPAY("Rate of pay"),
|
||||||
|
TOTALPAY("Total Pay")
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.appttude.h_mal.farmr.model
|
||||||
|
|
||||||
|
sealed class ViewState {
|
||||||
|
object HasStarted : ViewState()
|
||||||
|
class HasData<T : Any>(val data: T) : ViewState()
|
||||||
|
class HasError<T : Any>(val error: T) : ViewState()
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.appttude.h_mal.farmr.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.OnClickListener
|
||||||
|
import android.widget.AdapterView
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.Spinner
|
||||||
|
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.utils.setDatePicker
|
||||||
|
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
||||||
|
|
||||||
|
class FilterDataFragment : BaseFragment<MainViewModel>(R.layout.fragment_filter_data),
|
||||||
|
AdapterView.OnItemSelectedListener, OnClickListener {
|
||||||
|
private val spinnerList: Array<String> =
|
||||||
|
arrayOf("", ShiftType.HOURLY.type, ShiftType.PIECE.type)
|
||||||
|
|
||||||
|
private lateinit var LocationET: EditText
|
||||||
|
private lateinit var dateFromET: EditText
|
||||||
|
private lateinit var dateToET: EditText
|
||||||
|
private lateinit var typeSpinner: Spinner
|
||||||
|
|
||||||
|
private var description: String? = null
|
||||||
|
private var dateFrom: String? = null
|
||||||
|
private var dateTo: String? = null
|
||||||
|
private var type: String? = null
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
setTitle(getString(R.string.title_activity_filter_data))
|
||||||
|
|
||||||
|
LocationET = view.findViewById(R.id.filterLocationEditText)
|
||||||
|
dateFromET = view.findViewById(R.id.fromdateInEditText)
|
||||||
|
dateToET = view.findViewById(R.id.filterDateOutEditText)
|
||||||
|
typeSpinner = view.findViewById(R.id.TypeFilterEditText)
|
||||||
|
val submit: Button = view.findViewById(R.id.submitFiltered)
|
||||||
|
|
||||||
|
val adapter: ArrayAdapter<String> =
|
||||||
|
ArrayAdapter((context)!!, android.R.layout.simple_spinner_dropdown_item, spinnerList)
|
||||||
|
typeSpinner.adapter = adapter
|
||||||
|
|
||||||
|
val filterDetails = viewModel.getFiltrationDetails()
|
||||||
|
|
||||||
|
filterDetails.let {
|
||||||
|
LocationET.setText(it.description)
|
||||||
|
dateFromET.setText(it.dateFrom)
|
||||||
|
dateToET.setText(it.dateTo)
|
||||||
|
|
||||||
|
it.type?.let { t ->
|
||||||
|
val spinnerPosition: Int = adapter.getPosition(t)
|
||||||
|
typeSpinner.setSelection(spinnerPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationET.doAfterTextChanged { description = it.toString() }
|
||||||
|
dateFromET.setDatePicker { dateFrom = it }
|
||||||
|
dateToET.setDatePicker { dateTo = it }
|
||||||
|
typeSpinner.onItemSelectedListener = this
|
||||||
|
|
||||||
|
submit.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemSelected(
|
||||||
|
parentView: AdapterView<*>?,
|
||||||
|
selectedItemView: View?,
|
||||||
|
position: Int,
|
||||||
|
id: Long
|
||||||
|
) {
|
||||||
|
type = when (position) {
|
||||||
|
1 -> ShiftType.HOURLY.toString()
|
||||||
|
2 -> ShiftType.PIECE.toString()
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNothingSelected(parentView: AdapterView<*>?) {}
|
||||||
|
|
||||||
|
private fun submitFiltrationDetails() {
|
||||||
|
viewModel.setFiltrationDetails(description, dateFrom, dateTo, type)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(p0: View?) {
|
||||||
|
submitFiltrationDetails()
|
||||||
|
}
|
||||||
|
}
|
||||||
229
app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentAddItem.kt
Normal file
229
app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentAddItem.kt
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
package com.appttude.h_mal.farmr.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.RadioButton
|
||||||
|
import android.widget.RadioGroup
|
||||||
|
import android.widget.ScrollView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.widget.doAfterTextChanged
|
||||||
|
import com.appttude.h_mal.farmr.R
|
||||||
|
import com.appttude.h_mal.farmr.base.BackPressedListener
|
||||||
|
import com.appttude.h_mal.farmr.base.BaseFragment
|
||||||
|
import com.appttude.h_mal.farmr.model.ShiftType
|
||||||
|
import com.appttude.h_mal.farmr.utils.ID
|
||||||
|
import com.appttude.h_mal.farmr.utils.createDialog
|
||||||
|
import com.appttude.h_mal.farmr.utils.formatToTwoDpString
|
||||||
|
import com.appttude.h_mal.farmr.utils.hide
|
||||||
|
import com.appttude.h_mal.farmr.utils.popBackStack
|
||||||
|
import com.appttude.h_mal.farmr.utils.setDatePicker
|
||||||
|
import com.appttude.h_mal.farmr.utils.setTimePicker
|
||||||
|
import com.appttude.h_mal.farmr.utils.show
|
||||||
|
import com.appttude.h_mal.farmr.utils.validateField
|
||||||
|
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
||||||
|
|
||||||
|
class FragmentAddItem : BaseFragment<MainViewModel>(R.layout.fragment_add_item),
|
||||||
|
RadioGroup.OnCheckedChangeListener, BackPressedListener {
|
||||||
|
|
||||||
|
private lateinit var mHourlyRadioButton: RadioButton
|
||||||
|
private lateinit var mPieceRadioButton: RadioButton
|
||||||
|
private lateinit var mLocationEditText: EditText
|
||||||
|
private lateinit var mDateEditText: EditText
|
||||||
|
private lateinit var mDurationTextView: TextView
|
||||||
|
private lateinit var mTimeInEditText: EditText
|
||||||
|
private lateinit var mTimeOutEditText: EditText
|
||||||
|
private lateinit var mBreakEditText: EditText
|
||||||
|
private lateinit var mUnitEditText: EditText
|
||||||
|
private lateinit var mPayRateEditText: EditText
|
||||||
|
private lateinit var mTotalPayTextView: TextView
|
||||||
|
private lateinit var hourlyDataView: LinearLayout
|
||||||
|
private lateinit var unitsHolder: LinearLayout
|
||||||
|
private lateinit var durationHolder: LinearLayout
|
||||||
|
private lateinit var wholeView: LinearLayout
|
||||||
|
private lateinit var scrollView: ScrollView
|
||||||
|
private lateinit var submitProduct: Button
|
||||||
|
private lateinit var mRadioGroup: RadioGroup
|
||||||
|
|
||||||
|
private var mDate: String? = null
|
||||||
|
private var mDescription: String? = null
|
||||||
|
private var mTimeIn: String? = null
|
||||||
|
private var mTimeOut: String? = null
|
||||||
|
private var mBreaks = 0
|
||||||
|
private var mUnits = 0f
|
||||||
|
private var mPayRate = 0f
|
||||||
|
private var mType: ShiftType? = null
|
||||||
|
private var mDuration: Float = 0f
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
scrollView = view.findViewById(R.id.total_view)
|
||||||
|
mRadioGroup = view.findViewById(R.id.rg)
|
||||||
|
mHourlyRadioButton = view.findViewById(R.id.hourly)
|
||||||
|
mPieceRadioButton = view.findViewById(R.id.piecerate)
|
||||||
|
mLocationEditText = view.findViewById(R.id.locationEditText)
|
||||||
|
mDateEditText = view.findViewById(R.id.dateEditText)
|
||||||
|
mTimeInEditText = view.findViewById(R.id.timeInEditText)
|
||||||
|
mBreakEditText = view.findViewById(R.id.breakEditText)
|
||||||
|
mTimeOutEditText = view.findViewById(R.id.timeOutEditText)
|
||||||
|
mDurationTextView = view.findViewById(R.id.ShiftDuration)
|
||||||
|
mUnitEditText = view.findViewById(R.id.unitET)
|
||||||
|
mPayRateEditText = view.findViewById(R.id.payrateET)
|
||||||
|
mTotalPayTextView = view.findViewById(R.id.totalpayval)
|
||||||
|
hourlyDataView = view.findViewById(R.id.hourly_data_holder)
|
||||||
|
unitsHolder = view.findViewById(R.id.units_holder)
|
||||||
|
durationHolder = view.findViewById(R.id.duration_holder)
|
||||||
|
wholeView = view.findViewById(R.id.whole_view)
|
||||||
|
submitProduct = view.findViewById(R.id.submit)
|
||||||
|
|
||||||
|
mRadioGroup.setOnCheckedChangeListener(this)
|
||||||
|
mLocationEditText.doAfterTextChanged {
|
||||||
|
mDescription = it.toString()
|
||||||
|
}
|
||||||
|
mDateEditText.setDatePicker { mDate = it }
|
||||||
|
mTimeInEditText.setTimePicker {
|
||||||
|
mTimeIn = it
|
||||||
|
calculateTotalPay()
|
||||||
|
}
|
||||||
|
mTimeOutEditText.setTimePicker {
|
||||||
|
mTimeOut = it
|
||||||
|
calculateTotalPay()
|
||||||
|
}
|
||||||
|
mBreakEditText.doAfterTextChanged {
|
||||||
|
mBreaks = it.toString().toIntOrNull() ?: 0
|
||||||
|
calculateTotalPay()
|
||||||
|
}
|
||||||
|
mUnitEditText.doAfterTextChanged {
|
||||||
|
it.toString().toFloatOrNull()?.let { u -> mPayRate = u }
|
||||||
|
calculateTotalPay()
|
||||||
|
}
|
||||||
|
mPayRateEditText.doAfterTextChanged {
|
||||||
|
it.toString().toFloatOrNull()?.let { p ->
|
||||||
|
mPayRate = p
|
||||||
|
calculateTotalPay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submitProduct.setOnClickListener { submitShift() }
|
||||||
|
|
||||||
|
setupViewAfterViewCreated()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupViewAfterViewCreated() {
|
||||||
|
val title = when (arguments?.containsKey(ID)) {
|
||||||
|
true -> {
|
||||||
|
// Since we are editing a shift lets load the shift data into the views
|
||||||
|
viewModel.getCurrentShift(arguments!!.getLong(ID))?.run {
|
||||||
|
mLocationEditText.setText(description)
|
||||||
|
mDateEditText.setText(date)
|
||||||
|
when (ShiftType.getEnumByType(type)) {
|
||||||
|
ShiftType.HOURLY -> {
|
||||||
|
mHourlyRadioButton.isChecked = true
|
||||||
|
mPieceRadioButton.isChecked = false
|
||||||
|
mTimeInEditText.setText(timeIn)
|
||||||
|
mTimeOutEditText.setText(timeOut)
|
||||||
|
mBreakEditText.setText(breakMins.toString())
|
||||||
|
val durationText = "${duration.formatToTwoDpString()} Hours"
|
||||||
|
mDurationTextView.text = durationText
|
||||||
|
}
|
||||||
|
|
||||||
|
ShiftType.PIECE -> {
|
||||||
|
mHourlyRadioButton.isChecked = false
|
||||||
|
mPieceRadioButton.isChecked = true
|
||||||
|
mUnitEditText.setText(units.formatToTwoDpString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mPayRateEditText.setText(rateOfPay.formatToTwoDpString())
|
||||||
|
mTotalPayTextView.text = totalPay.formatToTwoDpString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return title
|
||||||
|
getString(R.string.edit_item_title)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> getString(R.string.add_item_title)
|
||||||
|
}
|
||||||
|
setTitle(title)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCheckedChanged(radioGroup: RadioGroup, id: Int) {
|
||||||
|
when (radioGroup.checkedRadioButtonId) {
|
||||||
|
R.id.hourly -> {
|
||||||
|
mType = ShiftType.HOURLY
|
||||||
|
wholeView.show()
|
||||||
|
unitsHolder.hide()
|
||||||
|
hourlyDataView.show()
|
||||||
|
durationHolder.show()
|
||||||
|
}
|
||||||
|
R.id.piecerate -> {
|
||||||
|
mType = ShiftType.PIECE
|
||||||
|
wholeView.show()
|
||||||
|
unitsHolder.show()
|
||||||
|
hourlyDataView.hide()
|
||||||
|
durationHolder.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun submitShift() {
|
||||||
|
mDate.validateField({ !it.isNullOrBlank() }){
|
||||||
|
onFailure("Date field cannot be empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mDescription.validateField({ !it.isNullOrBlank() }){
|
||||||
|
onFailure("Description field cannot be empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mPayRate.validateField({ !it.isNaN() }){
|
||||||
|
onFailure("Rate of pay field cannot be empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPieceRadioButton.isChecked) {
|
||||||
|
mUnits.validateField({!it.isNaN()}) {
|
||||||
|
onFailure("Units field cannot be empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
viewModel.insertPieceRateShift(mDescription!!, mDate!!, mUnits, mPayRate)
|
||||||
|
} else if (mHourlyRadioButton.isChecked) {
|
||||||
|
viewModel.insertHourlyShift(mDescription!!, mDate!!, mPayRate, mTimeIn, mTimeOut, mBreaks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculateTotalPay() {
|
||||||
|
mType?.let {
|
||||||
|
val total = when (it) {
|
||||||
|
ShiftType.HOURLY -> {
|
||||||
|
// Calculate duration before total pay calculation
|
||||||
|
mDuration = viewModel.retrieveDurationText(mTimeIn, mTimeOut, mBreaks) ?: return
|
||||||
|
mDurationTextView.text = StringBuilder().append(mDuration).append(" hours").toString()
|
||||||
|
mDuration * mPayRate
|
||||||
|
}
|
||||||
|
ShiftType.PIECE -> {
|
||||||
|
mUnits * mPayRate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTotalPayTextView.text = total.formatToTwoDpString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean {
|
||||||
|
if (mRadioGroup.checkedRadioButtonId == -1) {
|
||||||
|
mActivity?.popBackStack()
|
||||||
|
} else {
|
||||||
|
requireContext().createDialog(
|
||||||
|
title = "Discard Changes?",
|
||||||
|
message = "Are you sure you want to discard changes?",
|
||||||
|
displayCancel = true,
|
||||||
|
okCallback = { _, _ ->
|
||||||
|
mActivity?.popBackStack()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
367
app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentMain.kt
Normal file
367
app/src/main/java/com/appttude/h_mal/farmr/ui/FragmentMain.kt
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
package com.appttude.h_mal.farmr.ui
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
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.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 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: ShiftRecyclerAdapter
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
// Inflate the layout for this fragment
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
mAdapter = ShiftRecyclerAdapter(this) {
|
||||||
|
viewModel.deleteShift(it)
|
||||||
|
}
|
||||||
|
productListView = view.findViewById(R.id.list_item_view)
|
||||||
|
productListView.adapter = mAdapter
|
||||||
|
|
||||||
|
view.findViewById<FloatingActionButton>(R.id.fab1).setOnClickListener {
|
||||||
|
navigateToFragment(FragmentAddItem(), name = "additem")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(data: Any?) {
|
||||||
|
super.onSuccess(data)
|
||||||
|
if (data is List<*>) {
|
||||||
|
mAdapter.updateData(data as List<ShiftObject>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.delete_all -> {
|
||||||
|
deleteAllProducts()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.help -> {
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle("Help & Support:")
|
||||||
|
.setView(R.layout.dialog_layout)
|
||||||
|
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> arg0.dismiss() }
|
||||||
|
.create().show()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.filter_data -> {
|
||||||
|
// val fragmentTransaction: FragmentTransaction =
|
||||||
|
// activity.fragmentManager!!.beginTransaction()
|
||||||
|
// fragmentTransaction.replace(R.id.container, FilterDataFragment())
|
||||||
|
// .addToBackStack("filterdata").commit()
|
||||||
|
// Todo: filter shift
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.sort_data -> {
|
||||||
|
sortData()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.clear_filter -> {
|
||||||
|
// Todo: Apply filter to list
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.export_data -> {
|
||||||
|
if (checkStoragePermissions(activity)) {
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle("Export?")
|
||||||
|
.setMessage("Exporting current filtered data. Continue?")
|
||||||
|
.setNegativeButton(android.R.string.no, null)
|
||||||
|
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> ExportData() }
|
||||||
|
.create().show()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "Storage permissions required", Toast.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_favorite -> {
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle("Info:")
|
||||||
|
.setMessage(viewModel.getInformation())
|
||||||
|
.setPositiveButton(android.R.string.yes) { arg0, arg1 ->
|
||||||
|
arg0.dismiss()
|
||||||
|
}.create().show()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sortData() {
|
||||||
|
val groupName = Sortable.entries.map { it.label }.toTypedArray()
|
||||||
|
var sort = Sortable.ID
|
||||||
|
|
||||||
|
val sortAndOrder = viewModel.getSortAndOrder()
|
||||||
|
val checkedItem = Sortable.values().indexOf(sortAndOrder.first)
|
||||||
|
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle("Sort by:")
|
||||||
|
.setSingleChoiceItems(
|
||||||
|
groupName,
|
||||||
|
checkedItem
|
||||||
|
) { p0, p1 -> sort = Sortable.valueOf(groupName[p1]) }
|
||||||
|
.setPositiveButton("Ascending") { dialog, id ->
|
||||||
|
viewModel.setSortAndOrder(sort)
|
||||||
|
dialog.dismiss()
|
||||||
|
}.setNegativeButton("Descending") { dialog, id ->
|
||||||
|
viewModel.setSortAndOrder(sort, Order.DESCENDING)
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.create().show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteAllProducts() {
|
||||||
|
requireContext().createDialog(
|
||||||
|
"Warning",
|
||||||
|
message = "Are you sure you want to delete all date?",
|
||||||
|
displayCancel = true,
|
||||||
|
okCallback = { _, _ ->
|
||||||
|
viewModel.deleteAllShifts()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)"
|
||||||
|
}
|
||||||
|
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(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
println("request code$requestCode")
|
||||||
|
if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
|
||||||
|
if (grantResults.size > 0
|
||||||
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
exportDialog()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "Storage Permissions denied", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportDialog() {
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle("Export?")
|
||||||
|
.setMessage("Exporting current filtered data. Continue?")
|
||||||
|
.setNegativeButton(android.R.string.no, null)
|
||||||
|
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> ExportData() }.create().show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkStoragePermissions(activity: Activity?): Boolean {
|
||||||
|
var status = false
|
||||||
|
val permission = ActivityCompat.checkSelfPermission(
|
||||||
|
activity!!,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
if (permission == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
status = true
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean {
|
||||||
|
requireContext().createDialog(
|
||||||
|
title = "Leave?",
|
||||||
|
message = "Are you sure you want to exit Farmr?",
|
||||||
|
displayCancel = true,
|
||||||
|
okCallback = { _, _ ->
|
||||||
|
val intent = Intent(Intent.ACTION_MAIN)
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
|
intent.addCategory(Intent.CATEGORY_HOME)
|
||||||
|
startActivity(intent)
|
||||||
|
requireActivity().finish()
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package com.appttude.h_mal.farmr.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.appttude.h_mal.farmr.R
|
||||||
|
import com.appttude.h_mal.farmr.base.BaseFragment
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
|
||||||
|
import com.appttude.h_mal.farmr.model.ShiftType
|
||||||
|
import com.appttude.h_mal.farmr.utils.CURRENCY
|
||||||
|
import com.appttude.h_mal.farmr.utils.ID
|
||||||
|
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
|
||||||
|
|
||||||
|
class FurtherInfoFragment : BaseFragment<MainViewModel>(R.layout.fragment_futher_info) {
|
||||||
|
private lateinit var typeTV: TextView
|
||||||
|
private lateinit var descriptionTV: TextView
|
||||||
|
private lateinit var dateTV: TextView
|
||||||
|
private lateinit var times: TextView
|
||||||
|
private lateinit var breakTV: TextView
|
||||||
|
private lateinit var durationTV: TextView
|
||||||
|
private lateinit var unitsTV: TextView
|
||||||
|
private lateinit var payRateTV: TextView
|
||||||
|
private lateinit var totalPayTV: TextView
|
||||||
|
private lateinit var hourlyDetailHolder: LinearLayout
|
||||||
|
private lateinit var unitsHolder: LinearLayout
|
||||||
|
private lateinit var wholeView: LinearLayout
|
||||||
|
private lateinit var progressBarFI: ProgressBar
|
||||||
|
private lateinit var editButton: Button
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setTitle(getString(R.string.further_info_title))
|
||||||
|
|
||||||
|
progressBarFI = view.findViewById(R.id.progressBar_info)
|
||||||
|
wholeView = view.findViewById(R.id.further_info_view)
|
||||||
|
typeTV = view.findViewById(R.id.details_shift)
|
||||||
|
descriptionTV = view.findViewById(R.id.details_desc)
|
||||||
|
dateTV = view.findViewById(R.id.details_date)
|
||||||
|
times = view.findViewById(R.id.details_time)
|
||||||
|
breakTV = view.findViewById(R.id.details_breaks)
|
||||||
|
durationTV = view.findViewById(R.id.details_duration)
|
||||||
|
unitsTV = view.findViewById(R.id.details_units)
|
||||||
|
payRateTV = view.findViewById(R.id.details_pay_rate)
|
||||||
|
totalPayTV = view.findViewById(R.id.details_totalpay)
|
||||||
|
editButton = view.findViewById(R.id.details_edit)
|
||||||
|
hourlyDetailHolder = view.findViewById(R.id.details_hourly_details)
|
||||||
|
unitsHolder = view.findViewById(R.id.details_units_holder)
|
||||||
|
|
||||||
|
val id = arguments!!.getLong(ID)
|
||||||
|
|
||||||
|
editButton.setOnClickListener {
|
||||||
|
navigateToFragment(FragmentAddItem(), name = "additem", bundle = arguments!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
setupView(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupView(id: Long) {
|
||||||
|
viewModel.getCurrentShift(id)?.run {
|
||||||
|
typeTV.text = type
|
||||||
|
descriptionTV.text = description
|
||||||
|
dateTV.text = date
|
||||||
|
payRateTV.text = rateOfPay.toString()
|
||||||
|
totalPayTV.text = StringBuilder(CURRENCY).append(totalPay).toString()
|
||||||
|
|
||||||
|
when (ShiftType.getEnumByType(type)) {
|
||||||
|
ShiftType.HOURLY -> {
|
||||||
|
hourlyDetailHolder.show()
|
||||||
|
unitsHolder.hide()
|
||||||
|
times.text = StringBuilder(timeIn).append("-").append(timeOut).toString()
|
||||||
|
breakTV.text = StringBuilder(breakMins).append("mins").toString()
|
||||||
|
durationTV.text = buildDurationSummary(this)
|
||||||
|
val paymentSummary =
|
||||||
|
StringBuilder().append(duration).append(" Hours @ ").append(CURRENCY)
|
||||||
|
.append(rateOfPay).append(" per Hour").append("\n")
|
||||||
|
.append("Equals: ").append(CURRENCY).append(totalPay)
|
||||||
|
totalPayTV.text = paymentSummary
|
||||||
|
}
|
||||||
|
|
||||||
|
ShiftType.PIECE -> {
|
||||||
|
hourlyDetailHolder.hide()
|
||||||
|
unitsHolder.show()
|
||||||
|
unitsTV.text = units.toString()
|
||||||
|
|
||||||
|
val paymentSummary =
|
||||||
|
StringBuilder().append(units).append(" Units @ ").append(CURRENCY)
|
||||||
|
.append(rateOfPay).append(" per Unit").append("\n")
|
||||||
|
.append("Equals: ").append(CURRENCY).append(totalPay)
|
||||||
|
totalPayTV.text = paymentSummary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildDurationSummary(shiftObject: ShiftObject): String {
|
||||||
|
val time = shiftObject.getHoursMinutesPairFromDuration()
|
||||||
|
|
||||||
|
val stringBuilder = StringBuilder().append(time.first).append(" Hours ").append(time.second)
|
||||||
|
.append(" Minutes ")
|
||||||
|
if (shiftObject.breakMins > 0) {
|
||||||
|
stringBuilder.append(" (+ ").append(shiftObject.breakMins).append(" minutes break)")
|
||||||
|
}
|
||||||
|
return stringBuilder.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.appttude.h_mal.farmr.ui
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.R.string.cancel
|
||||||
|
import android.R.string.ok
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import com.appttude.h_mal.farmr.R
|
||||||
|
import com.appttude.h_mal.farmr.base.BackPressedListener
|
||||||
|
import com.appttude.h_mal.farmr.base.BaseActivity
|
||||||
|
import com.appttude.h_mal.farmr.utils.createDialog
|
||||||
|
import com.appttude.h_mal.farmr.utils.popBackStack
|
||||||
|
import com.appttude.h_mal.farmr.viewmodel.MainViewModel
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
class MainActivity : BaseActivity<MainViewModel>() {
|
||||||
|
private lateinit var toolbar: Toolbar
|
||||||
|
|
||||||
|
var selection: String? = null
|
||||||
|
var args: Array<String>? = null
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.main_view)
|
||||||
|
toolbar = findViewById(R.id.toolbar)
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
|
verifyStoragePermissions(this)
|
||||||
|
|
||||||
|
val fragmentTransaction = supportFragmentManager.beginTransaction()
|
||||||
|
fragmentTransaction.replace(R.id.container, FragmentMain()).addToBackStack("main").commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
menuInflater.inflate(R.menu.menu_main, menu)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
val currentFragment = supportFragmentManager.findFragmentById(R.id.container)
|
||||||
|
if (currentFragment is BackPressedListener) {
|
||||||
|
currentFragment.onBackPressed()
|
||||||
|
} else {
|
||||||
|
if (supportFragmentManager.backStackEntryCount > 1) {
|
||||||
|
popBackStack()
|
||||||
|
} else {
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setActionBarTitle(title: String?) {
|
||||||
|
toolbar.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage Permissions
|
||||||
|
private val REQUEST_EXTERNAL_STORAGE = 1
|
||||||
|
private val PERMISSIONS_STORAGE = arrayOf(
|
||||||
|
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the app has permission to write to device storage
|
||||||
|
*
|
||||||
|
* If the app does not has permission then the user will be prompted to grant permissions
|
||||||
|
*
|
||||||
|
* @param activity
|
||||||
|
*/
|
||||||
|
fun verifyStoragePermissions(activity: Activity?) {
|
||||||
|
// Check if we have write permission
|
||||||
|
val permission = ActivityCompat.checkSelfPermission(
|
||||||
|
activity!!,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// We don't have permission so prompt the user
|
||||||
|
ActivityCompat.requestPermissions(
|
||||||
|
activity,
|
||||||
|
PERMISSIONS_STORAGE,
|
||||||
|
REQUEST_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package com.appttude.h_mal.farmr.ui
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.appttude.h_mal.farmr.R
|
||||||
|
import com.appttude.h_mal.farmr.base.BaseRecyclerAdapter
|
||||||
|
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
|
||||||
|
import com.appttude.h_mal.farmr.model.ShiftType
|
||||||
|
import com.appttude.h_mal.farmr.utils.ID
|
||||||
|
import com.appttude.h_mal.farmr.utils.navigateToFragment
|
||||||
|
|
||||||
|
class ShiftRecyclerAdapter(
|
||||||
|
private val fragment: Fragment,
|
||||||
|
private val longPressCallback: (Long) -> Unit
|
||||||
|
) : BaseRecyclerAdapter<ShiftObject>(
|
||||||
|
emptyViewId = R.layout.empty_list_view,
|
||||||
|
currentViewId = R.layout.list_item_1
|
||||||
|
) {
|
||||||
|
override fun bindCurrentView(view: View, position: Int, data: ShiftObject) {
|
||||||
|
val descriptionTextView: TextView = view.findViewById(R.id.location)
|
||||||
|
val dateTextView: TextView = view.findViewById(R.id.date)
|
||||||
|
val totalPay: TextView = view.findViewById(R.id.total_pay)
|
||||||
|
val hoursView: TextView = view.findViewById(R.id.hours)
|
||||||
|
val h: TextView = view.findViewById(R.id.h)
|
||||||
|
val minutesView: TextView = view.findViewById(R.id.minutes)
|
||||||
|
val m: TextView = view.findViewById(R.id.m)
|
||||||
|
val editView: ImageView = view.findViewById(R.id.imageView)
|
||||||
|
h.text = "h"
|
||||||
|
m.text = "m"
|
||||||
|
val typeText: String = data.type
|
||||||
|
val descriptionText: String = data.description
|
||||||
|
val dateText: String = data.date
|
||||||
|
val totalPayText: String = data.totalPay.toString()
|
||||||
|
|
||||||
|
descriptionTextView.text = descriptionText
|
||||||
|
dateTextView.text = dateText
|
||||||
|
totalPay.text = totalPayText
|
||||||
|
|
||||||
|
when (ShiftType.getEnumByType(typeText)) {
|
||||||
|
ShiftType.HOURLY -> {
|
||||||
|
val time = data.getHoursMinutesPairFromDuration()
|
||||||
|
|
||||||
|
hoursView.text = time.first
|
||||||
|
minutesView.text = time.second
|
||||||
|
}
|
||||||
|
|
||||||
|
ShiftType.PIECE -> {
|
||||||
|
val unitsText: String = data.units.toString()
|
||||||
|
|
||||||
|
hoursView.text = unitsText
|
||||||
|
h.text = ""
|
||||||
|
minutesView.text = ""
|
||||||
|
m.text = "pcs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val b: Bundle = Bundle()
|
||||||
|
b.putLong(ID, data.id)
|
||||||
|
view.setOnClickListener {
|
||||||
|
// Navigate to further info
|
||||||
|
fragment.navigateToFragment(
|
||||||
|
FurtherInfoFragment(),
|
||||||
|
bundle = b,
|
||||||
|
name = "furtherinfo"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
editView.setOnClickListener {
|
||||||
|
// Navigate to edit
|
||||||
|
fragment.navigateToFragment(
|
||||||
|
FragmentAddItem(),
|
||||||
|
bundle = b,
|
||||||
|
name = "additem"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
view.setOnLongClickListener {
|
||||||
|
AlertDialog.Builder(it.context)
|
||||||
|
.setMessage("Are you sure you want to delete")
|
||||||
|
.setPositiveButton("delete") { dialog, id -> longPressCallback.invoke(data.id) }
|
||||||
|
.setNegativeButton("cancel") { dialog, id ->
|
||||||
|
dialog?.dismiss()
|
||||||
|
}
|
||||||
|
.create().show()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// override fun getItemId(position: Int): Long {
|
||||||
|
// return if (list.isNullOrEmpty()) {
|
||||||
|
// RecyclerView.NO_ID
|
||||||
|
// } else {
|
||||||
|
// list!![position].id
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun setHasStableIds(hasStableIds: Boolean) {
|
||||||
|
// super.setHasStableIds(true)
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.appttude.h_mal.farmr
|
package com.appttude.h_mal.farmr.ui
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -7,6 +7,7 @@ import android.os.Handler
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import androidx.core.app.ActivityOptionsCompat
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
|
import com.appttude.h_mal.farmr.R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by h_mal on 27/06/2017.
|
* Created by h_mal on 27/06/2017.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.appttude.h_mal.farmr.utils
|
||||||
|
|
||||||
|
const val LEGACY = "LEGACY_"
|
||||||
|
const val DATE_FORMAT = "yyyy-MM-dd"
|
||||||
|
const val TIME_FORMAT = "hh:mm"
|
||||||
|
const val ID = "ID"
|
||||||
|
const val CURRENCY = "£"
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.appttude.h_mal.farmr.utils
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
fun String.formatToTwoDp(): Float {
|
||||||
|
val formattedString = String.format("%.2f", this)
|
||||||
|
return formattedString.toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Float.formatToTwoDp(): Float {
|
||||||
|
val formattedString = String.format("%.2f", this)
|
||||||
|
return formattedString.toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Float.formatToTwoDpString(): String {
|
||||||
|
return formatToTwoDp().toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.dateStringIsValid(): Boolean {
|
||||||
|
return DATE_FORMAT.toPattern().matcher(this).matches()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.timeStringIsValid(): Boolean {
|
||||||
|
return TIME_FORMAT.toPattern().matcher(this).matches()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Calendar.getTimeString(): String {
|
||||||
|
val format = SimpleDateFormat(TIME_FORMAT, Locale.getDefault())
|
||||||
|
return format.format(time)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* turns "HH:mm" into an hour and minutes pair
|
||||||
|
*
|
||||||
|
* eg:
|
||||||
|
* @param 13:45
|
||||||
|
* @return Pair(13, 45)
|
||||||
|
*/
|
||||||
|
fun convertTimeStringToHourMinutesPair(timeString: String): Pair<Int, Int> {
|
||||||
|
val split = timeString.split(":")
|
||||||
|
if (split.size != 2) throw ArrayIndexOutOfBoundsException()
|
||||||
|
return Pair(split.first().toInt(), split[1].toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculate the duration between two 24 hour time strings minus the break in minutes
|
||||||
|
*
|
||||||
|
* can also calculate when time to string in past midnight eg: 23:00, 04:45, 30
|
||||||
|
* @return 5.75
|
||||||
|
*/
|
||||||
|
fun calculateDuration(timeIn: String, timeOut: String, breaks: Int): Float {
|
||||||
|
val timeFrom = convertTimeStringToHourMinutesPair(timeIn)
|
||||||
|
val timeTo = convertTimeStringToHourMinutesPair(timeOut)
|
||||||
|
|
||||||
|
val hoursIn = timeFrom.first
|
||||||
|
val minutesIn = timeFrom.second
|
||||||
|
val hoursOut = timeTo.first
|
||||||
|
val minutesOut = timeTo.second
|
||||||
|
|
||||||
|
var duration: Float = if (hoursOut > hoursIn) {
|
||||||
|
((hoursOut.toFloat() + (minutesOut.toFloat() / 60)) - (hoursIn.toFloat() + (minutesIn.toFloat() / 60)))
|
||||||
|
} else {
|
||||||
|
(((hoursOut.toFloat() + (minutesOut.toFloat() / 60)) - (hoursIn.toFloat() + (minutesIn.toFloat() / 60))) + 24)
|
||||||
|
}
|
||||||
|
if ((breaks.toFloat() / 60) > duration) throw IOException("Breaks duration cannot be larger than shift duration")
|
||||||
|
duration -= (breaks.toFloat() / 60)
|
||||||
|
|
||||||
|
return duration.formatToTwoDp()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateDuration(timeIn: String?, timeOut: String?, breaks: Int?): Float {
|
||||||
|
val calendar by lazy { Calendar.getInstance() }
|
||||||
|
val insertTimeIn = timeIn ?: calendar.getTimeString()
|
||||||
|
val insertTimeOut = timeOut ?: calendar.getTimeString()
|
||||||
|
|
||||||
|
return calculateDuration(insertTimeIn, insertTimeOut, breaks ?: 0)
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.appttude.h_mal.farmr.utils
|
||||||
|
|
||||||
|
import com.appttude.h_mal.farmr.model.Order
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <CLASS : Any> Any.getGenericClassAt(position: Int): KClass<CLASS> =
|
||||||
|
((javaClass.genericSuperclass as? ParameterizedType)
|
||||||
|
?.actualTypeArguments?.getOrNull(position) as? Class<CLASS>)
|
||||||
|
?.kotlin
|
||||||
|
?: throw IllegalStateException("Can not find class from generic argument")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param validate when result is false then we trigger
|
||||||
|
* @param onError
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @sample
|
||||||
|
* var s: String?
|
||||||
|
* i.validate{!i.isNullOrEmpty()} { print("string is empty") }
|
||||||
|
*/
|
||||||
|
inline fun<T: Any?> T.validateField(validate: (T) -> Boolean, onError: () -> Unit) {
|
||||||
|
if (!validate.invoke(this)) {
|
||||||
|
onError.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all elements sorted according to the specified comparator. In order of ascending or descending
|
||||||
|
* The sort is stable. It means that equal elements preserve their order relative to each other after sorting.
|
||||||
|
*/
|
||||||
|
inline fun <T, R : Comparable<R>> Iterable<T>.sortedByOrder(order: Order = Order.ASCENDING, crossinline selector: (T) -> R?): List<T> {
|
||||||
|
return when (order) {
|
||||||
|
Order.ASCENDING -> sortedWith(compareBy(selector))
|
||||||
|
Order.DESCENDING -> sortedWith(compareByDescending(selector))
|
||||||
|
}
|
||||||
|
}
|
||||||
177
app/src/main/java/com/appttude/h_mal/farmr/utils/ViewUtils.kt
Normal file
177
app/src/main/java/com/appttude/h_mal/farmr/utils/ViewUtils.kt
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
package com.appttude.h_mal.farmr.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.app.DatePickerDialog
|
||||||
|
import android.app.TimePickerDialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.Animation
|
||||||
|
import android.view.animation.AnimationUtils
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.AnimRes
|
||||||
|
import androidx.annotation.IdRes
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentTransaction
|
||||||
|
import com.appttude.h_mal.farmr.R
|
||||||
|
import com.appttude.h_mal.farmr.ui.FragmentAddItem
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
|
fun View.show() {
|
||||||
|
this.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.hide() {
|
||||||
|
this.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.displayToast(message: String) {
|
||||||
|
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Fragment.displayToast(message: String) {
|
||||||
|
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ViewGroup.generateView(layoutId: Int): View = LayoutInflater
|
||||||
|
.from(context)
|
||||||
|
.inflate(layoutId, this, false)
|
||||||
|
|
||||||
|
fun Fragment.hideKeyboard() {
|
||||||
|
val imm = context?.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager?
|
||||||
|
imm?.hideSoftInputFromWindow(view?.windowToken, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.triggerAnimation(@AnimRes id: Int, complete: (View) -> Unit) {
|
||||||
|
val animation = AnimationUtils.loadAnimation(context, id)
|
||||||
|
animation.setAnimationListener(object : Animation.AnimationListener {
|
||||||
|
override fun onAnimationEnd(animation: Animation?) = complete(this@triggerAnimation)
|
||||||
|
override fun onAnimationStart(a: Animation?) {}
|
||||||
|
override fun onAnimationRepeat(a: Animation?) {}
|
||||||
|
})
|
||||||
|
startAnimation(animation)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Fragment.navigateToFragment(fragment: Fragment, @IdRes container: Int = R.id.container, name: String = "") {
|
||||||
|
val fragmentTransaction = requireActivity().supportFragmentManager.beginTransaction()
|
||||||
|
fragmentTransaction.replace(container, fragment).addToBackStack(name).commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Fragment.navigateToFragment(fragment: Fragment, @IdRes container: Int = R.id.container, name: String = "", bundle: Bundle) {
|
||||||
|
val fragmentTransaction = requireActivity().supportFragmentManager.beginTransaction()
|
||||||
|
fragmentTransaction.replace(container, fragment.apply { arguments = bundle }).addToBackStack(name).commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.createDialog(
|
||||||
|
title: String?,
|
||||||
|
message: String?,
|
||||||
|
displayCancel: Boolean = false,
|
||||||
|
displayOk: Boolean = true,
|
||||||
|
cancelCallback: DialogInterface.OnClickListener? = null,
|
||||||
|
okCallback: DialogInterface.OnClickListener? = null,
|
||||||
|
) {
|
||||||
|
val builder = AlertDialog.Builder(this)
|
||||||
|
title?.let { builder.setTitle(it) }
|
||||||
|
message?.let { builder.setMessage(it) }
|
||||||
|
if (displayCancel) {
|
||||||
|
builder.setNegativeButton(android.R.string.cancel, cancelCallback)
|
||||||
|
}
|
||||||
|
if (displayOk) {
|
||||||
|
builder.setPositiveButton(android.R.string.ok, okCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.create().show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AppCompatActivity.popBackStack() {
|
||||||
|
supportFragmentManager.popBackStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun EditText.setTimePicker(onSelected: (String) -> Unit) {
|
||||||
|
var mHoursOut: Int
|
||||||
|
var mMinutesOut: Int
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
val mCurrentTime by lazy { Calendar.getInstance() }
|
||||||
|
if (!text.isNullOrEmpty()) {
|
||||||
|
// EditText contains text - lets try set the parse the text
|
||||||
|
try {
|
||||||
|
val convertedString = convertTimeStringToHourMinutesPair(text.toString())
|
||||||
|
mHoursOut = convertedString.first
|
||||||
|
mMinutesOut = convertedString.second
|
||||||
|
} catch (e: Exception) {
|
||||||
|
mHoursOut = mCurrentTime[Calendar.HOUR_OF_DAY]
|
||||||
|
mMinutesOut = mCurrentTime[Calendar.MINUTE]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mHoursOut = mCurrentTime[Calendar.HOUR_OF_DAY]
|
||||||
|
mMinutesOut = mCurrentTime[Calendar.MINUTE]
|
||||||
|
}
|
||||||
|
val mTimePicker = TimePickerDialog(this.context,
|
||||||
|
{ _, selectedHour, selectedMinute ->
|
||||||
|
val ddTime = String.format("%02d", selectedHour) + ":" + String.format(
|
||||||
|
"%02d",
|
||||||
|
selectedMinute
|
||||||
|
)
|
||||||
|
setText(ddTime)
|
||||||
|
onSelected.invoke(ddTime)
|
||||||
|
}, mHoursOut, mMinutesOut, true
|
||||||
|
) //Yes 24 hour time
|
||||||
|
mTimePicker.setTitle("Select Time")
|
||||||
|
mTimePicker.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun EditText.setDatePicker(onSelected: (String) -> Unit) {
|
||||||
|
//To show current date in the datepicker
|
||||||
|
var mYear: Int
|
||||||
|
var mMonth: Int
|
||||||
|
var mDay: Int
|
||||||
|
|
||||||
|
val mCurrentDate by lazy { Calendar.getInstance() }
|
||||||
|
|
||||||
|
if (!text.isNullOrEmpty()) {
|
||||||
|
try {
|
||||||
|
val dateSplit = text.split("-")
|
||||||
|
|
||||||
|
mYear = dateSplit[0].toInt()
|
||||||
|
mMonth = dateSplit[1].toInt()
|
||||||
|
mMonth = if (mMonth == 1) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
mMonth - 1
|
||||||
|
}
|
||||||
|
mDay = dateSplit[2].toInt()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
mYear = mCurrentDate[Calendar.YEAR]
|
||||||
|
mMonth = mCurrentDate[Calendar.MONTH]
|
||||||
|
mDay = mCurrentDate[Calendar.DAY_OF_MONTH]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mYear = mCurrentDate[Calendar.YEAR]
|
||||||
|
mMonth = mCurrentDate[Calendar.MONTH]
|
||||||
|
mDay = mCurrentDate[Calendar.DAY_OF_MONTH]
|
||||||
|
}
|
||||||
|
val mDatePicker = DatePickerDialog(
|
||||||
|
(this.context),
|
||||||
|
{ datepicker, selectedyear, selectedmonth, selectedday ->
|
||||||
|
var currentMonth = selectedmonth
|
||||||
|
val dateString = StringBuilder().append(selectedyear).append("-")
|
||||||
|
.append(String.format("%02d", (currentMonth + 1.also { currentMonth = it })))
|
||||||
|
.append("-")
|
||||||
|
.append(String.format("%02d", selectedday))
|
||||||
|
.toString()
|
||||||
|
setText(dateString)
|
||||||
|
onSelected.invoke(dateString)
|
||||||
|
}, mYear, mMonth, mDay
|
||||||
|
)
|
||||||
|
mDatePicker.setTitle("Select date")
|
||||||
|
mDatePicker.show()
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.appttude.h_mal.farmr.viewmodel
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.appttude.h_mal.farmr.data.RepositoryImpl
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationViewModelFactory(
|
||||||
|
private val repository: RepositoryImpl
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
with(modelClass) {
|
||||||
|
return when {
|
||||||
|
isAssignableFrom(MainViewModel::class.java) -> MainViewModel(repository)
|
||||||
|
else -> throw IllegalArgumentException("Unknown ViewModel class")
|
||||||
|
} as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,483 @@
|
|||||||
|
package com.appttude.h_mal.farmr.viewmodel
|
||||||
|
|
||||||
|
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.prefs.DESCRIPTION
|
||||||
|
import com.appttude.h_mal.farmr.data.prefs.TIME_IN
|
||||||
|
import com.appttude.h_mal.farmr.data.prefs.TIME_OUT
|
||||||
|
import com.appttude.h_mal.farmr.data.prefs.TYPE
|
||||||
|
import com.appttude.h_mal.farmr.model.FilterStore
|
||||||
|
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.utils.calculateDuration
|
||||||
|
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 java.io.IOException
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
|
|
||||||
|
class MainViewModel(
|
||||||
|
private val repository: Repository
|
||||||
|
) : BaseViewModel() {
|
||||||
|
|
||||||
|
private val _shiftLiveData = MutableLiveData<List<ShiftObject>>()
|
||||||
|
val shiftLiveData: LiveData<List<ShiftObject>> = _shiftLiveData
|
||||||
|
|
||||||
|
private var mSort: Sortable = Sortable.ID
|
||||||
|
private var mOrder: Order = Order.ASCENDING
|
||||||
|
|
||||||
|
private var mFilterStore: FilterStore? = null
|
||||||
|
|
||||||
|
private val observer = Observer<List<ShiftObject>> {
|
||||||
|
onSuccess(it.sortList(mSort, mOrder))
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
// Load shifts into live data when view model has been instantiated
|
||||||
|
refreshLiveData()
|
||||||
|
shiftLiveData.observeForever(observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
shiftLiveData.removeObserver(observer)
|
||||||
|
super.onCleared()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<ShiftObject>.sortList(sort: Sortable, order: Order): List<ShiftObject> {
|
||||||
|
return when (sort) {
|
||||||
|
Sortable.ID -> sortedByOrder(order) { it.id }
|
||||||
|
Sortable.TYPE -> sortedByOrder(order) { it.type }
|
||||||
|
Sortable.DATE -> sortedByOrder(order) { it.date }
|
||||||
|
Sortable.DESCRIPTION -> sortedByOrder(order) { it.description }
|
||||||
|
Sortable.DURATION -> sortedByOrder(order) { it.duration }
|
||||||
|
Sortable.UNITS -> sortedByOrder(order) { it.units }
|
||||||
|
Sortable.RATEOFPAY -> sortedByOrder(order) { it.rateOfPay }
|
||||||
|
Sortable.TOTALPAY -> sortedByOrder(order) { it.totalPay }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSortAndOrder(): Pair<Sortable, Order> {
|
||||||
|
return Pair(mSort, mOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSortAndOrder(sort: Sortable, order: Order = Order.ASCENDING) {
|
||||||
|
mSort = sort
|
||||||
|
mOrder = order
|
||||||
|
refreshLiveData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInformation(): String {
|
||||||
|
var totalDuration = 0.0f
|
||||||
|
var countOfTypeH = 0
|
||||||
|
var countOfTypeP = 0
|
||||||
|
var totalUnits = 0f
|
||||||
|
var totalPay = 0f
|
||||||
|
val lines = _shiftLiveData.value?.size ?: 0
|
||||||
|
_shiftLiveData.value?.forEach {
|
||||||
|
totalDuration += it.duration
|
||||||
|
when (ShiftType.getEnumByType(it.type)) {
|
||||||
|
ShiftType.HOURLY -> countOfTypeH += 1
|
||||||
|
ShiftType.PIECE -> countOfTypeP += 1
|
||||||
|
}
|
||||||
|
totalUnits += it.units
|
||||||
|
totalPay += it.totalPay
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildInfoString(
|
||||||
|
totalDuration,
|
||||||
|
countOfTypeH,
|
||||||
|
countOfTypeP,
|
||||||
|
totalUnits,
|
||||||
|
totalPay,
|
||||||
|
lines
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCurrentShift(id: Long) = repository.readSingleShiftFromDatabase(id)
|
||||||
|
|
||||||
|
fun insertHourlyShift(
|
||||||
|
description: String,
|
||||||
|
date: String,
|
||||||
|
rateOfPay: Float,
|
||||||
|
timeIn: String?,
|
||||||
|
timeOut: String?,
|
||||||
|
breakMins: Int?,
|
||||||
|
) {
|
||||||
|
// Validate inputs from the edit texts
|
||||||
|
(description.length > 3).validateField {
|
||||||
|
onError("Description length should be longer")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
date.dateStringIsValid().validateField {
|
||||||
|
onError("Date format is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
(rateOfPay.toFloat() >= 0.00).validateField {
|
||||||
|
onError("Rate of pay is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timeIn?.timeStringIsValid()?.validateField {
|
||||||
|
onError("Time in format is in correct")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timeOut?.timeStringIsValid()?.validateField {
|
||||||
|
onError("Time out format is in correct")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
breakMins?.let { it > 0 }?.validateField {
|
||||||
|
onError("Break in minutes is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
doTry {
|
||||||
|
insertShiftIntoDatabase(
|
||||||
|
ShiftType.HOURLY,
|
||||||
|
description,
|
||||||
|
date,
|
||||||
|
rateOfPay.formatToTwoDp(),
|
||||||
|
timeIn,
|
||||||
|
timeOut,
|
||||||
|
breakMins,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insertPieceRateShift(
|
||||||
|
description: String,
|
||||||
|
date: String,
|
||||||
|
units: Float,
|
||||||
|
rateOfPay: Float
|
||||||
|
) {
|
||||||
|
// Validate inputs from the edit texts
|
||||||
|
(description.length < 3).validateField {
|
||||||
|
onError("Description length should be longer")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
date.dateStringIsValid().validateField {
|
||||||
|
onError("Date format is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
(rateOfPay >= 0.00).validateField {
|
||||||
|
onError("Rate of pay is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
(units.toInt() >= 0).validateField {
|
||||||
|
onError("Units cannot be below zero")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
doTry {
|
||||||
|
insertShiftIntoDatabase(
|
||||||
|
type = ShiftType.PIECE,
|
||||||
|
description = description,
|
||||||
|
date = date,
|
||||||
|
rateOfPay = rateOfPay.formatToTwoDp(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
units = units
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateShift(
|
||||||
|
id: Long,
|
||||||
|
type: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
date: String? = null,
|
||||||
|
rateOfPay: String? = null,
|
||||||
|
timeIn: String? = null,
|
||||||
|
timeOut: String? = null,
|
||||||
|
breakMins: String? = null,
|
||||||
|
units: String? = null,
|
||||||
|
) {
|
||||||
|
description?.let {
|
||||||
|
(it.length < 3).validateField {
|
||||||
|
onError("Description length should be longer")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
date?.dateStringIsValid()?.validateField {
|
||||||
|
onError("Date format is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rateOfPay?.let {
|
||||||
|
(it.toFloat() >= 0.00).validateField {
|
||||||
|
onError("Rate of pay is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
units?.let {
|
||||||
|
(it.toInt() >= 0).validateField {
|
||||||
|
onError("Units cannot be below zero")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeIn?.timeStringIsValid()?.validateField {
|
||||||
|
onError("Time in format is in correct")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timeOut?.timeStringIsValid()?.validateField {
|
||||||
|
onError("Time out format is in correct")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
breakMins?.let { it.toInt() > 0 }?.validateField {
|
||||||
|
onError("Break in minutes is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
doTry {
|
||||||
|
updateShiftInDatabase(
|
||||||
|
id,
|
||||||
|
type = type?.let { ShiftType.getEnumByType(it) },
|
||||||
|
description = description,
|
||||||
|
date = date,
|
||||||
|
rateOfPay = rateOfPay?.toFloatOrNull(),
|
||||||
|
timeIn = timeIn,
|
||||||
|
timeOut = timeOut,
|
||||||
|
breakMins = breakMins?.toIntOrNull(),
|
||||||
|
units = units?.toFloatOrNull()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteShift(id: Long) {
|
||||||
|
if (!repository.deleteSingleShiftFromDatabase(id)) {
|
||||||
|
onError("Failed to delete shift")
|
||||||
|
} else {
|
||||||
|
refreshLiveData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteAllShifts() {
|
||||||
|
if (!repository.deleteAllShiftsFromDatabase()) {
|
||||||
|
onError("Failed to delete all shifts from database")
|
||||||
|
} else {
|
||||||
|
refreshLiveData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateShiftInDatabase(
|
||||||
|
id: Long,
|
||||||
|
type: ShiftType? = null,
|
||||||
|
description: String? = null,
|
||||||
|
date: String? = null,
|
||||||
|
rateOfPay: Float? = null,
|
||||||
|
timeIn: String? = null,
|
||||||
|
timeOut: String? = null,
|
||||||
|
breakMins: Int? = null,
|
||||||
|
units: Float? = null,
|
||||||
|
) {
|
||||||
|
val currentShift = repository.readSingleShiftFromDatabase(id)?.copyToShift()
|
||||||
|
?: throw IOException("Cannot update shift as it does not exist")
|
||||||
|
|
||||||
|
val shift = when (type) {
|
||||||
|
ShiftType.HOURLY -> {
|
||||||
|
// Shift type has changed so mandatory fields for hourly shift are now required as well
|
||||||
|
val insertTimeIn =
|
||||||
|
(timeIn ?: currentShift.timeIn) ?: throw IOException("No time in inserted")
|
||||||
|
val insertTimeOut =
|
||||||
|
(timeOut ?: currentShift.timeOut) ?: throw IOException("No time out inserted")
|
||||||
|
Shift(
|
||||||
|
description = description ?: currentShift.description,
|
||||||
|
date = date ?: currentShift.date,
|
||||||
|
timeIn = insertTimeIn,
|
||||||
|
timeOut = insertTimeOut,
|
||||||
|
breakMins = breakMins ?: currentShift.breakMins,
|
||||||
|
rateOfPay = rateOfPay ?: currentShift.rateOfPay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ShiftType.PIECE -> {
|
||||||
|
// Shift type has changed so mandatory fields for piece rate shift are now required as well
|
||||||
|
val insertUnits = (units ?: currentShift.units)
|
||||||
|
?: throw IOException("Units must be inserted for piece rate shifts")
|
||||||
|
Shift(
|
||||||
|
description = description ?: currentShift.description,
|
||||||
|
date = date ?: currentShift.date,
|
||||||
|
units = insertUnits,
|
||||||
|
rateOfPay = rateOfPay ?: currentShift.rateOfPay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
if (timeIn == null && timeOut == null && units == null && breakMins == null && rateOfPay == null) {
|
||||||
|
// Updates to description or date field
|
||||||
|
currentShift.copy(
|
||||||
|
description = description ?: currentShift.description,
|
||||||
|
date = date ?: currentShift.date,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Updating shifts where shift type has remained the same
|
||||||
|
when (currentShift.type) {
|
||||||
|
ShiftType.HOURLY -> {
|
||||||
|
val insertTimeIn = (timeIn ?: currentShift.timeIn) ?: throw IOException(
|
||||||
|
"No time in inserted"
|
||||||
|
)
|
||||||
|
val insertTimeOut = (timeOut ?: currentShift.timeOut)
|
||||||
|
?: throw IOException("No time out inserted")
|
||||||
|
Shift(
|
||||||
|
description = description ?: currentShift.description,
|
||||||
|
date = date ?: currentShift.date,
|
||||||
|
timeIn = insertTimeIn,
|
||||||
|
timeOut = insertTimeOut,
|
||||||
|
breakMins = breakMins ?: currentShift.breakMins,
|
||||||
|
rateOfPay = rateOfPay ?: currentShift.rateOfPay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ShiftType.PIECE -> {
|
||||||
|
val insertUnits = (units ?: currentShift.units)
|
||||||
|
?: throw IOException("Units must be inserted for piece rate shifts")
|
||||||
|
Shift(
|
||||||
|
description = description ?: currentShift.description,
|
||||||
|
date = date ?: currentShift.date,
|
||||||
|
units = insertUnits,
|
||||||
|
rateOfPay = rateOfPay ?: currentShift.rateOfPay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.updateShiftIntoDatabase(id, shift)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun insertShiftIntoDatabase(
|
||||||
|
type: ShiftType,
|
||||||
|
description: String,
|
||||||
|
date: String,
|
||||||
|
rateOfPay: Float,
|
||||||
|
timeIn: String?,
|
||||||
|
timeOut: String?,
|
||||||
|
breakMins: Int?,
|
||||||
|
units: Float?,
|
||||||
|
) {
|
||||||
|
val shift = when (type) {
|
||||||
|
ShiftType.HOURLY -> {
|
||||||
|
if (timeIn.isNullOrBlank() && timeOut.isNullOrBlank()) throw IOException("Time in and time out are null")
|
||||||
|
val calendar by lazy { Calendar.getInstance() }
|
||||||
|
val insertTimeIn = timeIn ?: calendar.getTimeString()
|
||||||
|
val insertTimeOut = timeOut ?: calendar.getTimeString()
|
||||||
|
|
||||||
|
Shift(
|
||||||
|
description = description,
|
||||||
|
date = date,
|
||||||
|
timeIn = insertTimeIn,
|
||||||
|
timeOut = insertTimeOut,
|
||||||
|
breakMins = breakMins,
|
||||||
|
rateOfPay = rateOfPay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ShiftType.PIECE -> {
|
||||||
|
Shift(
|
||||||
|
description = description,
|
||||||
|
date = date,
|
||||||
|
units = units!!,
|
||||||
|
rateOfPay = rateOfPay,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.insertShiftIntoDatabase(shift)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun buildInfoString(
|
||||||
|
totalDuration: Float,
|
||||||
|
countOfHourly: Int,
|
||||||
|
countOfPiece: Int,
|
||||||
|
totalUnits: Float,
|
||||||
|
totalPay: Float,
|
||||||
|
lines: Int
|
||||||
|
): String {
|
||||||
|
var textString: String
|
||||||
|
textString = "$lines Shifts"
|
||||||
|
if (countOfHourly != 0 && countOfPiece != 0) {
|
||||||
|
textString = "$textString ($countOfHourly Hourly/$countOfPiece Piece Rate)"
|
||||||
|
}
|
||||||
|
if (countOfHourly != 0) {
|
||||||
|
textString = """
|
||||||
|
$textString
|
||||||
|
Total Hours: ${String.format("%.2f", totalDuration)}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
if (countOfPiece != 0) {
|
||||||
|
textString = """
|
||||||
|
$textString
|
||||||
|
Total Units: ${String.format("%.2f", totalUnits)}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
if (totalPay != 0f) {
|
||||||
|
textString = """
|
||||||
|
$textString
|
||||||
|
Total Pay: ${"$"}${String.format("%.2f", totalPay)}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
return textString
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshLiveData() {
|
||||||
|
_shiftLiveData.postValue(repository.readShiftsFromDatabase())
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun Boolean.validateField(failureCallback: () -> Unit) {
|
||||||
|
if (!this) failureCallback.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lambda function that will invoke onError(...) on failure
|
||||||
|
* but update live data when successful
|
||||||
|
*/
|
||||||
|
private inline fun doTry(operation: () -> Unit) {
|
||||||
|
try {
|
||||||
|
operation.invoke()
|
||||||
|
refreshLiveData()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
onError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFiltrationDetails(
|
||||||
|
description: String?,
|
||||||
|
dateFrom: String?,
|
||||||
|
dateTo: String?,
|
||||||
|
type: String?
|
||||||
|
) {
|
||||||
|
repository.setFilteringDetailsInPrefs(description, dateFrom, dateTo, type)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFiltrationDetails(): FilterStore {
|
||||||
|
val prefs = repository.retrieveFilteringDetailsInPrefs()
|
||||||
|
mFilterStore = FilterStore(
|
||||||
|
prefs[DESCRIPTION],
|
||||||
|
prefs[TIME_IN],
|
||||||
|
prefs[TIME_OUT],
|
||||||
|
prefs[TYPE]
|
||||||
|
)
|
||||||
|
return mFilterStore!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun retrieveDurationText(mTimeIn: String?, mTimeOut: String?, mBreaks: Int): Float? {
|
||||||
|
try {
|
||||||
|
return calculateDuration(mTimeIn,mTimeOut,mBreaks)
|
||||||
|
}catch (e: IOException) {
|
||||||
|
onError(e)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
38
app/src/main/res/layout/empty_list_view.xml
Normal file
38
app/src/main/res/layout/empty_list_view.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<RelativeLayout
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
android:id="@+id/empty_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_title_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:text="Shift list empty"
|
||||||
|
android:textAppearance="?android:textAppearanceMedium"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_subtitle_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/empty_title_text"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:text="add shift to begin"
|
||||||
|
android:textAppearance="?android:textAppearanceSmall"
|
||||||
|
android:textColor="#A2AAB0"/>
|
||||||
|
</RelativeLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -8,16 +8,9 @@
|
|||||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
tools:context="com.appttude.h_mal.farmr.FragmentAddItem"
|
tools:context="com.appttude.h_mal.farmr.ui.FragmentAddItem"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/pd_ai"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@@ -189,7 +182,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="Break"
|
android:text="@string/break_res"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat" />
|
android:textAppearance="@style/TextAppearance.AppCompat" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
@@ -198,7 +191,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="2"
|
android:layout_weight="2"
|
||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:hint="Break in minutes"
|
android:hint="@string/insert_break_in_minutes"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:selectAllOnFocus="true" />
|
android:selectAllOnFocus="true" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
tools:context="com.appttude.h_mal.farmr.FilterDataFragment">
|
tools:context="com.appttude.h_mal.farmr.ui.FilterDataFragment">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
tools:context="com.appttude.h_mal.farmr.FurtherInfoFragment">
|
tools:context="com.appttude.h_mal.farmr.ui.FurtherInfoFragment">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
|||||||
@@ -2,44 +2,16 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:ads="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
tools:context="com.appttude.h_mal.farmr.FragmentMain">
|
tools:context="com.appttude.h_mal.farmr.ui.FragmentMain">
|
||||||
|
|
||||||
<ListView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/list_item_view"
|
android:id="@+id/list_item_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
tools:listitem="@layout/list_item_1">
|
tools:listitem="@layout/list_item_1">
|
||||||
</ListView>
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@+id/empty_view"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/empty_title_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:text="Shift list empty"
|
|
||||||
android:textAppearance="?android:textAppearanceMedium"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/empty_subtitle_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@+id/empty_title_text"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:fontFamily="sans-serif"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:text="add shift to begin"
|
|
||||||
android:textAppearance="?android:textAppearanceSmall"
|
|
||||||
android:textColor="#A2AAB0"/>
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fab1"
|
android:id="@+id/fab1"
|
||||||
@@ -49,6 +21,6 @@
|
|||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:layout_margin="@dimen/fab_margin"
|
android:layout_margin="@dimen/fab_margin"
|
||||||
android:src="@drawable/add"
|
android:src="@drawable/add"
|
||||||
ads:backgroundTint="@color/colorPrimary" />
|
app:backgroundTint="@color/colorPrimary" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|||||||
@@ -126,7 +126,6 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
|
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
app:srcCompat="@android:drawable/ic_menu_edit" />
|
app:srcCompat="@android:drawable/ic_menu_edit" />
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
tools:context=".MainActivity"
|
tools:context=".ui.MainActivity"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context="com.appttude.h_mal.farmr.MainActivity">
|
tools:context="com.appttude.h_mal.farmr.ui.MainActivity">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_favorite"
|
android:id="@+id/action_favorite"
|
||||||
android:icon="@drawable/image_i_64"
|
android:icon="@drawable/image_i_64"
|
||||||
|
|||||||
@@ -102,4 +102,6 @@
|
|||||||
<!-- TODO: Remove or change this placeholder text -->
|
<!-- TODO: Remove or change this placeholder text -->
|
||||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||||
<string name="further_info_title">Shift Details</string>
|
<string name="further_info_title">Shift Details</string>
|
||||||
|
<string name="insert_break_in_minutes">insert break in minutes</string>
|
||||||
|
<string name="break_res">Break</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user