- refactoring of admin app

- file structure updated

Took 34 hours 38 minutes
This commit is contained in:
2023-05-06 23:59:13 +01:00
parent 0c09966002
commit 2677b3ae25
80 changed files with 1476 additions and 828 deletions

View File

@@ -79,7 +79,7 @@ jobs:
parameters: parameters:
flavour: flavour:
type: string type: string
default: "" default: "Driver"
executor: executor:
name: android/android-machine name: android/android-machine
steps: steps:
@@ -101,7 +101,7 @@ workflows:
build-release-driver: build-release-driver:
jobs: jobs:
- build-and-test: - build-and-test:
flavour: Driver flavour: "Driver"
- assemble-and-release: - assemble-and-release:
flavour: "Driver" flavour: "Driver"
filters: filters:
@@ -113,9 +113,9 @@ workflows:
build-release-admin: build-release-admin:
jobs: jobs:
- build-and-test: - build-and-test:
flavour: Admin flavour: "Admin"
- assemble-and-release: - assemble-and-release:
flavour: Admin flavour: "Admin"
filters: filters:
branches: branches:
only: main_admin only: main_admin

View File

@@ -66,22 +66,25 @@ android {
flavorDimensions "Default" flavorDimensions "Default"
productFlavors { productFlavors {
driver { driver {
applicationId "h_mal.appttude.com.driver"
versionCode 7 versionCode 7
versionName "2.0.0" versionName "2.0.0"
} }
admin { admin {
applicationIdSuffix ".admin" applicationId "h_mal.appttude.com.driver.admin"
versionCode 4 versionCode 4
versionName "0.0.5" versionName "0.0.5"
} }
} }
sourceSets { sourceSets {
driver { driver {
java.srcDirs += 'src/driver/java'
manifest { manifest {
srcFile 'src/driver/AndroidManifest.xml' srcFile 'src/driver/AndroidManifest.xml'
} }
} }
admin { admin {
java.srcDirs += 'src/admin/java'
manifest { manifest {
srcFile 'src/admin/AndroidManifest.xml' srcFile 'src/admin/AndroidManifest.xml'
} }
@@ -108,6 +111,7 @@ dependencies {
implementation 'androidx.viewpager:viewpager:1.0.0' implementation 'androidx.viewpager:viewpager:1.0.0'
implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.legacy:legacy-support-v4:1.0.0"
testImplementation "junit:junit:4.13.2" testImplementation "junit:junit:4.13.2"
implementation "androidx.preference:preference-ktx:1.2.0"
/ * Android Espresso */ / * Android Espresso */
def testJunitVersion = "1.1.5" def testJunitVersion = "1.1.5"
def testRunnerVersion = "1.5.2" def testRunnerVersion = "1.5.2"
@@ -128,6 +132,7 @@ dependencies {
implementation "com.google.firebase:firebase-auth:$firebaseAuth" implementation "com.google.firebase:firebase-auth:$firebaseAuth"
implementation "com.google.firebase:firebase-storage:$firebaseStorage" implementation "com.google.firebase:firebase-storage:$firebaseStorage"
implementation "com.google.firebase:firebase-database:$firebaseDatabase" implementation "com.google.firebase:firebase-database:$firebaseDatabase"
implementation 'com.firebaseui:firebase-ui-database:8.0.2'
/ * Photoviewer */ / * Photoviewer */
implementation "com.github.chrisbanes:PhotoView:2.1.0" implementation "com.github.chrisbanes:PhotoView:2.1.0"
/ * Picasso photo loader */ / * Picasso photo loader */

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- To auto-complete the email text field in the login form with the user's emails -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round">
</application>
</manifest>

View File

@@ -0,0 +1,65 @@
package h_mal.appttude.com.driver.application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import h_mal.appttude.com.driver.data.FirebaseAuthSource
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.data.FirebaseStorageSource
import h_mal.appttude.com.driver.data.prefs.PreferenceProvider
import h_mal.appttude.com.driver.viewmodels.*
class ApplicationViewModelFactory(
private val auth: FirebaseAuthSource,
private val database: FirebaseDatabaseSource,
private val storage: FirebaseStorageSource,
private val preferences: PreferenceProvider
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
with(modelClass) {
return when {
isAssignableFrom(UserViewModel::class.java) -> UserViewModel(auth)
isAssignableFrom(MainViewModel::class.java) -> MainViewModel(auth, database)
isAssignableFrom(UpdateUserViewModel::class.java) -> UpdateUserViewModel(
auth,
storage
)
isAssignableFrom(RoleViewModel::class.java) -> RoleViewModel(
auth,
database,
storage
)
isAssignableFrom(DriverLicenseViewModel::class.java) -> DriverLicenseViewModel(
database
)
isAssignableFrom(DriverProfileViewModel::class.java) -> DriverProfileViewModel(
database
)
isAssignableFrom(PrivateHireLicenseViewModel::class.java) -> PrivateHireLicenseViewModel(
database
)
isAssignableFrom(VehicleProfileViewModel::class.java) -> VehicleProfileViewModel(
database
)
isAssignableFrom(InsuranceViewModel::class.java) -> InsuranceViewModel(database)
isAssignableFrom(MotViewModel::class.java) -> MotViewModel(database)
isAssignableFrom(LogbookViewModel::class.java) -> LogbookViewModel(database)
isAssignableFrom(PrivateHireVehicleViewModel::class.java) -> PrivateHireVehicleViewModel(
database
)
isAssignableFrom(DriverOverviewViewModel::class.java) -> DriverOverviewViewModel(
auth,
database
)
isAssignableFrom(SuperUserViewModel::class.java) -> SuperUserViewModel(
database,
preferences
)
else -> throw IllegalArgumentException("Unknown ViewModel class")
} as T
}
}
}

View File

@@ -0,0 +1,40 @@
package h_mal.appttude.com.driver.base
import com.google.firebase.database.DatabaseReference
import h_mal.appttude.com.driver.data.FirebaseCompletion
import h_mal.appttude.com.driver.utils.Coroutines.io
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
import h_mal.appttude.com.driver.utils.getDataFromDatabaseRef
import h_mal.appttude.com.driver.utils.isNotNull
abstract class DataViewerBaseViewModel<T : Any> : BaseViewModel() {
var uid: String? = null
abstract fun getDatabaseRef(uid: String): DatabaseReference
fun initViewModel(uid: String) {
this.uid = uid
retrieveData(uid)
}
fun fetchData() {
@Suppress("IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION")
uid.isNotNull {
retrieveData(it)
return
}
onError("Failed to retrieve data for user")
}
private fun retrieveData(uid: String) {
val clazz = getGenericClassAt<T>(0)
io {
doTryOperation("Failed to retrieve ${clazz.simpleName}") {
val data = getDatabaseRef(uid).getDataFromDatabaseRef(clazz.java)
onSuccess(data ?: FirebaseCompletion.Default)
}
}
}
}

View File

@@ -0,0 +1,55 @@
package h_mal.appttude.com.driver.base
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.EditText
import androidx.core.view.children
import androidx.viewbinding.ViewBinding
import h_mal.appttude.com.driver.data.USER_CONST
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
import h_mal.appttude.com.driver.utils.hide
import h_mal.appttude.com.driver.utils.isTrue
open class DataViewerFragment<V : DataViewerBaseViewModel<T>, VB : ViewBinding, T : Any> :
BaseFragment<V, VB>() {
override fun setupView(binding: VB) {
super.setupView(binding)
(binding.root as ViewGroup).children.forEach { disableViews(it) }
}
private fun disableViews(view: View) {
if (view is EditText)
view.isFocusable = false
if (view is CheckBox)
view.isFocusable = false
else if (view is ViewGroup)
view.children.forEach { disableViews(it) }
}
override fun onStart() {
super.onStart()
requireArguments().getString(USER_CONST)?.let {
viewModel.initViewModel(it)
}
}
@Suppress("UNCHECKED_CAST")
override fun onSuccess(data: Any?) {
super.onSuccess(data)
data?.let { (data::class == getGenericClassAt<T>(2)) }?.isTrue {
setFields(data as T)
}
}
open fun setFields(data: T) {}
fun viewsToHide(vararg view: View) {
view.forEach { it.hide() }
}
}

View File

@@ -0,0 +1,17 @@
package h_mal.appttude.com.driver.model
enum class SortOption(val key: String, val label: String) {
NAME("driver_profile/driver_details/forenames","Driver Name"),
NUMBER("driver_number", "Driver Number");
// APPROVAL("forenames")
companion object {
fun getSortOptionByLabel(label: String?): SortOption? {
return values().firstOrNull { i -> i.label == label }
}
fun getPositionByLabel(label: String?): Int? {
val sortOption = getSortOptionByLabel(label) ?: return null
return values().indexOf(sortOption)
}
}
}

View File

@@ -1,34 +1,13 @@
package h_mal.appttude.com.driver.admin.objects package h_mal.appttude.com.driver.objects
class ApprovalsObject { data class ApprovalsObject (
var driver_details_approval: Int = 0 var driver_details_approval: Int = 0,
var driver_license_approval: Int = 0 var driver_license_approval: Int = 0,
var private_hire_approval: Int = 0 var private_hire_approval: Int = 0,
var vehicle_details_approval: Int = 0 var vehicle_details_approval: Int = 0,
var mot_details_approval: Int = 0 var mot_details_approval: Int = 0,
var insurance_details_approval: Int = 0 var insurance_details_approval: Int = 0,
var log_book_approval: Int = 0 var log_book_approval: Int = 0,
var ph_car_approval: Int = 0 var ph_car_approval: Int = 0,
)
constructor()
constructor(
driver_details_approval: Int,
driver_license_approval: Int,
private_hire_approval: Int,
vehicle_details_approval: Int,
mot_details_approval: Int,
insurance_details_approval: Int,
log_book_approval: Int,
private_hire_vehicle_approval: Int
) {
this.driver_details_approval = driver_details_approval
this.driver_license_approval = driver_license_approval
this.private_hire_approval = private_hire_approval
this.vehicle_details_approval = vehicle_details_approval
this.mot_details_approval = mot_details_approval
this.insurance_details_approval = insurance_details_approval
this.log_book_approval = log_book_approval
this.ph_car_approval = private_hire_vehicle_approval
}
}

View File

@@ -1,33 +1,14 @@
package h_mal.appttude.com.driver.admin.objects package h_mal.appttude.com.driver.objects
import h_mal.appttude.com.driver.model.* import h_mal.appttude.com.driver.model.*
import java.util.*
class ArchiveObject {
var driver_license: HashMap<String, DriversLicense>? = null
var private_hire: HashMap<String, PrivateHireLicense>? = null
var vehicle_details: HashMap<String, VehicleProfile>? = null
var insurance_details: HashMap<String, Insurance>? = null
var mot_details: HashMap<String, Mot>? = null
var log_book: HashMap<String, Logbook>? = null
var ph_car: HashMap<String, PrivateHireVehicle>? = null
constructor() data class ArchiveObject(
constructor( var driver_license: HashMap<String, DriversLicense>? = HashMap(),
driver_license: HashMap<String, DriversLicense>?, var private_hire: HashMap<String, PrivateHireLicense>? = HashMap(),
private_hire: HashMap<String, PrivateHireLicense>?, var vehicle_details: HashMap<String, VehicleProfile>? = HashMap(),
vehicle_details: HashMap<String, VehicleProfile>?, var insurance_details: HashMap<String, Insurance>? = HashMap(),
insurance_details: HashMap<String, Insurance>?, var mot_details: HashMap<String, Mot>? = HashMap(),
mot_details: HashMap<String, Mot>?, var log_book: HashMap<String, Logbook>? = HashMap(),
log_book: HashMap<String, Logbook>?, var ph_car: HashMap<String, PrivateHireVehicle>? = HashMap(),
private_hire_vehicle: HashMap<String, PrivateHireVehicle>? )
) {
this.driver_license = driver_license
this.private_hire = private_hire
this.vehicle_details = vehicle_details
this.insurance_details = insurance_details
this.mot_details = mot_details
this.log_book = log_book
this.ph_car = private_hire_vehicle
}
}

View File

@@ -1,15 +1,10 @@
package h_mal.appttude.com.driver.admin.objects package h_mal.appttude.com.driver.objects
import com.google.firebase.database.IgnoreExtraProperties
class UserObject { @IgnoreExtraProperties
var profileName: String? = null data class UserObject (
var profileEmail: String? = null var profileName: String? = "",
var profilePicString: String? = null var profileEmail: String? = "",
var profilePicString: String? = "",
constructor() )
constructor(profileName: String?, profileEmail: String?, profilePicString: String?) {
this.profileName = profileName
this.profileEmail = profileEmail
this.profilePicString = profilePicString
}
}

View File

@@ -1,35 +1,15 @@
package h_mal.appttude.com.driver.admin.objects package h_mal.appttude.com.driver.objects
import h_mal.appttude.com.driver.admin.objects.wholeObject.DriverProfile import h_mal.appttude.com.driver.objects.wholeObject.DriverProfile
import h_mal.appttude.com.driver.admin.objects.wholeObject.VehicleProfile import h_mal.appttude.com.driver.objects.wholeObject.VehicleProfile
class WholeDriverObject { data class WholeDriverObject(
var driver_profile: DriverProfile? = null var driver_profile: DriverProfile? = DriverProfile(),
var role: String? = null var role: String? = "",
var archive: ArchiveObject? = null var archive: ArchiveObject? = ArchiveObject(),
var user_details: UserObject? = null var user_details: UserObject? = UserObject(),
var vehicle_profile: VehicleProfile? = null var vehicle_profile: VehicleProfile? = VehicleProfile(),
var approvalsObject: ApprovalsObject? = null var approvalsObject: ApprovalsObject? = ApprovalsObject(),
var driver_number: String? = null var driver_number: String? = "",
)
constructor(
driver_profile: DriverProfile?,
role: String?,
archive: ArchiveObject?,
user_details: UserObject?,
vehicle_profile: VehicleProfile?,
approvalsObject: ApprovalsObject?,
driver_number: String?
) {
this.driver_profile = driver_profile
this.role = role
this.archive = archive
this.user_details = user_details
this.vehicle_profile = vehicle_profile
this.approvalsObject = approvalsObject
this.driver_number = driver_number
}
constructor()
}

View File

@@ -1,24 +1,12 @@
package h_mal.appttude.com.driver.admin.objects.wholeObject package h_mal.appttude.com.driver.objects.wholeObject
import h_mal.appttude.com.driver.model.DriverProfile import h_mal.appttude.com.driver.model.DriverProfile
import h_mal.appttude.com.driver.model.DriversLicense import h_mal.appttude.com.driver.model.DriversLicense
import h_mal.appttude.com.driver.model.PrivateHireLicense import h_mal.appttude.com.driver.model.PrivateHireLicense
class DriverProfile { data class DriverProfile(
var driver_profile: DriverProfile? = null var driver_profile: DriverProfile? = DriverProfile(),
var driver_license: DriversLicense? = null var driver_license: DriversLicense? = DriversLicense(),
var private_hire: PrivateHireLicense? = null var private_hire: PrivateHireLicense? = PrivateHireLicense(),
)
constructor(
driver_profile: DriverProfile?,
driver_license: DriversLicense?,
private_hire: PrivateHireLicense?
) {
this.driver_profile = driver_profile
this.driver_license = driver_license
this.private_hire = private_hire
}
constructor()
}

View File

@@ -1,43 +0,0 @@
package h_mal.appttude.com.driver.admin.objects.wholeObject
import android.os.Parcel
import android.os.Parcelable
import h_mal.appttude.com.driver.admin.objects.WholeDriverObject
class MappedObject : Parcelable {
var userId: String? = null
var wholeDriverObject: WholeDriverObject? = null
constructor(userId: String?, wholeDriverObject: WholeDriverObject?) {
this.userId = userId
this.wholeDriverObject = wholeDriverObject
}
constructor()
protected constructor(`in`: Parcel) {
userId = `in`.readString()
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(userId)
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<MappedObject?> =
object : Parcelable.Creator<MappedObject?> {
override fun createFromParcel(`in`: Parcel): MappedObject? {
return MappedObject(`in`)
}
override fun newArray(size: Int): Array<MappedObject?> {
return arrayOfNulls(size)
}
}
}
}

View File

@@ -1,31 +1,12 @@
package h_mal.appttude.com.driver.admin.objects.wholeObject package h_mal.appttude.com.driver.objects.wholeObject
import h_mal.appttude.com.driver.model.Insurance import h_mal.appttude.com.driver.model.*
import h_mal.appttude.com.driver.model.Logbook
import h_mal.appttude.com.driver.model.PrivateHireVehicle
import h_mal.appttude.com.driver.model.Mot
import h_mal.appttude.com.driver.model.VehicleProfile import h_mal.appttude.com.driver.model.VehicleProfile
data class VehicleProfile (
class VehicleProfile { var insurance_details: Insurance? = Insurance(),
var insurance_details: Insurance? = null var log_book: Logbook? = Logbook(),
var log_book: Logbook? = null var mot_details: Mot? = Mot(),
var mot_details: Mot? = null var vehicle_details: VehicleProfile? = VehicleProfile(),
var vehicle_details: VehicleProfile? = null var privateHireVehicle: PrivateHireVehicle? = PrivateHireVehicle()
var privateHireVehicle: PrivateHireVehicle? = null )
constructor()
constructor(
insurance_details: Insurance?,
log_book: Logbook?,
mot_details: Mot?,
vehicle_details: VehicleProfile?,
private_hire_vehicle: PrivateHireVehicle?
) {
this.insurance_details = insurance_details
this.log_book = log_book
this.mot_details = mot_details
this.vehicle_details = vehicle_details
this.privateHireVehicle = private_hire_vehicle
}
}

View File

@@ -1,188 +1,77 @@
package h_mal.appttude.com.driver.ui package h_mal.appttude.com.driver.ui
import android.app.Activity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ArrayAdapter import android.widget.BaseAdapter
import h_mal.appttude.com.driver.admin.objects.wholeObject.MappedObject import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import h_mal.appttude.com.driver.R import h_mal.appttude.com.driver.R
import h_mal.appttude.com.driver.databinding.ApprovalListItemBinding
import h_mal.appttude.com.driver.utils.hide
class ApprovalListAdapter( class ApprovalListAdapter(
private val activity: Activity, private val layoutInflater: LayoutInflater,
objects: Array<MappedObject?> private var approvals: Map<String, Int?>,
) : ArrayAdapter<MappedObject?>(activity, 0, objects) { private val callback: (String) -> Unit
) : BaseAdapter() {
override fun getCount(): Int = approvals.size
override fun getItem(position: Int): Map.Entry<String, Int?> = approvals.entries.elementAt(position)
override fun getItemId(position: Int): Long = position.toLong()
var mappedObject: MappedObject? = objects[0]
var names: Array<String> = arrayOf(
"Driver Profile",
"Driver License",
"Private Hire",
"Vehicle Profile",
"Insurance",
"MOT",
"Logbook",
"P/H Vehicle"
)
var approvalCode: Int = 0
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var listItemView: View? = convertView var listItemView: View? = convertView
val binding: ApprovalListItemBinding
if (listItemView == null) { if (listItemView == null) {
listItemView = LayoutInflater.from(activity).inflate( binding = ApprovalListItemBinding.inflate(layoutInflater, parent, false)
R.layout.approval_list_grid_item, parent, false listItemView = binding.root
) listItemView.setTag(listItemView.id, binding)
} else {
binding = listItemView.getTag(listItemView.id) as ApprovalListItemBinding
}
val key = getItem(position).key
val itemValue = getItem(position).value
binding.approvalText.text = key
if (itemValue != 0) {
binding.root.setOnClickListener { callback.invoke(key) }
}
binding.approvalIv.setImageResource(getImageResourceBasedOnApproval(itemValue))
binding.approvalStatus.text = listItemView.context.getString(getStringResourceBasedOnApproval(itemValue))
if (position == 0) {
binding.divider.hide()
}
return (listItemView)
}
@DrawableRes
private fun getImageResourceBasedOnApproval(value: Int?): Int {
return when(value) {
0 -> R.drawable.denied
1 -> R.drawable.denied
2 -> R.drawable.pending
3 -> R.drawable.approved
else -> R.drawable.pending
} }
// approvalCode = getApprovalStatusCode(position)
// val textView: TextView = listItemView!!.findViewById(R.id.approval_text)
// textView.text = names.get(position)
// val imageView: ImageView = listItemView.findViewById(R.id.approval_iv)
// imageView.setImageResource(
// MainActivity.approvalsClass!!.setImageResource(
// approvalCode
// )
// )
// imageView.setOnClickListener {
// SetApprovalDialog(
// approvalCode,
// activity,
// mappedObject.userId,
// position,
// imageView
// )
// }
// val archiveImage: ImageView = listItemView.findViewById(R.id.archive_icon)
// mappedObject.wholeDriverObject?.archive?.let {
//
//
// archiveImage.visibility = getArchive(
// position,
// it
// )
// archiveImage.setOnClickListener {
// var s: String? = null
// when (position) {
// 1 -> s = FirebaseClass.DRIVERS_LICENSE_FIREBASE
// 2 -> s = FirebaseClass.PRIVATE_HIRE_FIREBASE
// 3 -> s = FirebaseClass.VEHICLE_DETAILS_FIREBASE
// 4 -> s = FirebaseClass.INSURANCE_FIREBASE
// 5 -> s = FirebaseClass.MOT_FIREBASE
// 6 -> s = FirebaseClass.LOG_BOOK_FIREBASE
// 7 -> s = FirebaseClass.PRIVATE_HIRE_VEHICLE_LICENSE
// }
//// executeFragment(ArchiveFragment(), mappedObject.userId, s)
// }
// }
// listItemView.setOnClickListener(View.OnClickListener { getFragment(position) })
// listItemView.minimumHeight = parent.height / 4
// listItemView.setPadding(
// convertDpToPixel(9f, context).toInt(),
// convertDpToPixel(9f, context).toInt(),
// convertDpToPixel(9f, context).toInt(),
// convertDpToPixel(9f, context).toInt()
// )
return (listItemView)!!
} }
// override fun getCount(): Int { @StringRes
// return 8 private fun getStringResourceBasedOnApproval(value: Int?): Int {
// } return when(value) {
// 0 -> R.string.not_submitted
// private fun getArchive(i: Int, archiveObject: ArchiveObject?): Int { 1 -> R.string.denied
// var o: Any? = null 2 -> R.string.pending
// val visible: Int 3 -> R.string.approved
// when (i) { else -> R.string.pending
// 0 -> { } }
// 1 -> o = archiveObject!!.driver_license }
// 2 -> o = archiveObject!!.private_hire
// 3 -> o = archiveObject!!.vehicle_details
// 4 -> o = archiveObject!!.insurance_details
// 5 -> o = archiveObject!!.mot_details
// 6 -> o = archiveObject!!.log_book
// 7 -> o = archiveObject!!.ph_car
// }
// if (o != null) {
// visible = View.VISIBLE
// } else {
// visible = View.GONE
// }
// return visible
// }
//
// private fun getFragment(i: Int) {
// lateinit var f: Fragment
// val driverProfile by lazy { mappedObject.wholeDriverObject?.driver_profile }
// val vehicleProfile by lazy { mappedObject.wholeDriverObject?.vehicle_profile }
// val o: Any? = when (i) {
// 0 -> {
// f = DriverProfileFragment()
// driverProfile?.driver_profile
// }
// 1 -> {
// f = DriverLicenseFragment()
// driverProfile?.driver_license
// }
// 2 -> {
// f = PrivateHireLicenseFragment()
// driverProfile?.private_hire
// }
// 3 -> {
// f = VehicleProfileFragment()
// vehicleProfile?.vehicle_details
// }
// 4 -> {
// f = InsuranceFragment()
// vehicleProfile?.insurance_details
// }
// 5 -> {
// f = MotFragment()
// vehicleProfile?.insurance_details
// }
// 6 -> {
// f = LogbookFragment()
// vehicleProfile?.log_book
// }
// 7 -> {
// f = PrivateHireVehicleFragment()
// vehicleProfile?.privateHireVehicleObject
// }
// else -> null
// }
// if (o == null) {
//// executeFragment(f, mappedObject.userId)
// } else {
// MainActivity.archiveClass.openDialogArchive(
// context, o, mappedObject.userId, f
// )
// }
// }
//
// private fun getApprovalStatusCode(i: Int): Int {
// val statusCode = mappedObject.wholeDriverObject?.approvalsObject?.let{
// when (i) {
// 0 -> it.driver_details_approval
// 1 -> it.driver_license_approval
// 2 -> it.private_hire_approval
// 3 -> it.vehicle_details_approval
// 4 -> it.insurance_details_approval
// 5 -> it.mot_details_approval
// 6 -> it.log_book_approval
// 7 -> it.ph_car_approval
// else -> FirebaseClass.NO_DATE_PRESENT
// }
// }
// return statusCode ?: FirebaseClass.NO_DATE_PRESENT
// }
//
// companion object {
// fun convertDpToPixel(dp: Float, context: Context): Float {
// return dp * (context.resources
// .displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
// }
// }
fun updateAdapter(data: Map<String, Int?>) {
approvals = data
notifyDataSetChanged()
}
} }

View File

@@ -0,0 +1,58 @@
package h_mal.appttude.com.driver.ui
import android.view.View
import android.widget.ListView
import h_mal.appttude.com.driver.R
import h_mal.appttude.com.driver.base.BaseFragment
import h_mal.appttude.com.driver.data.USER_CONST
import h_mal.appttude.com.driver.databinding.FragmentUserMainBinding
import h_mal.appttude.com.driver.utils.navigateTo
import h_mal.appttude.com.driver.utils.toBundle
import h_mal.appttude.com.driver.viewmodels.DriverOverviewViewModel
import java.io.IOException
class DriverOverviewFragment : BaseFragment<DriverOverviewViewModel, FragmentUserMainBinding>() {
private lateinit var listView: ListView
private lateinit var driverId: String
override fun setupView(binding: FragmentUserMainBinding) {
listView = binding.approvalsList
driverId = requireArguments().getString(USER_CONST) ?: throw IOException("No user ID has been passed")
viewModel.loadDriverApprovals(driverId)
}
override fun onSuccess(data: Any?) {
super.onSuccess(data)
@Suppress("UNCHECKED_CAST")
if (data is Map<*, *>) {
if (listView.adapter == null) {
listView.adapter = ApprovalListAdapter(layoutInflater, data as Map<String, Int?>) {
this.view?.applyNavigation(it)
}
listView.isScrollContainer = false
} else {
(listView.adapter as ApprovalListAdapter).updateAdapter(data as Map<String, Int?>)
}
}
}
private fun View.applyNavigation(key: String) {
val navId = when (key) {
context.getString(R.string.driver_profile) -> R.id.to_driverProfileFragment
context.getString(R.string.drivers_license) -> R.id.to_driverLicenseFragment
context.getString(R.string.private_hire_license) -> R.id.to_privateHireLicenseFragment
context.getString(R.string.vehicle_profile) -> R.id.to_vehicleProfileFragment
context.getString(R.string.insurance) -> R.id.to_insuranceFragment
context.getString(R.string.m_o_t) -> R.id.to_motFragment
context.getString(R.string.log_book) -> R.id.to_logbookFragment
context.getString(R.string.private_hire_vehicle_license) -> R.id.to_privateHireVehicleFragment
else -> {
throw StringIndexOutOfBoundsException("No resource for $key")
}
}
navigateTo(navId, driverId.toBundle(USER_CONST))
}
}

View File

@@ -1,153 +1,158 @@
package h_mal.appttude.com.driver.ui package h_mal.appttude.com.driver.ui
import android.app.AlertDialog import android.app.AlertDialog
import android.content.DialogInterface
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.* import android.view.*
import androidx.fragment.app.Fragment import android.widget.EditText
import androidx.recyclerview.widget.LinearLayoutManager import android.widget.LinearLayout
import androidx.recyclerview.widget.RecyclerView import androidx.core.view.MenuProvider
import com.google.firebase.database.DataSnapshot import androidx.lifecycle.Lifecycle
import com.google.firebase.database.DatabaseError import com.firebase.ui.database.FirebaseRecyclerAdapter
import com.google.firebase.database.DatabaseReference import com.firebase.ui.database.FirebaseRecyclerOptions
import com.google.firebase.database.ValueEventListener
import h_mal.appttude.com.driver.admin.objects.WholeDriverObject
import h_mal.appttude.com.driver.admin.objects.wholeObject.MappedObject
import h_mal.appttude.com.driver.R import h_mal.appttude.com.driver.R
import java.io.IOException import h_mal.appttude.com.driver.base.BaseFirebaseAdapter
import h_mal.appttude.com.driver.base.BaseFragment
import h_mal.appttude.com.driver.base.CustomViewHolder
import h_mal.appttude.com.driver.data.USER_CONST
import h_mal.appttude.com.driver.databinding.FragmentHomeSuperUserBinding
import h_mal.appttude.com.driver.databinding.ListItemLayoutBinding
import h_mal.appttude.com.driver.model.SortOption
import h_mal.appttude.com.driver.objects.UserObject
import h_mal.appttude.com.driver.objects.WholeDriverObject
import h_mal.appttude.com.driver.utils.*
import h_mal.appttude.com.driver.viewmodels.SuperUserViewModel
import java.util.* import java.util.*
class HomeSuperUserFragment : Fragment() { class HomeSuperUserFragment : BaseFragment<SuperUserViewModel, FragmentHomeSuperUserBinding>(), MenuProvider {
var users: DatabaseReference? = null private lateinit var adapter: FirebaseRecyclerAdapter<WholeDriverObject, CustomViewHolder<ListItemLayoutBinding>>
var mappedObjectList: MutableList<MappedObject>? = null
private var sharedPreferences: SharedPreferences? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
private var sortOrder: Int = 0 super.onViewCreated(view, savedInstanceState)
private val sortDesc: Boolean = false requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
private var recyclerViewAdapter: RecyclerViewAdapter? = null viewModel.retrieveDefaultFirebaseOptions()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
mappedObjectList = ArrayList()
users!!.addValueEventListener(valueEventListener)
sharedPreferences = requireActivity().getSharedPreferences("PREFS", 0)
} }
override fun onCreateView( override fun onSuccess(data: Any?) {
inflater: LayoutInflater, container: ViewGroup?, super.onSuccess(data)
savedInstanceState: Bundle? when (data) {
): View? { is FirebaseRecyclerOptions<*> -> setAdapterToRecyclerView(data)
// Inflate the layout for this fragment
val view: View = inflater.inflate(R.layout.fragment_home_super_user, container, false)
view.findViewById<RecyclerView>(R.id.recycler_view).apply {
layoutManager = LinearLayoutManager(context)
recyclerViewAdapter = RecyclerViewAdapter(context, mappedObjectList)
adapter = recyclerViewAdapter
} }
return view
} }
var valueEventListener: ValueEventListener = object : ValueEventListener { @Suppress("UNCHECKED_CAST")
override fun onDataChange(snapshot: DataSnapshot) { private fun setAdapterToRecyclerView(options: FirebaseRecyclerOptions<*>) {
mappedObjectList!!.clear() applyBinding {
Log.i("Count ", "" + snapshot.childrenCount) progressCircular.show()
for (postSnapshot: DataSnapshot in snapshot.children) { if (recyclerView.adapter == null) {
if ((postSnapshot.child("role").value.toString() == "driver")) { // create an adapter for the first time
mappedObjectList!!.add( adapter = createAdapter(options = options as FirebaseRecyclerOptions<WholeDriverObject>)
MappedObject( recyclerView.adapter = adapter
postSnapshot.key, postSnapshot.getValue( recyclerView.setHasFixedSize(true)
WholeDriverObject::class.java adapter.startListening()
) } else {
adapter.updateOptions(options as FirebaseRecyclerOptions<WholeDriverObject>)
}
}
}
override fun onStop() {
super.onStop()
adapter.stopListening()
}
private fun createAdapter(options: FirebaseRecyclerOptions<WholeDriverObject>): BaseFirebaseAdapter<WholeDriverObject, ListItemLayoutBinding> {
return object : BaseFirebaseAdapter<WholeDriverObject, ListItemLayoutBinding>(options, layoutInflater) {
override fun onBindViewHolder(
holder: CustomViewHolder<ListItemLayoutBinding>,
position: Int,
model: WholeDriverObject
) {
val userDetails: UserObject? = model.user_details
holder.viewBinding.apply {
driverPic.setGlideImage(userDetails?.profilePicString)
usernameText.text = userDetails?.profileName
emailaddressText.text = userDetails?.profileEmail
driverNo.run {
val number = model.driver_number ?: "0"
text = number
setOnClickListener {
val uid = getKeyAtPosition(position)
if (uid != null) {
showChangeNumberDialog(uid, number)
}
}
}
root.setOnClickListener {
it.navigateTo(
R.id.action_homeAdminFragment_to_userMainFragment,
snapshots.getSnapshot(position).key?.toBundle(USER_CONST)
) )
) }
} }
} }
sortDate(sortOrder, sortDesc)
} override fun onDataChanged() {
super.onDataChanged()
override fun onCancelled(databaseError: DatabaseError) { applyBinding {
// If there are no chat messages, show a view that invites the user to add a message.
} if (itemCount == 0) {
} emptyView.root.visibility = if (itemCount == 0) View.VISIBLE else View.GONE
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_calls_fragment, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.archive) {
val grpname: Array<String> = arrayOf("Driver Name", "Driver Number", "Approval")
sortOrder = sharedPreferences!!.getInt(SORT, 0)
val checkedItem: Int = sortOrder
var compareInt = 0
val click = DialogInterface.OnClickListener { dialog, _ ->
sortDate(compareInt, false)
dialog.dismiss()
}
val builder: AlertDialog.Builder = AlertDialog.Builder(context)
builder.setTitle("Sort by:")
.setSingleChoiceItems(
grpname,
checkedItem
) { _, pos -> compareInt = pos }
.setPositiveButton("Ascending", click)
.setNegativeButton("Descending", click)
.create().show()
}
return super.onOptionsItemSelected(item)
}
private fun sortDate(compareInt: Int, reversed: Boolean) {
val comparator: Comparator<MappedObject> = object : Comparator<MappedObject> {
override fun compare(o1: MappedObject, o2: MappedObject): Int {
when (compareInt) {
0 -> return o1.wholeDriverObject?.user_details?.profileName!!.compareTo(
o2.wholeDriverObject?.user_details?.profileName!!
)
1 -> {
var s1: String? = o1.wholeDriverObject?.driver_number
var s2: String? = o2.wholeDriverObject?.driver_number
if (o1.wholeDriverObject?.driver_number == null || (o1.wholeDriverObject!!
.driver_number == "0")
) {
s1 = ";"
}
if (o2.wholeDriverObject?.driver_number == null || (o2.wholeDriverObject!!
.driver_number == "0")
) {
s2 = ";"
}
return s1!!.compareTo((s2)!!)
} }
else -> {
throw IOException("dfdfs") progressCircular.hide()
}
// 2 -> return MainActivity.approvalsClass.getOverApprovalStatusCode(o1.wholeDriverObject) -
// MainActivity.approvalsClass.getOverApprovalStatusCode(o2.wholeDriverObject)
// else -> return MainActivity.approvalsClass.getOverApprovalStatusCode(
// o1.wholeDriverObject
// ) - MainActivity.approvalsClass.getOverApprovalStatusCode(o2.wholeDriverObject)
} }
} }
override fun connectionLost() {
requireContext().displayToast("No connection available")
}
} }
sharedPreferences!!.edit().putInt(SORT, compareInt).apply()
sharedPreferences!!.edit().putBoolean(REVERSED, reversed).apply()
if (reversed) {
Collections.sort(mappedObjectList, comparator.reversed())
} else {
Collections.sort(mappedObjectList, comparator)
}
recyclerViewAdapter!!.notifyDataSetChanged()
} }
companion object { private fun showChangeNumberDialog(defaultNumber: String, uid: String) {
private val SORT: String = "SORT" val inputText = EditText(context).apply {
private val REVERSED: String = "REVERSED" setText(defaultNumber)
setSelectAllOnFocus(true)
}
val layout = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
setPadding(28, 0, 56, 0)
addView(inputText)
}
AlertDialog.Builder(context).setTitle("Change Driver Number")
.setView(layout)
.setPositiveButton("Submit") { _, _ ->
val input = inputText.text?.toString()
viewModel.updateDriverNumber(uid, input)
}.create().show()
}
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_calls_fragment, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
if (menuItem.itemId == R.id.archive) {
displaySortOptions()
}
return true
}
private fun displaySortOptions() {
val groupName: Array<String> = arrayOf("Driver Name", "Driver Number")
val defaultPosition = viewModel.getSelectionAsPosition()
val builder: AlertDialog.Builder = AlertDialog.Builder(context)
builder.setTitle("Sort by:")
.setSingleChoiceItems(
groupName,
defaultPosition
) { _, pos ->
val option = SortOption.getSortOptionByLabel(groupName[pos])
viewModel.createFirebaseOptions(sort = option)
}
.create().show()
} }
} }

View File

@@ -1,102 +0,0 @@
package h_mal.appttude.com.driver.ui
import android.app.AlertDialog
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import h_mal.appttude.com.driver.admin.objects.UserObject
import h_mal.appttude.com.driver.admin.objects.wholeObject.MappedObject
import h_mal.appttude.com.driver.R
class RecyclerViewAdapter constructor(var context: Context?, var objects: List<MappedObject>?) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): RecyclerView.ViewHolder {
val viewCurrent: View =
LayoutInflater.from(context).inflate(R.layout.list_item_layout, viewGroup, false)
return ViewHolderMain(viewCurrent)
}
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, i: Int) {
val viewHolderCurrent: ViewHolderMain = viewHolder as ViewHolderMain
val mappedObject: MappedObject = objects!!.get(i)
val `object`: UserObject? = mappedObject.wholeDriverObject?.user_details
if (`object`!!.profilePicString != null) {
Picasso.get()
.load(`object`.profilePicString)
.resize(128, 128)
.placeholder(R.drawable.choice_img_round)
.into(viewHolderCurrent.profilePicImage)
} else {
viewHolderCurrent.profilePicImage.setImageResource(R.drawable.choice_img_round)
}
viewHolderCurrent.userNameTextView.setText(`object`.profileName)
viewHolderCurrent.userEmailTextView.setText(`object`.profileEmail)
if (mappedObject.wholeDriverObject?.driver_number == null) {
viewHolderCurrent.driverNo.text = "0"
} else {
val s: String = mappedObject.wholeDriverObject?.driver_number.toString()
viewHolderCurrent.driverNo.text = s
}
viewHolderCurrent.driverNo.setOnClickListener {
val builder: AlertDialog.Builder = AlertDialog.Builder(
context
)
val input: EditText = EditText(context)
val layout: LinearLayout = LinearLayout(context)
layout.orientation = LinearLayout.VERTICAL
layout.setPadding(28, 0, 56, 0)
input.setText(viewHolderCurrent.driverNo.text.toString())
input.setSelectAllOnFocus(true)
layout.addView(input)
builder.setTitle("Change Driver Number")
.setView(layout)
.setPositiveButton(
"Submit"
) { dialog, which ->
}.create()
.show()
}
// viewHolderCurrent.profileApprovalImage.setImageResource(
// MainActivity.approvalsClass!!.getOverApprovalStatusCode(mappedObject.wholeDriverObject)
// )
viewHolderCurrent.itemView.setOnClickListener {
val bundle: Bundle = Bundle()
bundle.putParcelable("mapped", mappedObject)
// executeFragment(UserMainFragment(), bundle)
}
}
override fun getItemCount(): Int {
return objects!!.size
}
internal inner class ViewHolderMain constructor(listItemView: View) :
RecyclerView.ViewHolder(listItemView) {
var profilePicImage: ImageView
var userNameTextView: TextView
var userEmailTextView: TextView
// CardView statusCard;
var profileApprovalImage: ImageView
var driverNo: TextView
init {
profilePicImage = listItemView.findViewById(R.id.driverPic)
userNameTextView = listItemView.findViewById(R.id.username_text)
// statusCard = listItemView.findViewById(R.id.status_icon);
userEmailTextView = listItemView.findViewById(R.id.emailaddress_text)
profileApprovalImage = listItemView.findViewById(R.id.approval_iv)
driverNo = listItemView.findViewById(R.id.driver_no)
}
}
}

View File

@@ -1,30 +0,0 @@
package h_mal.appttude.com.driver.ui
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.GridView
import androidx.fragment.app.Fragment
import h_mal.appttude.com.driver.admin.objects.wholeObject.MappedObject
import h_mal.appttude.com.driver.R
class UserMainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view: View = inflater.inflate(R.layout.fragment_user_main, container, false)
Log.i("UserMain", "onCreateView: height = " + view.height)
val mappedObject: MappedObject? = requireArguments().getParcelable<MappedObject>("mapped")
activity?.title = mappedObject?.wholeDriverObject?.user_details?.profileName
val listView: GridView = view.findViewById(R.id.approvals_list)
listView.adapter = ApprovalListAdapter(requireActivity(), arrayOf(mappedObject))
return view
}
}

View File

@@ -0,0 +1,26 @@
package h_mal.appttude.com.driver.ui.driverprofile
import h_mal.appttude.com.driver.base.DataViewerFragment
import h_mal.appttude.com.driver.databinding.FragmentDriverLicenseBinding
import h_mal.appttude.com.driver.model.DriversLicense
import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.DriverLicenseViewModel
class DriverLicenseFragment :
DataViewerFragment<DriverLicenseViewModel, FragmentDriverLicenseBinding, DriversLicense>() {
override fun setupView(binding: FragmentDriverLicenseBinding) {
super.setupView(binding)
viewsToHide(binding.submit, binding.searchImage)
}
override fun setFields(data: DriversLicense) {
super.setFields(data)
applyBinding {
driversliImg.setGlideImage(data.licenseImageString)
licNo.setText(data.licenseNumber)
licExpiry.setText(data.licenseExpiry)
}
}
}

View File

@@ -0,0 +1,31 @@
package h_mal.appttude.com.driver.ui.driverprofile
import h_mal.appttude.com.driver.base.DataViewerFragment
import h_mal.appttude.com.driver.databinding.FragmentDriverProfileBinding
import h_mal.appttude.com.driver.model.DriverProfile
import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.DriverProfileViewModel
class DriverProfileFragment :
DataViewerFragment<DriverProfileViewModel, FragmentDriverProfileBinding, DriverProfile>() {
override fun setupView(binding: FragmentDriverProfileBinding) {
super.setupView(binding)
viewsToHide(binding.submitDriver, binding.addPhoto)
}
override fun setFields(data: DriverProfile) {
super.setFields(data)
applyBinding {
driverPic.setGlideImage(data.driverPic)
namesInput.setText(data.forenames)
addressInput.setText(data.address)
postcodeInput.setText(data.postcode)
dobInput.setText(data.dob)
niNumber.setText(data.ni)
dateFirst.setText(data.dateFirst)
}
}
}

View File

@@ -0,0 +1,26 @@
package h_mal.appttude.com.driver.ui.driverprofile
import h_mal.appttude.com.driver.base.DataViewerFragment
import h_mal.appttude.com.driver.databinding.FragmentPrivateHireLicenseBinding
import h_mal.appttude.com.driver.model.PrivateHireLicense
import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.PrivateHireLicenseViewModel
class PrivateHireLicenseFragment : DataViewerFragment<PrivateHireLicenseViewModel, FragmentPrivateHireLicenseBinding, PrivateHireLicense>() {
override fun setupView(binding: FragmentPrivateHireLicenseBinding) {
super.setupView(binding)
viewsToHide(binding.submit, binding.uploadphlic)
}
override fun setFields(data: PrivateHireLicense) {
super.setFields(data)
applyBinding {
imageView2.setGlideImage(data.phImageString)
phNo.setText(data.phNumber)
phExpiry.setText(data.phExpiry)
}
}
}

View File

@@ -0,0 +1,45 @@
package h_mal.appttude.com.driver.ui.vehicleprofile
import android.net.Uri
import android.widget.ImageView
import h_mal.appttude.com.driver.base.DataViewerFragment
import h_mal.appttude.com.driver.databinding.FragmentInsuranceBinding
import h_mal.appttude.com.driver.model.Insurance
import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.InsuranceViewModel
class InsuranceFragment :
DataViewerFragment<InsuranceViewModel, FragmentInsuranceBinding, Insurance>() {
override fun setupView(binding: FragmentInsuranceBinding) {
super.setupView(binding)
viewsToHide(binding.submitIns, binding.uploadInsurance)
}
private fun updateImageCarousal(list: List<Any?>) {
applyBinding {
carouselView.setImageClickListener(null)
carouselView.setImageListener { i: Int, imageView: ImageView ->
when (list[i]) {
is Uri -> {
imageView.setGlideImage(list[i] as Uri)
}
is String -> imageView.setGlideImage(list[i] as String)
}
}
carouselView.pageCount = list.size
}
}
override fun setFields(data: Insurance) {
super.setFields(data)
applyBinding {
insurer.setText(data.insurerName)
insuranceExp.setText(data.expiryDate)
data.photoStrings?.let { updateImageCarousal(it) }
}
}
}

View File

@@ -0,0 +1,26 @@
package h_mal.appttude.com.driver.ui.vehicleprofile
import h_mal.appttude.com.driver.base.DataViewerFragment
import h_mal.appttude.com.driver.databinding.FragmentLogbookBinding
import h_mal.appttude.com.driver.model.Logbook
import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.LogbookViewModel
class LogbookFragment :
DataViewerFragment<LogbookViewModel, FragmentLogbookBinding, Logbook>() {
override fun setupView(binding: FragmentLogbookBinding) {
super.setupView(binding)
viewsToHide(binding.submitLb, binding.uploadLb)
}
override fun setFields(data: Logbook) {
super.setFields(data)
applyBinding {
logBookImg.setGlideImage(data.photoString)
v5cNo.setText(data.v5cnumber)
}
}
}

View File

@@ -0,0 +1,25 @@
package h_mal.appttude.com.driver.ui.vehicleprofile
import h_mal.appttude.com.driver.base.DataViewerFragment
import h_mal.appttude.com.driver.databinding.FragmentMotBinding
import h_mal.appttude.com.driver.model.Mot
import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.MotViewModel
class MotFragment : DataViewerFragment<MotViewModel, FragmentMotBinding, Mot>() {
override fun setupView(binding: FragmentMotBinding) {
super.setupView(binding)
viewsToHide(binding.submitMot, binding.uploadmot)
}
override fun setFields(data: Mot) {
super.setFields(data)
applyBinding {
motImg.setGlideImage(data.motImageString)
motExpiry.setText(data.motExpiry)
}
}
}

View File

@@ -0,0 +1,28 @@
package h_mal.appttude.com.driver.ui.vehicleprofile
import h_mal.appttude.com.driver.base.DataViewerFragment
import h_mal.appttude.com.driver.databinding.FragmentPrivateHireLicenseBinding
import h_mal.appttude.com.driver.model.PrivateHireVehicle
import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.PrivateHireVehicleViewModel
class PrivateHireVehicleFragment :
DataViewerFragment<PrivateHireVehicleViewModel, FragmentPrivateHireLicenseBinding, PrivateHireVehicle>() {
override fun setupView(binding: FragmentPrivateHireLicenseBinding) {
super.setupView(binding)
viewsToHide(binding.submit, binding.uploadphlic)
}
override fun setFields(data: PrivateHireVehicle) {
super.setFields(data)
applyBinding {
imageView2.setGlideImage(data.phCarImageString)
phNo.setText(data.phCarNumber)
phExpiry.setText(data.phCarExpiry)
}
}
}

View File

@@ -0,0 +1,31 @@
package h_mal.appttude.com.driver.ui.vehicleprofile
import h_mal.appttude.com.driver.base.DataViewerFragment
import h_mal.appttude.com.driver.databinding.FragmentVehicleSetupBinding
import h_mal.appttude.com.driver.model.VehicleProfile
import h_mal.appttude.com.driver.viewmodels.VehicleProfileViewModel
class VehicleProfileFragment :
DataViewerFragment<VehicleProfileViewModel, FragmentVehicleSetupBinding, VehicleProfile>() {
override fun setupView(binding: FragmentVehicleSetupBinding) {
super.setupView(binding)
viewsToHide(binding.submitVehicle)
}
override fun setFields(data: VehicleProfile) {
super.setFields(data)
applyBinding {
reg.setText(data.reg)
make.setText(data.make)
carModel.setText(data.model)
colour.setText(data.colour)
keeperName.setText(data.keeperName)
address.setText(data.keeperAddress)
postcode.setText(data.keeperPostCode)
startDate.setText(data.startDate)
seizedCheckbox.isChecked = data.isSeized
}
}
}

View File

@@ -0,0 +1,12 @@
package h_mal.appttude.com.driver.viewmodels
import h_mal.appttude.com.driver.base.DataViewerBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.model.DriversLicense
class DriverLicenseViewModel(
private val database: FirebaseDatabaseSource
) : DataViewerBaseViewModel<DriversLicense>() {
override fun getDatabaseRef(uid: String) = database.getDriverLicenseRef(uid)
}

View File

@@ -0,0 +1,53 @@
package h_mal.appttude.com.driver.viewmodels
import com.google.firebase.database.DatabaseReference
import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseAuthentication
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.objects.ApprovalsObject
import h_mal.appttude.com.driver.utils.Coroutines.io
import h_mal.appttude.com.driver.utils.getDataFromDatabaseRef
import kotlinx.coroutines.Job
class DriverOverviewViewModel(
auth: FirebaseAuthentication,
private val database: FirebaseDatabaseSource
) : DataSubmissionBaseViewModel<ApprovalsObject>(auth, database, null) {
private var driverId: String? = null
override val databaseRef: DatabaseReference = database.getApprovalsRef(driverId ?: "")
override val storageRef: StorageReference? = null
override val objectName: String = "Approvals"
override fun getDataFromDatabase(): Job = retrieveDataFromDatabase<ApprovalsObject>()
fun loadDriverApprovals(driverId: String?) {
this.driverId = driverId
io {
doTryOperation("Failed to retrieve $objectName") {
val data = driverId?.let {
database.getApprovalsRef(it).getDataFromDatabaseRef()
} ?: ApprovalsObject()
val mappedData = mapApprovalsForView(data)
onSuccess(mappedData)
return@doTryOperation
}
}
}
private fun mapApprovalsForView(data: ApprovalsObject): Map<String, Int> {
return mutableMapOf<String, Int>().apply {
put("Driver Profile", data.driver_details_approval)
put("Drivers License", data.driver_license_approval)
put("Private Hire License", data.private_hire_approval)
put("Vehicle Profile", data.vehicle_details_approval)
put("Insurance", data.insurance_details_approval)
put("M.O.T", data.mot_details_approval)
put("Log book", data.log_book_approval)
put("Private Hire Vehicle License", data.ph_car_approval)
}.toMap()
}
}

View File

@@ -0,0 +1,11 @@
package h_mal.appttude.com.driver.viewmodels
import h_mal.appttude.com.driver.base.DataViewerBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.model.DriverProfile
class DriverProfileViewModel(
private val database: FirebaseDatabaseSource
) : DataViewerBaseViewModel<DriverProfile>() {
override fun getDatabaseRef(uid: String) = database.getDriverDetailsRef(uid)
}

View File

@@ -0,0 +1,12 @@
package h_mal.appttude.com.driver.viewmodels
import h_mal.appttude.com.driver.base.DataViewerBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.model.Insurance
class InsuranceViewModel(
private val database: FirebaseDatabaseSource
) : DataViewerBaseViewModel<Insurance>() {
override fun getDatabaseRef(uid: String) = database.getInsuranceDetailsRef(uid)
}

View File

@@ -0,0 +1,12 @@
package h_mal.appttude.com.driver.viewmodels
import h_mal.appttude.com.driver.base.DataViewerBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.model.Logbook
class LogbookViewModel(
private val database: FirebaseDatabaseSource
) : DataViewerBaseViewModel<Logbook>() {
override fun getDatabaseRef(uid: String) = database.getLogbookRef(uid)
}

View File

@@ -0,0 +1,12 @@
package h_mal.appttude.com.driver.viewmodels
import h_mal.appttude.com.driver.base.DataViewerBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.model.Mot
class MotViewModel(
private val database: FirebaseDatabaseSource
) : DataViewerBaseViewModel<Mot>() {
override fun getDatabaseRef(uid: String) = database.getMotDetailsRef(uid)
}

View File

@@ -0,0 +1,12 @@
package h_mal.appttude.com.driver.viewmodels
import h_mal.appttude.com.driver.base.DataViewerBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.model.PrivateHireLicense
class PrivateHireLicenseViewModel(
private val database: FirebaseDatabaseSource
) : DataViewerBaseViewModel<PrivateHireLicense>() {
override fun getDatabaseRef(uid: String) = database.getPrivateHireRef(uid)
}

View File

@@ -0,0 +1,12 @@
package h_mal.appttude.com.driver.viewmodels
import h_mal.appttude.com.driver.base.DataViewerBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.model.PrivateHireVehicle
class PrivateHireVehicleViewModel(
private val database: FirebaseDatabaseSource
) : DataViewerBaseViewModel<PrivateHireVehicle>() {
override fun getDatabaseRef(uid: String) = database.getPrivateHireVehicleRef(uid)
}

View File

@@ -0,0 +1,60 @@
package h_mal.appttude.com.driver.viewmodels
import com.firebase.ui.database.FirebaseRecyclerOptions
import h_mal.appttude.com.driver.base.BaseViewModel
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.data.prefs.PreferenceProvider
import h_mal.appttude.com.driver.model.SortOption
import h_mal.appttude.com.driver.objects.WholeDriverObject
import h_mal.appttude.com.driver.utils.Coroutines.io
import h_mal.appttude.com.driver.utils.isNotNull
class SuperUserViewModel(
private val firebaseDatabaseSource: FirebaseDatabaseSource,
private val preferenceProvider: PreferenceProvider
) : BaseViewModel() {
fun retrieveDefaultFirebaseOptions() {
val optionLabel = preferenceProvider.getSortOption()
val option = SortOption.getSortOptionByLabel(optionLabel)
createFirebaseOptions(option)
}
fun createFirebaseOptions(sort: SortOption? = null) {
val ref = firebaseDatabaseSource.getUsersRef()
sort?.isNotNull { preferenceProvider.setSortOption(it.label) }
// val query = when(sort) {
// NAME, NUMBER -> ref.orderByChild(sort.key)
//// SortOption.APPROVAL -> TODO()
// else -> ref.orderByKey()
// }
val options = FirebaseRecyclerOptions.Builder<WholeDriverObject>()
.setQuery(ref.orderByKey(), WholeDriverObject::class.java)
.build()
onSuccess(options)
}
fun updateDriverNumber(uid: String, input: String?) {
io {
doTryOperation("failed update driver identifier") {
// validate input
if (input.isNullOrBlank()) {
onError("No driver identifier provided")
return@doTryOperation
}
firebaseDatabaseSource.postToDatabaseRed(firebaseDatabaseSource.getUserRef(uid), input)
}
}
}
fun getSelectionAsPosition(): Int {
val optionLabel = preferenceProvider.getSortOption()
return SortOption.getPositionByLabel(optionLabel) ?: -1
}
}

View File

@@ -0,0 +1,12 @@
package h_mal.appttude.com.driver.viewmodels
import h_mal.appttude.com.driver.base.DataViewerBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.model.VehicleProfile
class VehicleProfileViewModel(
private val database: FirebaseDatabaseSource
) : DataViewerBaseViewModel<VehicleProfile>() {
override fun getDatabaseRef(uid: String) = database.getVehicleDetailsRef(uid)
}

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:id="@+id/divider"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/colour_ten"
android:paddingEnd="28dp"
app:layout_constraintLeft_toLeftOf="@id/approval_status"
app:layout_constraintRight_toRightOf="@id/approval_status"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="RtlSymmetry" />
<ImageView
android:id="@+id/approval_iv"
android:layout_width="@dimen/status_size"
android:layout_height="@dimen/status_size"
android:layout_gravity="center"
android:layout_marginStart="28dp"
android:layout_marginTop="9dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/pending"
android:contentDescription="@string/approval_status" />
<TextView
android:id="@+id/approval_status"
style="@style/subheader"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="28dp"
android:layout_marginRight="28dp"
app:layout_constraintBottom_toBottomOf="@id/approval_iv"
app:layout_constraintLeft_toRightOf="@id/approval_iv"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/approval_iv"
tools:text="Pending Approval" />
<TextView
android:id="@+id/approval_text"
style="@style/headerStyle"
android:textSize="20sp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="0dp"
android:layout_marginEnd="16dp"
android:paddingBottom="9dp"
app:layout_constraintLeft_toLeftOf="@id/approval_status"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/approval_status"
tools:text="Private Hire License" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/empty_view_message"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,16 +1,42 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.HomeSuperUserFragment"> tools:context=".ui.HomeSuperUserFragment">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_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"
android:orientation="vertical"
tools:listitem="@layout/list_item_layout"> tools:listitem="@layout/list_item_layout">
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
</RelativeLayout> <ProgressBar
android:id="@+id/progress_circular"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:visibility="gone"
tools:visibility="visible"/>
<include
android:id="@+id/empty_view"
layout="@layout/empty_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:visibility="gone"
tools:visibility="visible"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,17 +1,32 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
style="@style/parent_constraint_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.UserMainFragment"> tools:context=".ui.DriverOverviewFragment">
<GridView <androidx.cardview.widget.CardView
android:id="@+id/approvals_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:numColumns="2" android:layout_gravity="center"
android:rowCount="4" android:layout_marginBottom="12dp"
android:stretchMode="columnWidth" app:cardBackgroundColor="@color/colour_nine"
tools:listitem="@layout/approval_list_grid_item" /> app:cardCornerRadius="28dp"
app:cardElevation="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
</RelativeLayout> <ListView
android:id="@+id/approvals_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@color/colour_three"
tools:layout_height="658dp"
tools:listitem="@layout/approval_list_item" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -5,11 +5,86 @@
android:id="@+id/main_navigation" android:id="@+id/main_navigation"
app:startDestination="@id/homeAdminFragment"> app:startDestination="@id/homeAdminFragment">
<activity
android:id="@+id/nav_user_settings"
android:name="h_mal.appttude.com.driver.ui.update.UpdateActivity"
android:label="fragment_profile"
tools:layout="@layout/update_activity" />
<fragment <fragment
android:id="@+id/homeAdminFragment" android:id="@+id/homeAdminFragment"
android:name="h_mal.appttude.com.driver.ui.HomeSuperUserFragment" android:name="h_mal.appttude.com.driver.ui.HomeSuperUserFragment"
android:label="fragment_home" android:label="fragment_home"
tools:layout="@layout/fragment_home_super_user"> tools:layout="@layout/fragment_home_super_user">
<action
android:id="@+id/action_homeAdminFragment_to_userMainFragment"
app:destination="@id/userMainFragment" />
</fragment> </fragment>
<fragment
android:id="@+id/userMainFragment"
android:name="h_mal.appttude.com.driver.ui.DriverOverviewFragment"
android:label="fragment_user_main"
tools:layout="@layout/fragment_user_main" >
<action
android:id="@+id/to_driverLicenseFragment"
app:destination="@id/driverLicenseFragment" />
<action
android:id="@+id/to_driverProfileFragment"
app:destination="@id/driverProfileFragment" />
<action
android:id="@+id/to_privateHireLicenseFragment"
app:destination="@id/privateHireLicenseFragment" />
<action
android:id="@+id/to_insuranceFragment"
app:destination="@id/insuranceFragment" />
<action
android:id="@+id/to_logbookFragment"
app:destination="@id/logbookFragment" />
<action
android:id="@+id/to_motFragment"
app:destination="@id/motFragment" />
<action
android:id="@+id/to_privateHireVehicleFragment"
app:destination="@id/privateHireVehicleFragment" />
<action
android:id="@+id/to_vehicleProfileFragment"
app:destination="@id/vehicleProfileFragment" />
</fragment>
<fragment
android:id="@+id/driverLicenseFragment"
android:name="h_mal.appttude.com.driver.ui.driverprofile.DriverLicenseFragment"
android:label="DriverLicenseFragment" />
<fragment
android:id="@+id/driverProfileFragment"
android:name="h_mal.appttude.com.driver.ui.driverprofile.DriverProfileFragment"
android:label="fragment_driver_profile"
tools:layout="@layout/fragment_driver_profile" />
<fragment
android:id="@+id/privateHireLicenseFragment"
android:name="h_mal.appttude.com.driver.ui.driverprofile.PrivateHireLicenseFragment"
android:label="fragment_private_hire_license"
tools:layout="@layout/fragment_private_hire_license" />
<fragment
android:id="@+id/insuranceFragment"
android:name="h_mal.appttude.com.driver.ui.vehicleprofile.InsuranceFragment"
android:label="InsuranceFragment" />
<fragment
android:id="@+id/logbookFragment"
android:name="h_mal.appttude.com.driver.ui.vehicleprofile.LogbookFragment"
android:label="fragment_logbook"
tools:layout="@layout/fragment_logbook" />
<fragment
android:id="@+id/motFragment"
android:name="h_mal.appttude.com.driver.ui.vehicleprofile.MotFragment"
android:label="MotFragment" />
<fragment
android:id="@+id/privateHireVehicleFragment"
android:name="h_mal.appttude.com.driver.ui.vehicleprofile.PrivateHireVehicleFragment"
android:label="fragment_private_hire_vehicle"
tools:layout="@layout/fragment_private_hire_vehicle" />
<fragment
android:id="@+id/vehicleProfileFragment"
android:name="h_mal.appttude.com.driver.ui.vehicleprofile.VehicleProfileFragment"
android:label="fragment_vehicle_setup"
tools:layout="@layout/fragment_vehicle_setup" />
</navigation> </navigation>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Driver Admin</string>
</resources>

View File

@@ -1,5 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<network-security-config> <network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<debug-overrides>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
<domain-config cleartextTrafficPermitted="true"> <domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">10.0.2.2</domain> <domain includeSubdomains="true">10.0.2.2</domain>
</domain-config> </domain-config>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="h_mal.appttude.com.driver">
<application
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round">
</application>
</manifest>

View File

@@ -5,12 +5,14 @@ import androidx.lifecycle.ViewModelProvider
import h_mal.appttude.com.driver.data.FirebaseAuthSource import h_mal.appttude.com.driver.data.FirebaseAuthSource
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.data.FirebaseStorageSource import h_mal.appttude.com.driver.data.FirebaseStorageSource
import h_mal.appttude.com.driver.data.prefs.PreferenceProvider
import h_mal.appttude.com.driver.viewmodels.* import h_mal.appttude.com.driver.viewmodels.*
class ApplicationViewModelFactory( class ApplicationViewModelFactory(
private val auth: FirebaseAuthSource, private val auth: FirebaseAuthSource,
private val database: FirebaseDatabaseSource, private val database: FirebaseDatabaseSource,
private val storage: FirebaseStorageSource private val storage: FirebaseStorageSource,
private val preferences: PreferenceProvider
) : ViewModelProvider.Factory { ) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -23,6 +25,11 @@ class ApplicationViewModelFactory(
auth, auth,
storage storage
) )
isAssignableFrom(RoleViewModel::class.java) -> RoleViewModel(
auth,
database,
storage
)
isAssignableFrom(DriverLicenseViewModel::class.java) -> DriverLicenseViewModel( isAssignableFrom(DriverLicenseViewModel::class.java) -> DriverLicenseViewModel(
auth, auth,
database, database,
@@ -59,11 +66,6 @@ class ApplicationViewModelFactory(
database, database,
storage storage
) )
isAssignableFrom(RoleViewModel::class.java) -> RoleViewModel(
auth,
database,
storage
)
else -> throw IllegalArgumentException("Unknown ViewModel class") else -> throw IllegalArgumentException("Unknown ViewModel class")
} as T } as T
} }

View File

@@ -20,7 +20,7 @@ class DriverLicenseViewModel(
override val storageRef: StorageReference = storage.driversLicenseStorageRef(uid) override val storageRef: StorageReference = storage.driversLicenseStorageRef(uid)
override val objectName: String = "drivers license" override val objectName: String = "drivers license"
override fun getDataFromDatabase() = getDataClass<DriversLicense>() override fun getDataFromDatabase() = retrieveDataFromDatabase<DriversLicense>()
override fun setDataInDatabase(data: DriversLicense, localImageUri: Uri?) = io { override fun setDataInDatabase(data: DriversLicense, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") { doTryOperation("Failed to upload $objectName") {

View File

@@ -19,8 +19,7 @@ class DriverProfileViewModel(
override val databaseRef: DatabaseReference = database.getDriverDetailsRef(uid) override val databaseRef: DatabaseReference = database.getDriverDetailsRef(uid)
override val storageRef: StorageReference = storage.profileImageStorageRef(uid) override val storageRef: StorageReference = storage.profileImageStorageRef(uid)
override val objectName: String = "drivers profile" override val objectName: String = "drivers profile"
override fun getDataFromDatabase() = retrieveDataFromDatabase<DriverProfile>()
override fun getDataFromDatabase() = getDataClass<DriverProfile>()
override fun setDataInDatabase(data: DriverProfile, localImageUri: Uri?) = io { override fun setDataInDatabase(data: DriverProfile, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") { doTryOperation("Failed to upload $objectName") {

View File

@@ -20,7 +20,7 @@ class InsuranceViewModel(
override val storageRef: StorageReference = storage.insuranceStorageRef(uid) override val storageRef: StorageReference = storage.insuranceStorageRef(uid)
override val objectName: String = "insurance" override val objectName: String = "insurance"
override fun getDataFromDatabase() = getDataClass<Insurance>() override fun getDataFromDatabase() = retrieveDataFromDatabase<Insurance>()
override fun setDataInDatabase(data: Insurance, localImageUris: List<Uri?>?) = io { override fun setDataInDatabase(data: Insurance, localImageUris: List<Uri?>?) = io {
doTryOperation("Failed to upload $objectName") { doTryOperation("Failed to upload $objectName") {

View File

@@ -20,7 +20,7 @@ class LogbookViewModel(
override val storageRef: StorageReference = storage.logBookStorageRef(uid) override val storageRef: StorageReference = storage.logBookStorageRef(uid)
override val objectName: String = "Log book" override val objectName: String = "Log book"
override fun getDataFromDatabase() = getDataClass<Logbook>() override fun getDataFromDatabase() = retrieveDataFromDatabase<Logbook>()
override fun setDataInDatabase(data: Logbook, localImageUri: Uri?) = io { override fun setDataInDatabase(data: Logbook, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") { doTryOperation("Failed to upload $objectName") {

View File

@@ -20,7 +20,7 @@ class MotViewModel(
override val storageRef: StorageReference? = storage.motStorageRef(uid) override val storageRef: StorageReference? = storage.motStorageRef(uid)
override val objectName: String = "vehicle profile" override val objectName: String = "vehicle profile"
override fun getDataFromDatabase() = getDataClass<Mot>() override fun getDataFromDatabase() = retrieveDataFromDatabase<Mot>()
override fun setDataInDatabase(data: Mot, localImageUri: Uri?) = io { override fun setDataInDatabase(data: Mot, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") { doTryOperation("Failed to upload $objectName") {

View File

@@ -20,7 +20,7 @@ class PrivateHireLicenseViewModel(
override val storageRef: StorageReference = storage.privateHireStorageRef(uid) override val storageRef: StorageReference = storage.privateHireStorageRef(uid)
override val objectName: String = "private hire license" override val objectName: String = "private hire license"
override fun getDataFromDatabase() = getDataClass<PrivateHireLicense>() override fun getDataFromDatabase() = retrieveDataFromDatabase<PrivateHireLicense>()
override fun setDataInDatabase(data: PrivateHireLicense, localImageUri: Uri?) = io { override fun setDataInDatabase(data: PrivateHireLicense, localImageUri: Uri?) = io {
doTryOperation("Failed to upload private hire license") { doTryOperation("Failed to upload private hire license") {

View File

@@ -20,7 +20,7 @@ class PrivateHireVehicleViewModel(
override val storageRef: StorageReference = storage.privateHireVehicleStorageRef(uid) override val storageRef: StorageReference = storage.privateHireVehicleStorageRef(uid)
override val objectName: String = "private hire vehicle license" override val objectName: String = "private hire vehicle license"
override fun getDataFromDatabase() = getDataClass<PrivateHireVehicle>() override fun getDataFromDatabase() = retrieveDataFromDatabase<PrivateHireVehicle>()
override fun setDataInDatabase(data: PrivateHireVehicle, localImageUri: Uri?) = io { override fun setDataInDatabase(data: PrivateHireVehicle, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") { doTryOperation("Failed to upload $objectName") {

View File

@@ -19,7 +19,7 @@ class VehicleProfileViewModel(
override val storageRef: StorageReference? = null override val storageRef: StorageReference? = null
override val objectName: String = "vehicle profile" override val objectName: String = "vehicle profile"
override fun getDataFromDatabase() = getDataClass<VehicleProfile>() override fun getDataFromDatabase() = retrieveDataFromDatabase<VehicleProfile>()
override fun setDataInDatabase(data: VehicleProfile) { override fun setDataInDatabase(data: VehicleProfile) {
io { io {

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
android:id="@+id/app_bar_layout"
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:itemTextColor="@android:color/white"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<TextView
android:id="@+id/logout"
style="@style/headerStyle"
android:background="@color/colour_nine"
android:textSize="14sp"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center"
android:text="@string/logout"
android:textStyle="bold" />
</LinearLayout>
</com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>

View File

@@ -1,4 +1,3 @@
<resources> <resources>
<!-- TODO: Remove or change this placeholder text --> <string name="app_name">Driver Choice cars</string>
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources> </resources>

View File

@@ -5,7 +5,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application <application
android:name="h_mal.appttude.com.driver.application.DriverApplication" android:name=".application.DriverApplication"
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"
@@ -13,8 +13,7 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<activity <activity
android:name="h_mal.appttude.com.driver.ui.user.LoginActivity" android:name=".ui.user.LoginActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar.User" android:theme="@style/AppTheme.NoActionBar.User"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
@@ -25,17 +24,16 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name="h_mal.appttude.com.driver.ui.MainActivity" android:name=".ui.MainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar" /> android:theme="@style/AppTheme.NoActionBar" />
<activity <activity
android:name="h_mal.appttude.com.driver.ui.update.UpdateActivity" android:name=".ui.update.UpdateActivity"
android:theme="@style/AppTheme.NoActionBar.Update" /> android:theme="@style/AppTheme.NoActionBar.Update" />
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="h_mal.appttude.com.driver" android:authorities="${applicationId}.contentprovider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data <meta-data

View File

@@ -4,6 +4,7 @@ import android.app.Application
import h_mal.appttude.com.driver.data.FirebaseAuthSource import h_mal.appttude.com.driver.data.FirebaseAuthSource
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.data.FirebaseStorageSource import h_mal.appttude.com.driver.data.FirebaseStorageSource
import h_mal.appttude.com.driver.data.prefs.PreferenceProvider
import org.kodein.di.Kodein import org.kodein.di.Kodein
import org.kodein.di.KodeinAware import org.kodein.di.KodeinAware
import org.kodein.di.android.x.androidXModule import org.kodein.di.android.x.androidXModule
@@ -20,6 +21,7 @@ class DriverApplication : Application(), KodeinAware {
bind() from singleton { FirebaseAuthSource() } bind() from singleton { FirebaseAuthSource() }
bind() from singleton { FirebaseDatabaseSource() } bind() from singleton { FirebaseDatabaseSource() }
bind() from singleton { FirebaseStorageSource() } bind() from singleton { FirebaseStorageSource() }
bind() from provider { ApplicationViewModelFactory(instance(), instance(), instance()) } bind() from singleton { PreferenceProvider(this@DriverApplication) }
bind() from provider { ApplicationViewModelFactory(instance(), instance(), instance(), instance()) }
} }
} }

View File

@@ -2,7 +2,6 @@ package h_mal.appttude.com.driver.base
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup.LayoutParams import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.MATCH_PARENT
@@ -16,12 +15,11 @@ import h_mal.appttude.com.driver.R
import h_mal.appttude.com.driver.application.ApplicationViewModelFactory import h_mal.appttude.com.driver.application.ApplicationViewModelFactory
import h_mal.appttude.com.driver.data.ViewState import h_mal.appttude.com.driver.data.ViewState
import h_mal.appttude.com.driver.utils.* import h_mal.appttude.com.driver.utils.*
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
import h_mal.appttude.com.driver.utils.GenericsHelper.inflateBindingByType
import org.kodein.di.KodeinAware import org.kodein.di.KodeinAware
import org.kodein.di.android.kodein import org.kodein.di.android.kodein
import org.kodein.di.generic.instance import org.kodein.di.generic.instance
import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
abstract class BaseActivity<V : BaseViewModel, VB : ViewBinding> : AppCompatActivity(), KodeinAware { abstract class BaseActivity<V : BaseViewModel, VB : ViewBinding> : AppCompatActivity(), KodeinAware {
// The Idling Resource which will be null in production. // The Idling Resource which will be null in production.
@@ -47,34 +45,12 @@ abstract class BaseActivity<V : BaseViewModel, VB : ViewBinding> : AppCompatActi
{ defaultViewModelCreationExtras } { defaultViewModelCreationExtras }
) )
@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")
/**
* Create a view binding out of the the generic [VB]
*/
private fun inflateBindingByType(
genericClassAt: KClass<VB>
): VB = try {
@Suppress("UNCHECKED_CAST")
genericClassAt.java.methods.first { viewBinding ->
viewBinding.parameterTypes.size == 1
&& viewBinding.parameterTypes.getOrNull(0) == LayoutInflater::class.java
}.invoke(null, layoutInflater) as VB
} catch (exception: Exception) {
throw IllegalStateException("Can not inflate binding from generic")
}
private var loading: Boolean = false private var loading: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
configureObserver() configureObserver()
_binding = inflateBindingByType(getGenericClassAt(1)) _binding = inflateBindingByType(getGenericClassAt(1), layoutInflater)
setContentView(requireNotNull(_binding).root) setContentView(requireNotNull(_binding).root)
setupView(binding) setupView(binding)
} }

View File

@@ -0,0 +1,55 @@
package h_mal.appttude.com.driver.base
import android.net.ConnectivityManager
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import androidx.viewbinding.ViewBinding
import com.firebase.ui.database.FirebaseRecyclerAdapter
import com.firebase.ui.database.FirebaseRecyclerOptions
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
import h_mal.appttude.com.driver.utils.GenericsHelper.inflateBindingByType
import java.nio.ByteBuffer
open class BaseFirebaseAdapter<T: Any, VB : ViewBinding>(options: FirebaseRecyclerOptions<T>, private val layoutInflater: LayoutInflater):
FirebaseRecyclerAdapter<T, CustomViewHolder<VB>>(options) {
private val connectivityManager = layoutInflater.context.getSystemService(ConnectivityManager::class.java) as ConnectivityManager
private var _binding: VB? = null
val binding: VB
get() = _binding ?: error("Must only access binding while fragment is attached.")
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder<VB> {
_binding = inflateBindingByType(getGenericClassAt(1), layoutInflater)
val lp = RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
binding.root.layoutParams = lp
return CustomViewHolder(requireNotNull(_binding))
}
override fun onBindViewHolder(holder: CustomViewHolder<VB>, position: Int, model: T) { }
override fun getItemId(position: Int): Long {
return snapshots.getSnapshot(position).key?.toByteArray()
?.let { ByteBuffer.wrap(it).long } ?: super.getItemId(position)
}
fun getKeyAtPosition(position: Int) = snapshots.getSnapshot(position).key
override fun startListening() {
super.startListening()
// check if network is connected
if (connectivityManager.activeNetwork == null) {
connectionLost()
}
}
open fun connectionLost() {}
}
class CustomViewHolder<VB : ViewBinding>(val viewBinding: VB): ViewHolder(viewBinding.root)

View File

@@ -1,29 +1,27 @@
package h_mal.appttude.com.driver.base package h_mal.appttude.com.driver.base
import android.app.Activity
import android.content.ClipData import android.content.ClipData
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContract
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.createViewModelLazy import androidx.fragment.app.createViewModelLazy
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import h_mal.appttude.com.driver.application.ApplicationViewModelFactory import h_mal.appttude.com.driver.application.ApplicationViewModelFactory
import h_mal.appttude.com.driver.data.ViewState import h_mal.appttude.com.driver.data.ViewState
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
import h_mal.appttude.com.driver.utils.GenericsHelper.inflateBindingByType
import h_mal.appttude.com.driver.utils.PermissionsUtils import h_mal.appttude.com.driver.utils.PermissionsUtils
import org.kodein.di.KodeinAware import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance import org.kodein.di.generic.instance
import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
const val IMAGE_SELECT_REQUEST_CODE = 401 abstract class BaseFragment<V : BaseViewModel, VB : ViewBinding> : Fragment(), KodeinAware {
abstract class BaseFragment<V : BaseViewModel, VB : ViewBinding>(
) : Fragment(), KodeinAware {
private var _binding: VB? = null private var _binding: VB? = null
private val binding: VB private val binding: VB
@@ -31,7 +29,12 @@ abstract class BaseFragment<V : BaseViewModel, VB : ViewBinding>(
var mActivity: BaseActivity<V, *>? = null var mActivity: BaseActivity<V, *>? = null
override val kodein by kodein()
private val factory by instance<ApplicationViewModelFactory>()
val viewModel: V by getFragmentViewModel() val viewModel: V by getFragmentViewModel()
private fun getFragmentViewModel(): Lazy<V> =
createViewModelLazy(getGenericClassAt(0), { viewModelStore }, factoryProducer = { factory })
private var multipleImage: Boolean = false private var multipleImage: Boolean = false
@@ -39,33 +42,6 @@ abstract class BaseFragment<V : BaseViewModel, VB : ViewBinding>(
multipleImage = true multipleImage = true
} }
override val kodein by kodein()
val factory by instance<ApplicationViewModelFactory>()
fun getFragmentViewModel(): Lazy<V> =
createViewModelLazy(getGenericClassAt(0), { viewModelStore }, factoryProducer = { factory })
fun LayoutInflater.inflateBindingByType(
container: ViewGroup?,
genericClassAt: KClass<VB>
): VB = try {
@Suppress("UNCHECKED_CAST")
genericClassAt.java.methods.first { inflateFun ->
inflateFun.parameterTypes.size == 3
&& inflateFun.parameterTypes.getOrNull(0) == LayoutInflater::class.java
&& inflateFun.parameterTypes.getOrNull(1) == ViewGroup::class.java
&& inflateFun.parameterTypes.getOrNull(2) == Boolean::class.java
}.invoke(null, this, container, false) as VB
} catch (exception: Exception) {
throw IllegalStateException("Can not inflate binding from generic")
}
@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")
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@@ -126,25 +102,6 @@ abstract class BaseFragment<V : BaseViewModel, VB : ViewBinding>(
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
IMAGE_SELECT_REQUEST_CODE -> {
data?.clipData?.convertToList()?.let { clip ->
val list = clip.takeIf { it.size > 10 }?.let {
clip.subList(0, 9)
} ?: clip
onImageGalleryResult(list)
return
}
onImageGalleryResult(data?.data)
}
}
}
}
private fun ClipData.convertToList(): List<Uri> = 0.rangeTo(itemCount).map { getItemAt(it).uri } private fun ClipData.convertToList(): List<Uri> = 0.rangeTo(itemCount).map { getItemAt(it).uri }
/** /**
@@ -180,10 +137,33 @@ abstract class BaseFragment<V : BaseViewModel, VB : ViewBinding>(
open fun onImageGalleryResult(imageUris: List<Uri>?) {} open fun onImageGalleryResult(imageUris: List<Uri>?) {}
fun openGalleryForImage() { fun openGalleryForImage() {
val intent = Intent(Intent.ACTION_PICK) registerForActivityResult(getResultsContract()) { result ->
intent.type = "image/*" @Suppress("UNCHECKED_CAST")
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multipleImage) when (result) {
startActivityForResult(intent, IMAGE_SELECT_REQUEST_CODE) is Uri -> onImageGalleryResult(result)
is List<*> -> onImageGalleryResult(result as List<Uri>)
}
}.launch(multipleImage)
} }
private fun getResultsContract(): ActivityResultContract<Boolean, Any?> {
return object : ActivityResultContract<Boolean, Any?>() {
override fun createIntent(context: Context, input: Boolean): Intent {
return Intent(Intent.ACTION_PICK).apply {
type = "image/*"
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, input)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): Any? {
intent?.clipData?.convertToList()?.let { clip ->
val list = clip.takeIf { it.size > 10 }?.let {
clip.subList(0, 9)
} ?: clip
return list
}
return intent?.data
}
}
}
} }

View File

@@ -29,14 +29,11 @@ abstract class BaseViewModel : ViewModel() {
operation() operation()
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
e.message?.let {
onError(it)
return
}
defaultErrorMessage?.let { defaultErrorMessage?.let {
onError(it) onError(it)
return return
} }
onError((e.message ?: "Operation failed!!"))
} }
} }
} }

View File

@@ -15,6 +15,7 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import java.io.IOException import java.io.IOException
abstract class DataSubmissionBaseViewModel<T : Any>( abstract class DataSubmissionBaseViewModel<T : Any>(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
private val database: FirebaseDatabaseSource, private val database: FirebaseDatabaseSource,
@@ -32,7 +33,7 @@ abstract class DataSubmissionBaseViewModel<T : Any>(
open fun setDataInDatabase(data: T, localImageUris: List<Uri?>?): Job = Job() open fun setDataInDatabase(data: T, localImageUris: List<Uri?>?): Job = Job()
open fun setDataInDatabase(data: T) {} open fun setDataInDatabase(data: T) {}
inline fun <reified T : Any> getDataClass() = io { inline fun <reified T : Any> retrieveDataFromDatabase() = io {
doTryOperation("Failed to retrieve $objectName") { doTryOperation("Failed to retrieve $objectName") {
val data = databaseRef.getDataFromDatabaseRef<T>() val data = databaseRef.getDataFromDatabaseRef<T>()
onSuccess(data ?: FirebaseCompletion.Default) onSuccess(data ?: FirebaseCompletion.Default)

View File

@@ -21,6 +21,7 @@ const val PRIVATE_HIRE_VEHICLE = "private_hire_vehicle"
const val VEHICLE_DETAILS = "vehicle_details" const val VEHICLE_DETAILS = "vehicle_details"
const val ARCHIVE = "archive" const val ARCHIVE = "archive"
@SuppressWarnings("unused|WeakerAccess")
class FirebaseDatabaseSource { class FirebaseDatabaseSource {
private val database = FirebaseDatabase.getInstance() private val database = FirebaseDatabase.getInstance()
@@ -35,7 +36,12 @@ class FirebaseDatabaseSource {
return data return data
} }
fun getUserRef(uid: String) = database.getReference(USER_CONST).child(uid) fun getDatabaseReferenceFromLink(link: String) = database.getReferenceFromUrl(link)
val users = database.getReference(USER_CONST)
fun getUsersRef() = database.reference.child(USER_CONST)
fun getUserRef(uid: String) = users.child(uid)
fun getUserDetailsRef(uid: String) = getUserRef(uid).child(USER_DETAILS) fun getUserDetailsRef(uid: String) = getUserRef(uid).child(USER_DETAILS)
fun getVehicleRef(uid: String) = getUserRef(uid).child(VEHICLE_PROFILE) fun getVehicleRef(uid: String) = getUserRef(uid).child(VEHICLE_PROFILE)
fun getDriverRef(uid: String) = getUserRef(uid).child(DRIVER_PROFILE) fun getDriverRef(uid: String) = getUserRef(uid).child(DRIVER_PROFILE)

View File

@@ -0,0 +1,29 @@
package h_mal.appttude.com.driver.data.prefs
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
/**
* Shared prefs class used for storing conversion name values as pairs
* Then retrieving as pairs
*
*/
const val SORT_OPTION = "SORT_OPTION"
class PreferenceProvider (context: Context) {
private val appContext = context.applicationContext
private val preference: SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(appContext)
fun setSortOption(sortLabel: String) {
preference.edit()
.putString(SORT_OPTION, sortLabel)
.apply()
}
fun getSortOption(): String? = preference
.getString(SORT_OPTION, null)
}

View File

@@ -1,13 +1,22 @@
package h_mal.appttude.com.driver.utils package h_mal.appttude.com.driver.utils
/**
* Extension function that will execute high order if value is true or do nothing
*
* @sample #boolean.isTrue{ #Do something when its true }
*/
inline fun Boolean.isTrue(block: () -> Unit){ inline fun Boolean.isTrue(block: () -> Unit){
if (this) { if (this) {
block() block()
} }
} }
inline fun <T, R> T.isNotNull(block: (T) -> R): R?{ /**
* Extension function that will execute high order if value is not null or do nothing
*
* @sample #nullable.isNotNull{i -> i.doSomethingSinceItsNotNull() }
*/
inline fun <T, R> T?.isNotNull(block: (T) -> R): R?{
return if (this != null) { return if (this != null) {
block(this) block(this)
} else { } else {

View File

@@ -43,3 +43,14 @@ suspend inline fun <reified T : Any> DatabaseReference.getDataFromDatabaseRef():
} }
} }
} }
suspend fun <T: Any> DatabaseReference.getDataFromDatabaseRef(clazz : Class<T>): T? {
return when (val response: EventResponse = singleValueEvent()) {
is EventResponse.Changed -> {
response.snapshot.getValue(clazz)
}
is EventResponse.Cancelled -> {
throw response.error.toException()
}
}
}

View File

@@ -0,0 +1,52 @@
package h_mal.appttude.com.driver.utils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.viewbinding.ViewBinding
import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
object GenericsHelper {
@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")
/**
* Create a view binding out of the the generic [VB]
*
* @sample inflateBindingByType(getGenericClassAt(0), layoutInflater)
*/
fun <VB: ViewBinding> inflateBindingByType(
genericClassAt: KClass<VB>,
layoutInflater: LayoutInflater
): VB = try {
@Suppress("UNCHECKED_CAST")
genericClassAt.java.methods.first { viewBinding ->
viewBinding.parameterTypes.size == 1
&& viewBinding.parameterTypes.getOrNull(0) == LayoutInflater::class.java
}.invoke(null, layoutInflater) as VB
} catch (exception: Exception) {
println ("generic class failed at = $genericClassAt")
exception.printStackTrace()
throw IllegalStateException("Can not inflate binding from generic")
}
fun <VB: ViewBinding> LayoutInflater.inflateBindingByType(
container: ViewGroup?,
genericClassAt: KClass<VB>
): VB = try {
@Suppress("UNCHECKED_CAST")
genericClassAt.java.methods.first { inflateFun ->
inflateFun.parameterTypes.size == 3
&& inflateFun.parameterTypes.getOrNull(0) == LayoutInflater::class.java
&& inflateFun.parameterTypes.getOrNull(1) == ViewGroup::class.java
&& inflateFun.parameterTypes.getOrNull(2) == Boolean::class.java
}.invoke(null, this, container, false) as VB
} catch (exception: Exception) {
throw IllegalStateException("Can not inflate binding from generic")
}
}

View File

@@ -1,28 +1,29 @@
package h_mal.appttude.com.driver.utils package h_mal.appttude.com.driver.utils
import android.content.Context import android.os.Bundle
import android.content.Intent import android.os.Parcelable
import android.view.View import android.view.View
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.navigation.Navigation import androidx.navigation.Navigation
const val UPLOAD_NEW = "upload_new" fun View.navigateTo(@IdRes navId: Int, args: Bundle? = null) {
Navigation
.findNavController(this)
.navigate(navId, args)
}
fun Any.toBundle(key: String): Bundle {
return Bundle().apply {
when (this@toBundle) {
is String -> putString(key, this@toBundle)
is Int -> putInt(key, this@toBundle)
is Boolean -> putBoolean(key, this@toBundle)
is Parcelable -> putParcelable(key, this@toBundle)
is Double -> putDouble(key, this@toBundle)
is Float -> putFloat(key, this@toBundle)
}
fun navigateToActivity(context: Context, navigationActivity: Navigations) {
try {
val ourClass: Class<*> =
Class.forName("h_mal.appttude.com.driver." + navigationActivity.value)
val intent = Intent(context, ourClass)
context.startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
} }
} }
fun View.navigateTo(@IdRes navId: Int) {
Navigation
.findNavController(this)
.navigate(navId)
}

View File

@@ -18,7 +18,7 @@ class RoleViewModel(
override val storageRef: StorageReference? = null override val storageRef: StorageReference? = null
override val objectName: String = "role" override val objectName: String = "role"
override fun getDataFromDatabase() = getDataClass<String>() override fun getDataFromDatabase() = retrieveDataFromDatabase<String>()
override fun setDataInDatabase(data: String) { override fun setDataInDatabase(data: String) {
io { io {

View File

@@ -18,22 +18,9 @@
android:layout_marginBottom="24dp" android:layout_marginBottom="24dp"
android:text="@string/not_a_driver_message" /> android:text="@string/not_a_driver_message" />
<androidx.cardview.widget.CardView <com.google.android.material.button.MaterialButton
android:id="@+id/request_driver_button" android:id="@+id/request_driver_button"
android:layout_width="match_parent" style="@style/TextButton"
android:layout_height="wrap_content" android:text="@string/request_driver_profile" />
android:backgroundTint="@color/colour_one"
android:selectAllOnFocus="true"
app:cardCornerRadius="8dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:gravity="center"
android:text="@string/request_driver_profile"
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold" />
</androidx.cardview.widget.CardView>
</LinearLayout> </LinearLayout>

View File

@@ -46,7 +46,8 @@
tools:src="@drawable/choice_img_round" /> tools:src="@drawable/choice_img_round" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout"> <com.google.android.material.textfield.TextInputLayout
style="@style/text_input_layout">
<EditText <EditText
android:id="@+id/names_input" android:id="@+id/names_input"

View File

@@ -4,9 +4,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
style="@style/parent_constraint_layout" style="@style/parent_constraint_layout"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto">
tools:context=".ui.vehicleprofile.LogbookFragment">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -2,8 +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: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"
style="@style/parent_constraint_layout" style="@style/parent_constraint_layout">
tools:context=".ui.vehicleprofile.PrivateHireVehicleFragment">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -2,9 +2,7 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical">
android:orientation="vertical"
tools:context=".ui.vehicleprofile.VehicleProfileFragment">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -5,6 +5,6 @@
<item <item
android:id="@+id/archive" android:id="@+id/archive"
android:icon="@drawable/ic_sort_black_24dp" android:icon="@drawable/ic_sort_black_24dp"
android:title="sort" android:title="@string/sort"
app:showAsAction="ifRoom"></item> app:showAsAction="ifRoom" />
</menu> </menu>

View File

@@ -14,5 +14,6 @@
<color name="colour_seven">#91ddff</color> <color name="colour_seven">#91ddff</color>
<color name="colour_eight">#9958BE3F</color> <color name="colour_eight">#9958BE3F</color>
<color name="colour_ten">#604D4A51</color>
<color name="colour_nine">#303030</color> <color name="colour_nine">#303030</color>
</resources> </resources>

View File

@@ -4,4 +4,5 @@
<dimen name="activity_vertical_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="nav_header_vertical_spacing">8dp</dimen> <dimen name="nav_header_vertical_spacing">8dp</dimen>
<dimen name="nav_header_height">200dp</dimen> <dimen name="nav_header_height">200dp</dimen>
<dimen name="status_size">16dp</dimen>
</resources> </resources>

View File

@@ -1,5 +1,4 @@
<resources> <resources>
<string name="app_name">Choice Minicabs</string>
<string name="nav_header_title">Driver Name</string> <string name="nav_header_title">Driver Name</string>
<string name="nav_header_subtitle">driver@example.com</string> <string name="nav_header_subtitle">driver@example.com</string>
<string name="nav_header_desc">Navigation header</string> <string name="nav_header_desc">Navigation header</string>
@@ -83,7 +82,7 @@
<string name="insurance">Insurance</string> <string name="insurance">Insurance</string>
<string name="m_o_t">M.O.T</string> <string name="m_o_t">M.O.T</string>
<string name="log_book">Log book</string> <string name="log_book">Log book</string>
<string name="private_hire_vehicle_license">Private hire Vehicle License</string> <string name="private_hire_vehicle_license">Private Hire Vehicle License</string>
<string name="upload_insurance_documents">Upload Insurance Documents</string> <string name="upload_insurance_documents">Upload Insurance Documents</string>
<string name="insurer">Insurer</string> <string name="insurer">Insurer</string>
<string name="insurance_expiry">Insurance expiry</string> <string name="insurance_expiry">Insurance expiry</string>
@@ -95,4 +94,11 @@
<string name="request_driver_profile">Request driver profile</string> <string name="request_driver_profile">Request driver profile</string>
<string name="or">OR</string> <string name="or">OR</string>
<string name="floating_action_button">Floating action button</string> <string name="floating_action_button">Floating action button</string>
<string name="sort">sort</string>
<string name="denied">Approval denied</string>
<string name="pending">Pending approval</string>
<string name="approved">Approved</string>
<string name="not_submitted">Not submitted</string>
<string name="empty_view_message">You have no drivers</string>
<string name="approval_status">approval status</string>
</resources> </resources>