Merge pull request #20 from hmalik144/fastlane_setup

Fastlane setup
This commit is contained in:
2023-08-29 21:19:37 +01:00
committed by GitHub
35 changed files with 392 additions and 359 deletions

View File

@@ -1,113 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

3
Gemfile Normal file
View File

@@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "fastlane"

View File

@@ -2,19 +2,34 @@ apply plugin: 'com.android.application'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'kotlin-kapt'
def relStorePassword = System.getenv("RELEASE_STORE_PASSWORD")
def relKeyPassword = System.getenv("RELEASE_KEY_PASSWORD")
def relKeyAlias = System.getenv("RELEASE_KEY_ALIAS")
def keystorePath = "/keystore.jks"
def keystore = file(keystorePath).exists() ? file(keystorePath) : null
android {
compileSdkVersion 31
defaultConfig {
applicationId "com.appttude.h_mal.farmr"
minSdkVersion 21
targetSdkVersion 31
versionCode 1
versionName "1.0"
versionCode 2
versionName "2.0"
testInstrumentationRunner 'com.appttude.h_mal.farmr.application.TestRunner'
vectorDrawables.useSupportLibrary = true
}
signingConfigs {
release {
storePassword relStorePassword
keyPassword relKeyPassword
keyAlias relKeyAlias
storeFile keystore
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}

View File

@@ -3,6 +3,7 @@ package com.appttude.h_mal.farmr.data
import android.content.ContentResolver
import android.content.ContentValues
import androidx.test.rule.provider.ProviderTestRule
import com.appttude.h_mal.farmr.data.legacydb.ShiftProvider
import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.CONTENT_AUTHORITY
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
@@ -16,7 +17,6 @@ import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_
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.data.legacydb.ShiftProvider
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull
import org.junit.After

View File

@@ -6,9 +6,7 @@ import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
@@ -18,7 +16,6 @@ import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import com.appttude.h_mal.farmr.application.TestAppClass
import com.appttude.h_mal.farmr.di.ShiftApplication
import com.appttude.h_mal.farmr.ui.utils.getShifts
import kotlinx.coroutines.runBlocking
import org.hamcrest.Matcher

View File

@@ -1,8 +1,8 @@
package com.appttude.h_mal.farmr.ui.robots
import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.ui.BaseTestRobot
import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.ui.BaseTestRobot
fun filterScreen(func: FilterScreenRobot.() -> Unit) = FilterScreenRobot().apply { func() }
class FilterScreenRobot : BaseTestRobot() {

View File

@@ -1,6 +1,5 @@
package com.appttude.h_mal.farmr.ui.robots
import androidx.test.espresso.matcher.RootMatchers.isDialog
import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.base.BaseRecyclerAdapter.CurrentViewHolder
import com.appttude.h_mal.farmr.model.Order

View File

@@ -1,8 +1,8 @@
package com.appttude.h_mal.farmr.ui.robots
import com.appttude.h_mal.farmr.R
import com.appttude.h_mal.farmr.ui.BaseTestRobot
import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.ui.BaseTestRobot
fun viewScreen(func: ViewItemScreenRobot.() -> Unit) = ViewItemScreenRobot().apply { func() }
class ViewItemScreenRobot : BaseTestRobot() {

View File

@@ -9,7 +9,6 @@ import com.appttude.h_mal.farmr.ui.robots.addScreen
import com.appttude.h_mal.farmr.ui.robots.filterScreen
import com.appttude.h_mal.farmr.ui.robots.homeScreen
import com.appttude.h_mal.farmr.ui.robots.viewScreen
import com.appttude.h_mal.farmr.utils.ID
import org.junit.Test
class ShiftTests : BaseTest<MainActivity>(MainActivity::class.java) {

View File

@@ -40,7 +40,7 @@
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.appttude.h_mal.farmr.provider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@@ -4,14 +4,12 @@ import android.os.Bundle
import android.view.View
import androidx.annotation.LayoutRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.createViewModelLazy
import androidx.lifecycle.ViewModelLazy
import com.appttude.h_mal.farmr.model.ViewState
import com.appttude.h_mal.farmr.utils.getGenericClassAt
import com.appttude.h_mal.farmr.utils.popBackStack
import com.appttude.h_mal.farmr.viewmodel.ApplicationViewModelFactory
import org.kodein.di.KodeinAware
import org.kodein.di.android.kodein
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
import kotlin.properties.Delegates

View File

@@ -4,7 +4,6 @@ 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() {

View File

@@ -3,7 +3,6 @@ package com.appttude.h_mal.farmr.data.legacydb
import android.content.ContentResolver
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
@@ -19,7 +18,6 @@ import com.appttude.h_mal.farmr.data.legacydb.ShiftsContract.ShiftsEntry.COLUMN_
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(private val resolver: ContentResolver) {

View File

@@ -19,22 +19,24 @@ class ShiftProvider : ContentProvider() {
return true
}
override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?,
sortOrder: String?): Cursor? {
var selection = selection
var selectionArgs = selectionArgs
override fun query(
uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?,
sortOrder: String?
): Cursor {
val database = mDbHelper!!.readableDatabase
val cursor: Cursor
val match = sUriMatcher.match(uri)
when (match) {
SHIFTS -> cursor = database.query(ShiftsEntry.TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder)
val cursor: Cursor = when (sUriMatcher.match(uri)) {
SHIFTS -> database.query(
ShiftsEntry.TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder
)
SHIFT_ID -> {
selection = ShiftsEntry._ID + "=?"
selectionArgs = arrayOf(ContentUris.parseId(uri).toString())
cursor = database.query(ShiftsEntry.TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder)
val mSelection = ShiftsEntry._ID + "=?"
val mSelectionArgs = arrayOf(ContentUris.parseId(uri).toString())
database.query(
ShiftsEntry.TABLE_NAME, projection, mSelection, mSelectionArgs,
null, null, sortOrder
)
}
else -> throw IllegalArgumentException("Cannot query $uri")
@@ -44,26 +46,25 @@ class ShiftProvider : ContentProvider() {
}
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
val match = sUriMatcher.match(uri)
return when (match) {
return when (sUriMatcher.match(uri)) {
SHIFTS -> insertShift(uri, contentValues)
else -> throw IllegalArgumentException("Insertion is not supported for $uri")
}
}
private fun insertShift(uri: Uri, values: ContentValues?): Uri? {
val description = values!!.getAsString(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION)
?: throw IllegalArgumentException("Description required")
val date = values.getAsString(ShiftsEntry.COLUMN_SHIFT_DATE)
?: throw IllegalArgumentException("Date required")
val timeIn = values.getAsString(ShiftsEntry.COLUMN_SHIFT_TIME_IN)
?: throw IllegalArgumentException("Time In required")
val timeOut = values.getAsString(ShiftsEntry.COLUMN_SHIFT_TIME_OUT)
?: throw IllegalArgumentException("Time Out required")
values!!.getAsString(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION)
?: throw IllegalArgumentException("Description required")
values.getAsString(ShiftsEntry.COLUMN_SHIFT_DATE)
?: throw IllegalArgumentException("Date required")
values.getAsString(ShiftsEntry.COLUMN_SHIFT_TIME_IN)
?: throw IllegalArgumentException("Time In required")
values.getAsString(ShiftsEntry.COLUMN_SHIFT_TIME_OUT)
?: throw IllegalArgumentException("Time Out required")
val duration = values.getAsFloat(ShiftsEntry.COLUMN_SHIFT_DURATION)
require(duration >= 0) { "Duration cannot be negative" }
val shiftType = values.getAsString(ShiftsEntry.COLUMN_SHIFT_TYPE)
?: throw IllegalArgumentException("Shift type required")
values.getAsString(ShiftsEntry.COLUMN_SHIFT_TYPE)
?: throw IllegalArgumentException("Shift type required")
val shiftUnits = values.getAsFloat(ShiftsEntry.COLUMN_SHIFT_UNIT)
require(shiftUnits >= 0) { "Units cannot be negative" }
val payRate = values.getAsFloat(ShiftsEntry.COLUMN_SHIFT_PAYRATE)
@@ -82,43 +83,47 @@ class ShiftProvider : ContentProvider() {
return ContentUris.withAppendedId(uri, id)
}
override fun update(uri: Uri, contentValues: ContentValues?, selection: String?,
selectionArgs: Array<String>?): Int {
var selection = selection
var selectionArgs = selectionArgs
val match = sUriMatcher.match(uri)
return when (match) {
override fun update(
uri: Uri, contentValues: ContentValues?, selection: String?,
selectionArgs: Array<String>?
): Int {
return when (sUriMatcher.match(uri)) {
SHIFTS -> updateShift(uri, contentValues, selection, selectionArgs)
SHIFT_ID -> {
selection = ShiftsEntry._ID + "=?"
selectionArgs = arrayOf(ContentUris.parseId(uri).toString())
updateShift(uri, contentValues, selection, selectionArgs)
val mSelection = ShiftsEntry._ID + "=?"
val mSelectionArgs = arrayOf(ContentUris.parseId(uri).toString())
updateShift(uri, contentValues, mSelection, mSelectionArgs)
}
else -> throw IllegalArgumentException("Update is not supported for $uri")
}
}
private fun updateShift(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int {
private fun updateShift(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<String>?
): Int {
if (values!!.containsKey(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION)) {
val description = values.getAsString(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION)
?: throw IllegalArgumentException("description required")
values.getAsString(ShiftsEntry.COLUMN_SHIFT_DESCRIPTION)
?: throw IllegalArgumentException("description required")
}
if (values.containsKey(ShiftsEntry.COLUMN_SHIFT_DATE)) {
val date = values.getAsString(ShiftsEntry.COLUMN_SHIFT_DATE)
?: throw IllegalArgumentException("date required")
values.getAsString(ShiftsEntry.COLUMN_SHIFT_DATE)
?: throw IllegalArgumentException("date required")
}
if (values.containsKey(ShiftsEntry.COLUMN_SHIFT_TIME_IN)) {
val timeIn = values.getAsString(ShiftsEntry.COLUMN_SHIFT_TIME_IN)
?: throw IllegalArgumentException("time in required")
values.getAsString(ShiftsEntry.COLUMN_SHIFT_TIME_IN)
?: throw IllegalArgumentException("time in required")
}
if (values.containsKey(ShiftsEntry.COLUMN_SHIFT_TIME_OUT)) {
val timeOut = values.getAsString(ShiftsEntry.COLUMN_SHIFT_TIME_OUT)
?: throw IllegalArgumentException("time out required")
values.getAsString(ShiftsEntry.COLUMN_SHIFT_TIME_OUT)
?: throw IllegalArgumentException("time out required")
}
if (values.containsKey(ShiftsEntry.COLUMN_SHIFT_BREAK)) {
val breaks = values.getAsString(ShiftsEntry.COLUMN_SHIFT_BREAK)
?: throw IllegalArgumentException("break required")
values.getAsString(ShiftsEntry.COLUMN_SHIFT_BREAK)
?: throw IllegalArgumentException("break required")
}
if (values.size() == 0) {
return 0
@@ -132,17 +137,15 @@ class ShiftProvider : ContentProvider() {
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
var selection = selection
var selectionArgs = selectionArgs
val database = mDbHelper!!.writableDatabase
val rowsDeleted: Int
val match = sUriMatcher.match(uri)
when (match) {
SHIFTS -> rowsDeleted = database.delete(ShiftsEntry.TABLE_NAME, selection, selectionArgs)
val rowsDeleted: Int = when (sUriMatcher.match(uri)) {
SHIFTS -> database.delete(ShiftsEntry.TABLE_NAME, selection, selectionArgs)
SHIFT_ID -> {
selection = ShiftsEntry._ID + "=?"
selectionArgs = arrayOf(ContentUris.parseId(uri).toString())
rowsDeleted = database.delete(ShiftsEntry.TABLE_NAME, selection, selectionArgs)
val mSelection = ShiftsEntry._ID + "=?"
val mSelectionArgs = arrayOf(ContentUris.parseId(uri).toString())
database.delete(ShiftsEntry.TABLE_NAME, mSelection, mSelectionArgs)
}
else -> throw IllegalArgumentException("Deletion is not supported for $uri")
@@ -169,7 +172,11 @@ class ShiftProvider : ContentProvider() {
init {
sUriMatcher.addURI(ShiftsContract.CONTENT_AUTHORITY, ShiftsContract.PATH_SHIFTS, SHIFTS)
sUriMatcher.addURI(ShiftsContract.CONTENT_AUTHORITY, ShiftsContract.PATH_SHIFTS + "/#", SHIFT_ID)
sUriMatcher.addURI(
ShiftsContract.CONTENT_AUTHORITY,
ShiftsContract.PATH_SHIFTS + "/#",
SHIFT_ID
)
}
}

View File

@@ -3,13 +3,12 @@ package com.appttude.h_mal.farmr.data.legacydb
import android.content.ContentResolver
import android.net.Uri
import android.provider.BaseColumns
import com.appttude.h_mal.farmr.BuildConfig
/**
* Created by h_mal on 26/12/2017.
*/
object ShiftsContract {
const val CONTENT_AUTHORITY = BuildConfig.APPLICATION_ID
const val CONTENT_AUTHORITY = "com.appttude.h_mal.farmr"
val BASE_CONTENT_URI = Uri.parse("content://$CONTENT_AUTHORITY")
const val PATH_SHIFTS = "shifts"

View File

@@ -1,17 +1,8 @@
package com.appttude.h_mal.farmr.di
import com.appttude.h_mal.farmr.base.BaseApplication
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 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: BaseApplication() {

View File

@@ -1,10 +1,7 @@
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(
val type: ShiftType,

View File

@@ -10,8 +10,6 @@ enum class Sortable(val label: String) {
TOTALPAY("Total Pay");
companion object {
val entries = Sortable.values()
fun getEnumByType(label: String): Sortable {
return Sortable.values().first { it.label == label }
}

View File

@@ -26,7 +26,6 @@ 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
import com.appttude.h_mal.farmr.viewmodel.SubmissionViewModel
class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_item),
@@ -120,6 +119,7 @@ class FragmentAddItem : BaseFragment<SubmissionViewModel>(R.layout.fragment_add_
private fun setupViewAfterViewCreated() {
id = arguments?.getLong(ID)
wholeView.hide()
val title = when (arguments?.containsKey(ID)) {
true -> {

View File

@@ -1,12 +1,10 @@
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.net.Uri
import android.os.Bundle
import android.view.MenuItem
import android.view.View
@@ -95,7 +93,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
AlertDialog.Builder(context)
.setTitle("Help & Support:")
.setView(R.layout.dialog_layout)
.setPositiveButton(android.R.string.yes) { arg0, arg1 -> arg0.dismiss() }
.setPositiveButton(android.R.string.ok) { arg0, _ -> arg0.dismiss() }
.create().show()
return true
}
@@ -120,12 +118,11 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
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() }
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _, _ -> exportData() }
.create().show()
} else {
Toast.makeText(context, "Storage permissions required", Toast.LENGTH_SHORT)
.show()
displayToast("Storage permissions required")
}
return true
}
@@ -134,7 +131,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
AlertDialog.Builder(context)
.setTitle("Info:")
.setMessage(viewModel.getInformation())
.setPositiveButton(android.R.string.yes) { arg0, arg1 ->
.setPositiveButton(android.R.string.ok) { arg0, _ ->
arg0.dismiss()
}.create().show()
return true
@@ -144,7 +141,7 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
}
private fun sortData() {
val groupName = Sortable.entries.map { it.label }.toTypedArray()
val groupName = Sortable.values().map { it.label }.toTypedArray()
var sort = Sortable.ID
val sortAndOrder = viewModel.getSortAndOrder()
@@ -155,11 +152,11 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
.setSingleChoiceItems(
groupName,
checkedItem
) { p0, p1 -> sort = Sortable.getEnumByType(groupName[p1]) }
.setPositiveButton("Ascending") { dialog, id ->
) { _, p1 -> sort = Sortable.getEnumByType(groupName[p1]) }
.setPositiveButton("Ascending") { dialog, _ ->
viewModel.setSortAndOrder(sort)
dialog.dismiss()
}.setNegativeButton("Descending") { dialog, id ->
}.setNegativeButton("Descending") { dialog, _ ->
viewModel.setSortAndOrder(sort, Order.DESCENDING)
dialog.dismiss()
}
@@ -210,25 +207,25 @@ class FragmentMain : BaseFragment<MainViewModel>(R.layout.fragment_main), BackPr
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
println("request code$requestCode")
if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
if (grantResults.size > 0
if (grantResults.isNotEmpty()
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
exportDialog()
} else {
Toast.makeText(context, "Storage Permissions denied", Toast.LENGTH_SHORT).show()
displayToast("Storage Permissions denied")
}
}
}
fun exportDialog() {
private 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()
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _, _ -> exportData() }.create().show()
}
fun checkStoragePermissions(activity: Activity?): Boolean {
private fun checkStoragePermissions(activity: Activity?): Boolean {
var status = false
val permission = ActivityCompat.checkSelfPermission(
activity!!,

View File

@@ -12,7 +12,6 @@ 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.formatAsCurrencyString
import com.appttude.h_mal.farmr.utils.formatToTwoDpString
import com.appttude.h_mal.farmr.utils.hide
import com.appttude.h_mal.farmr.utils.navigateToFragment
import com.appttude.h_mal.farmr.utils.show

View File

@@ -1,11 +1,7 @@
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
@@ -14,10 +10,7 @@ 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() {
private lateinit var toolbar: Toolbar

View File

@@ -1,5 +1,6 @@
package com.appttude.h_mal.farmr.ui
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.os.Bundle
import android.view.ViewGroup
@@ -28,6 +29,7 @@ class ShiftListAdapter(
return BaseRecyclerAdapter.CurrentViewHolder(currentViewHolder)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: BaseRecyclerAdapter.CurrentViewHolder, position: Int) {
val view = holder.itemView
val data = getItem(position)
@@ -90,8 +92,8 @@ class ShiftListAdapter(
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 ->
.setPositiveButton("delete") { _, _ -> longPressCallback.invoke(data.id) }
.setNegativeButton("cancel") { dialog, _ ->
dialog?.dismiss()
}
.create().show()

View File

@@ -1,18 +1,17 @@
package com.appttude.h_mal.farmr.ui
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.RelativeLayout
import androidx.core.app.ActivityOptionsCompat
import com.appttude.h_mal.farmr.R
/**
* Created by h_mal on 27/06/2017.
*/
@SuppressLint("CustomSplashScreen")
class SplashScreen : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -20,13 +19,9 @@ class SplashScreen : Activity() {
val i = Intent(this@SplashScreen, MainActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK)
Handler().postDelayed({
// This method will be executed once the timer is over
// Start your app main activity
// startActivity(i,bundle);
Handler(Looper.getMainLooper()).postDelayed({
startActivity(i)
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
// finish();
}, SPLASH_TIME_OUT)
}

View File

@@ -19,9 +19,7 @@ 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() {
@@ -161,12 +159,12 @@ fun EditText.setDatePicker(onSelected: (String) -> Unit) {
}
val mDatePicker = DatePickerDialog(
(this.context),
{ datepicker, selectedyear, selectedmonth, selectedday ->
var currentMonth = selectedmonth
val dateString = StringBuilder().append(selectedyear).append("-")
{ _, 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))
.append(String.format("%02d", selectedDay))
.toString()
setText(dateString)
onSelected.invoke(dateString)

View File

@@ -35,6 +35,5 @@ class InfoViewModel(
stringBuilder.append(" (+ ").append(shiftObject.breakMins).append(" minutes break)")
}
return stringBuilder.toString()
}
}

View File

@@ -22,7 +22,6 @@ import com.appttude.h_mal.farmr.model.Order
import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.model.Sortable
import com.appttude.h_mal.farmr.model.Success
import com.appttude.h_mal.farmr.utils.CURRENCY
import com.appttude.h_mal.farmr.utils.convertDateString
import com.appttude.h_mal.farmr.utils.formatAsCurrencyString
import com.appttude.h_mal.farmr.utils.sortedByOrder
@@ -239,7 +238,7 @@ class MainViewModel(
val data = shiftLiveData.value!!.applyFilters()
.sortList(sortAndOrder.first, sortAndOrder.second)
var currentRow = 0
val cells = data.mapIndexed { index, shift ->
val cells = data.map { shift ->
currentRow += 1
listOf(
Label(0, currentRow, shift.id.toString()),

View File

@@ -2,9 +2,9 @@ package com.appttude.h_mal.farmr.viewmodel
import com.appttude.h_mal.farmr.base.BaseViewModel
import com.appttude.h_mal.farmr.data.Repository
import com.appttude.h_mal.farmr.data.prefs.DESCRIPTION
import com.appttude.h_mal.farmr.data.prefs.DATE_IN
import com.appttude.h_mal.farmr.data.prefs.DATE_OUT
import com.appttude.h_mal.farmr.data.prefs.DESCRIPTION
import com.appttude.h_mal.farmr.data.prefs.TYPE
import com.appttude.h_mal.farmr.model.FilterStore

View File

@@ -10,7 +10,6 @@ import io.mockk.mockk
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyLong
import java.util.UUID
import kotlin.test.assertEquals
import kotlin.test.assertIs

View File

@@ -2,9 +2,12 @@ package com.appttude.h_mal.farmr.utils
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
import com.appttude.h_mal.farmr.model.ShiftType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.mockito.ArgumentMatchers
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@@ -36,4 +39,111 @@ fun <T> LiveData<T>.getOrAwaitValue(
fun sleep(millis: Long = 1000) {
runBlocking(Dispatchers.Default) { delay(millis) }
}
}
fun getShifts() = listOf(
ShiftObject(
ArgumentMatchers.anyLong(),
ShiftType.HOURLY.type,
"Day one",
"2023-08-01",
"12:00",
"13:00",
1f,
ArgumentMatchers.anyInt(),
ArgumentMatchers.anyFloat(),
10f,
10f
),
ShiftObject(
ArgumentMatchers.anyLong(),
ShiftType.HOURLY.type,
"Day two",
"2023-08-02",
"12:00",
"13:00",
1f,
ArgumentMatchers.anyInt(),
ArgumentMatchers.anyFloat(),
10f,
10f
),
ShiftObject(
ArgumentMatchers.anyLong(),
ShiftType.HOURLY.type,
"Day three",
"2023-08-03",
"12:00",
"13:00",
1f,
30,
ArgumentMatchers.anyFloat(),
10f,
5f
),
ShiftObject(
ArgumentMatchers.anyLong(),
ShiftType.HOURLY.type,
"Day four",
"2023-08-04",
"12:00",
"13:00",
1f,
30,
ArgumentMatchers.anyFloat(),
10f,
5f
),
ShiftObject(
ArgumentMatchers.anyLong(),
ShiftType.PIECE.type,
"Day five",
"2023-08-05",
ArgumentMatchers.anyString(),
ArgumentMatchers.anyString(),
ArgumentMatchers.anyFloat(),
ArgumentMatchers.anyInt(),
1f,
10f,
10f
),
ShiftObject(
ArgumentMatchers.anyLong(),
ShiftType.PIECE.type,
"Day six",
"2023-08-06",
ArgumentMatchers.anyString(),
ArgumentMatchers.anyString(),
ArgumentMatchers.anyFloat(),
ArgumentMatchers.anyInt(),
1f,
10f,
10f
),
ShiftObject(
ArgumentMatchers.anyLong(),
ShiftType.PIECE.type,
"Day seven",
"2023-08-07",
ArgumentMatchers.anyString(),
ArgumentMatchers.anyString(),
ArgumentMatchers.anyFloat(),
ArgumentMatchers.anyInt(),
1f,
10f,
10f
),
ShiftObject(
ArgumentMatchers.anyLong(),
ShiftType.PIECE.type,
"Day eight",
"2023-08-08",
ArgumentMatchers.anyString(),
ArgumentMatchers.anyString(),
ArgumentMatchers.anyFloat(),
ArgumentMatchers.anyInt(),
1f,
10f,
10f
),
)

View File

@@ -0,0 +1,93 @@
package com.appttude.h_mal.farmr.viewmodel
import android.os.Bundle
import com.appttude.h_mal.farmr.data.legacydb.ShiftObject
import com.appttude.h_mal.farmr.utils.ID
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Test
import org.mockito.ArgumentMatchers.anyLong
import kotlin.test.assertIs
class InfoViewModelTest : ShiftViewModelTest<InfoViewModel>() {
@Test
fun retrieveData_validBundleAndId_successfulRetrieval() {
// Arrange
val id = anyLong()
val shift = mockk<ShiftObject>()
val bundle = mockk<Bundle>()
// Act
every { repository.readSingleShiftFromDatabase(id) }.returns(shift)
every { bundle.getLong(ID) }.returns(id)
viewModel.retrieveData(bundle)
// Assert
assertIs<ShiftObject>(retrieveCurrentData())
assertEquals(
retrieveCurrentData(),
shift
)
}
@Test
fun retrieveData_noValidBundleAndId_unsuccessfulRetrieval() {
// Arrange
val id = anyLong()
val shift = mockk<ShiftObject>()
val bundle = mockk<Bundle>()
// Act
every { repository.readSingleShiftFromDatabase(id) }.returns(shift)
every { bundle.getLong(ID) }.returns(id)
viewModel.retrieveData(null)
// Assert
assertEquals(
retrieveCurrentError(),
"Failed to retrieve shift"
)
}
@Test
fun retrieveData_validBundleNoShift_successfulRetrieval() {
// Arrange
val id = anyLong()
val bundle = mockk<Bundle>()
// Act
every { repository.readSingleShiftFromDatabase(id) }.returns(null)
every { bundle.getLong(ID) }.returns(id)
viewModel.retrieveData(bundle)
// Assert
assertEquals(
retrieveCurrentError(),
"Failed to retrieve shift"
)
}
@Test
fun buildDurationSummary_validHourlyShift_successfulRetrieval() {
// Arrange
val shift = getShifts()[0]
val shiftWithBreak = getShifts()[3]
// Act
val summary = viewModel.buildDurationSummary(shift)
val summaryWithBreak = viewModel.buildDurationSummary(shiftWithBreak)
// Assert
assertEquals(
"1 Hours 0 Minutes ",
summary
)
assertEquals(
"1 Hours 0 Minutes (+ 30 minutes break)",
summaryWithBreak
)
}
}

View File

@@ -10,17 +10,14 @@ import com.appttude.h_mal.farmr.data.prefs.TYPE
import com.appttude.h_mal.farmr.model.ShiftType
import com.appttude.h_mal.farmr.model.ViewState
import com.appttude.h_mal.farmr.utils.getOrAwaitValue
import com.appttude.h_mal.farmr.utils.getShifts
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyList
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import java.util.concurrent.TimeoutException
import kotlin.test.assertEquals
@@ -130,110 +127,4 @@ class MainViewModelTest {
Pair(TYPE, type)
)
private fun getShifts() = listOf(
ShiftObject(
anyLong(),
ShiftType.HOURLY.type,
"Day one",
"2023-08-01",
"12:00",
"13:00",
1f,
anyInt(),
anyFloat(),
10f,
10f
),
ShiftObject(
anyLong(),
ShiftType.HOURLY.type,
"Day two",
"2023-08-02",
"12:00",
"13:00",
1f,
anyInt(),
anyFloat(),
10f,
10f
),
ShiftObject(
anyLong(),
ShiftType.HOURLY.type,
"Day three",
"2023-08-03",
"12:00",
"13:00",
1f,
30,
anyFloat(),
10f,
5f
),
ShiftObject(
anyLong(),
ShiftType.HOURLY.type,
"Day four",
"2023-08-04",
"12:00",
"13:00",
1f,
30,
anyFloat(),
10f,
5f
),
ShiftObject(
anyLong(),
ShiftType.PIECE.type,
"Day five",
"2023-08-05",
anyString(),
anyString(),
anyFloat(),
anyInt(),
1f,
10f,
10f
),
ShiftObject(
anyLong(),
ShiftType.PIECE.type,
"Day six",
"2023-08-06",
anyString(),
anyString(),
anyFloat(),
anyInt(),
1f,
10f,
10f
),
ShiftObject(
anyLong(),
ShiftType.PIECE.type,
"Day seven",
"2023-08-07",
anyString(),
anyString(),
anyFloat(),
anyInt(),
1f,
10f,
10f
),
ShiftObject(
anyLong(),
ShiftType.PIECE.type,
"Day eight",
"2023-08-08",
anyString(),
anyString(),
anyFloat(),
anyInt(),
1f,
10f,
10f
),
)
}

2
fastlane/Appfile Normal file
View File

@@ -0,0 +1,2 @@
json_key_file("google-play-key.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
package_name("com.appttude.h_mal.farmr") # e.g. com.krausefx.app

29
fastlane/Fastfile Normal file
View File

@@ -0,0 +1,29 @@
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
# https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
# https://docs.fastlane.tools/plugins/available-plugins
#
# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane
default_platform(:android)
platform :android do
desc "Runs all the tests"
lane :test do
gradle(task: "test")
end
desc "Deploy a new version to the Google Play"
lane :deploy do
gradle(task: "clean assembleRelease")
upload_to_play_store
end
end

40
fastlane/README.md Normal file
View File

@@ -0,0 +1,40 @@
fastlane documentation
----
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
```sh
xcode-select --install
```
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
# Available Actions
## Android
### android test
```sh
[bundle exec] fastlane android test
```
Runs all the tests
### android deploy
```sh
[bundle exec] fastlane android deploy
```
Deploy a new version to the Google Play
----
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).