- retrieve thumbnail or no image

- changes to storage ref in process retrieval in process

 ** broken commit **
This commit is contained in:
2023-10-19 21:39:59 +01:00
parent 401b43f3c3
commit 32def043f9
56 changed files with 896 additions and 314 deletions

View File

@@ -1,5 +1,5 @@
{ {
"licenseExpiry": "27/04/2019", "licenseExpiry": "27/04/2032",
"licenseImageString": "driver_license_driver.jpg", "licenseImageString": "driver_license_driver.jpg",
"licenseNumber": "FARME100165AB5EW" "licenseNumber": "FARME100165AB5EW"
} }

View File

@@ -16,12 +16,14 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.internal.util.Checks
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.rule.GrantPermissionRule import androidx.test.rule.GrantPermissionRule
import com.google.gson.Gson import com.google.gson.Gson
import h_mal.appttude.com.driver.base.BaseActivity import h_mal.appttude.com.driver.base.BaseActivity
import h_mal.appttude.com.driver.helpers.BaseViewAction import h_mal.appttude.com.driver.helpers.BaseViewAction
import h_mal.appttude.com.driver.helpers.SnapshotRule import h_mal.appttude.com.driver.helpers.SnapshotRule
import kotlinx.coroutines.runBlocking
import org.hamcrest.CoreMatchers import org.hamcrest.CoreMatchers
import org.hamcrest.Matcher import org.hamcrest.Matcher
import org.hamcrest.core.AllOf import org.hamcrest.core.AllOf
@@ -60,8 +62,10 @@ open class BaseUiTest<T : BaseActivity<*, *>>(
beforeLaunch() beforeLaunch()
mActivityScenarioRule = ActivityScenario.launch(activity) mActivityScenarioRule = ActivityScenario.launch(activity)
mActivityScenarioRule.onActivity { mActivityScenarioRule.onActivity {
mIdlingResource = it.getIdlingResource()!! runBlocking {
IdlingRegistry.getInstance().register(mIdlingResource) mIdlingResource = it.getIdlingResource()!!
IdlingRegistry.getInstance().register(mIdlingResource)
}
} }
afterLaunch() afterLaunch()
} }

View File

@@ -35,12 +35,15 @@ open class FirebaseTest<T : BaseActivity<*, *>>(
val localHost = "10.0.2.2" val localHost = "10.0.2.2"
FirebaseAuth.getInstance().useEmulator(localHost, 9099) FirebaseAuth.getInstance().useEmulator(localHost, 9099)
FirebaseDatabase.getInstance().useEmulator(localHost, 9000) FirebaseDatabase.getInstance().useEmulator(localHost, 9001)
FirebaseStorage.getInstance().useEmulator(localHost, 9199) FirebaseStorage.getInstance().useEmulator(localHost, 9199)
} }
} }
override fun beforeLaunch() { override fun beforeLaunch() {
// // sign out if it failed to sign out
// if (firebaseAuthSource.getUser() != null) firebaseAuthSource.logOut()
if (registered) { if (registered) {
runBlocking { runBlocking {
setupUser() setupUser()

View File

@@ -7,7 +7,7 @@ import androidx.test.espresso.action.ViewActions.scrollTo
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import h_mal.appttude.com.driver.helpers.EspressoHelper.trying import h_mal.appttude.com.driver.helpers.EspressoHelper.trying
import h_mal.appttude.com.driver.model.Model import h_mal.appttude.com.driver.base.Model
import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.allOf
import java.time.LocalDate import java.time.LocalDate
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter

View File

@@ -21,7 +21,7 @@ class HomeRobot : BaseTestRobot() {
fun updateProfile() { fun updateProfile() {
openDrawer() openDrawer()
clickButton(R.id.nav_user_settings) clickButton(R.id.nav_update_profile)
} }
fun openDriverProfile() = clickButton(R.id.driver) fun openDriverProfile() = clickButton(R.id.driver)

View File

@@ -9,7 +9,7 @@ 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.Insurance import h_mal.appttude.com.driver.model.Insurance
import h_mal.appttude.com.driver.model.Logbook import h_mal.appttude.com.driver.model.Logbook
import h_mal.appttude.com.driver.model.Model import h_mal.appttude.com.driver.base.Model
import h_mal.appttude.com.driver.model.Mot import h_mal.appttude.com.driver.model.Mot
import h_mal.appttude.com.driver.model.PrivateHireLicense import h_mal.appttude.com.driver.model.PrivateHireLicense
import h_mal.appttude.com.driver.model.PrivateHireVehicle import h_mal.appttude.com.driver.model.PrivateHireVehicle

View File

@@ -7,6 +7,7 @@ open class DriverProfileTest : DataSubmissionTest() {
override fun afterLaunch() { override fun afterLaunch() {
super.afterLaunch() super.afterLaunch()
home { home {
waitFor(1500)
openDriverProfile() openDriverProfile()
} }
} }

View File

@@ -26,6 +26,8 @@ class SubmitNewDriverDataTest : DriverProfileTest() {
driversLicense { driversLicense {
val data = getAssetData<DriversLicense>() val data = getAssetData<DriversLicense>()
submitAndValidate(data) submitAndValidate(data)
waitFor(17000)
} }
} }

View File

@@ -45,8 +45,7 @@ class ApplicationViewModelFactory(
) )
isAssignableFrom(VehicleProfileViewModel::class.java) -> VehicleProfileViewModel( isAssignableFrom(VehicleProfileViewModel::class.java) -> VehicleProfileViewModel(
auth, auth,
database, database
storage
) )
isAssignableFrom(InsuranceViewModel::class.java) -> InsuranceViewModel( isAssignableFrom(InsuranceViewModel::class.java) -> InsuranceViewModel(
auth, auth,

View File

@@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.driverprofile
import android.net.Uri import android.net.Uri
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment import h_mal.appttude.com.driver.base.ImageFormSubmissionFragment
import h_mal.appttude.com.driver.databinding.FragmentDriverLicenseBinding import h_mal.appttude.com.driver.databinding.FragmentDriverLicenseBinding
import h_mal.appttude.com.driver.dialogs.DateDialog import h_mal.appttude.com.driver.dialogs.DateDialog
import h_mal.appttude.com.driver.model.DriversLicense import h_mal.appttude.com.driver.model.DriversLicense
@@ -11,7 +11,7 @@ import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.DriverLicenseViewModel import h_mal.appttude.com.driver.viewmodels.DriverLicenseViewModel
class DriverLicenseFragment : class DriverLicenseFragment :
DataSubmissionBaseFragment<DriverLicenseViewModel, FragmentDriverLicenseBinding, DriversLicense>() { ImageFormSubmissionFragment<DriverLicenseViewModel, FragmentDriverLicenseBinding, DriversLicense>() {
override fun setupView(binding: FragmentDriverLicenseBinding) { override fun setupView(binding: FragmentDriverLicenseBinding) {
binding.apply { binding.apply {
@@ -25,32 +25,31 @@ class DriverLicenseFragment :
} }
licNo.setTextOnChange { model.licenseNumber = it } licNo.setTextOnChange { model.licenseNumber = it }
searchImage.setOnClickListener { openGalleryWithPermissionRequest() } searchImage.setOnClickListener { openGalleryForImageSelection() }
submit.setOnClickListener { submit.setOnClickListener {
validateEditTexts(licExpiry, licNo).isTrue { validateEditTexts(licExpiry, licNo).isTrue { submitDocument() }
viewModel.setDataInDatabase(model, picUri)
}
} }
} }
} }
override fun setFields(data: DriversLicense) { override fun setFields(data: DriversLicense) {
super.setFields(data)
applyBinding { applyBinding {
licNo.setText(data.licenseNumber) licNo.setText(data.licenseNumber)
licExpiry.setText(data.licenseExpiry) licExpiry.setText(data.licenseExpiry)
data.licenseImageString?.setImages{
driversliImg.setGlideImage(it.second)
}
} }
} }
override fun onImageGalleryResult(imageUri: Uri?) { override fun setImage(image: StorageReference?, thumbnail: StorageReference?) {
super.onImageGalleryResult(imageUri) thumbnail?.let {
applyBinding { binding.driversliImg.setGlideImage(it)
driversliImg.setGlideImage(imageUri) return
} }
binding.driversliImg.setGlideImage(image)
}
override fun onImageGalleryResult(imageUri: Uri) {
super.onImageGalleryResult(imageUri)
binding.driversliImg.setGlideImage(imageUri)
} }
} }

View File

@@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.driverprofile
import android.net.Uri import android.net.Uri
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment import h_mal.appttude.com.driver.base.ImageFormSubmissionFragment
import h_mal.appttude.com.driver.databinding.FragmentDriverProfileBinding import h_mal.appttude.com.driver.databinding.FragmentDriverProfileBinding
import h_mal.appttude.com.driver.dialogs.DateDialog import h_mal.appttude.com.driver.dialogs.DateDialog
import h_mal.appttude.com.driver.model.DriverProfile import h_mal.appttude.com.driver.model.DriverProfile
@@ -12,7 +12,7 @@ import h_mal.appttude.com.driver.viewmodels.DriverProfileViewModel
class DriverProfileFragment : class DriverProfileFragment :
DataSubmissionBaseFragment<DriverProfileViewModel, FragmentDriverProfileBinding, DriverProfile>() { ImageFormSubmissionFragment<DriverProfileViewModel, FragmentDriverProfileBinding, DriverProfile>() {
override fun setupView(binding: FragmentDriverProfileBinding) = binding.run { override fun setupView(binding: FragmentDriverProfileBinding) = binding.run {
namesInput.setTextOnChange { model.forenames = it } namesInput.setTextOnChange { model.forenames = it }
@@ -35,7 +35,7 @@ class DriverProfileFragment :
} }
} }
} }
addPhoto.setOnClickListener { openGalleryWithPermissionRequest() } addPhoto.setOnClickListener { openGalleryForImageSelection() }
submit.setOnClickListener { submit() } submit.setOnClickListener { submit() }
} }
@@ -45,13 +45,12 @@ class DriverProfileFragment :
namesInput, addressInput, postcodeInput, namesInput, addressInput, postcodeInput,
dobInput, niNumber, dateFirst dobInput, niNumber, dateFirst
).isTrue { ).isTrue {
viewModel.setDataInDatabase(model, picUri) submitDocument()
} }
} }
} }
override fun setFields(data: DriverProfile) { override fun setFields(data: DriverProfile) {
super.setFields(data)
applyBinding { applyBinding {
namesInput.setText(data.forenames) namesInput.setText(data.forenames)
addressInput.setText(data.address) addressInput.setText(data.address)
@@ -59,18 +58,16 @@ class DriverProfileFragment :
dobInput.setText(data.dob) dobInput.setText(data.dob)
niNumber.setText(data.ni) niNumber.setText(data.ni)
dateFirst.setText(data.dateFirst) dateFirst.setText(data.dateFirst)
data.driverPic?.setImages {
driverPic.setGlideImage(it.second)
}
} }
} }
override fun onImageGalleryResult(imageUri: Uri?) { override fun setImage(image: StorageReference, thumbnail: StorageReference) {
binding.driverPic.setGlideImage(thumbnail)
}
override fun onImageGalleryResult(imageUri: Uri) {
super.onImageGalleryResult(imageUri) super.onImageGalleryResult(imageUri)
applyBinding { binding.driverPic.setGlideImage(imageUri)
driverPic.setGlideImage(imageUri)
}
} }
} }

View File

@@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.driverprofile
import android.net.Uri import android.net.Uri
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment import h_mal.appttude.com.driver.base.ImageFormSubmissionFragment
import h_mal.appttude.com.driver.databinding.FragmentPrivateHireLicenseBinding import h_mal.appttude.com.driver.databinding.FragmentPrivateHireLicenseBinding
import h_mal.appttude.com.driver.dialogs.DateDialog import h_mal.appttude.com.driver.dialogs.DateDialog
import h_mal.appttude.com.driver.model.PrivateHireLicense import h_mal.appttude.com.driver.model.PrivateHireLicense
@@ -11,7 +11,7 @@ import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.PrivateHireLicenseViewModel import h_mal.appttude.com.driver.viewmodels.PrivateHireLicenseViewModel
class PrivateHireLicenseFragment : DataSubmissionBaseFragment class PrivateHireLicenseFragment : ImageFormSubmissionFragment
<PrivateHireLicenseViewModel, FragmentPrivateHireLicenseBinding, PrivateHireLicense>() { <PrivateHireLicenseViewModel, FragmentPrivateHireLicenseBinding, PrivateHireLicense>() {
override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run { override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run {
@@ -25,14 +25,14 @@ class PrivateHireLicenseFragment : DataSubmissionBaseFragment
} }
} }
uploadphlic.setOnClickListener { openGalleryWithPermissionRequest() } uploadphlic.setOnClickListener { openGalleryForImageSelection() }
submit.setOnClickListener { submit() } submit.setOnClickListener { submit() }
} }
override fun submit() { override fun submit() {
applyBinding { applyBinding {
validateEditTexts(phNo, phExpiry).isTrue { validateEditTexts(phNo, phExpiry).isTrue {
viewModel.setDataInDatabase(model, picUri) submitDocument()
} }
} }
} }
@@ -42,15 +42,16 @@ class PrivateHireLicenseFragment : DataSubmissionBaseFragment
applyBinding { applyBinding {
phNo.setText(data.phNumber) phNo.setText(data.phNumber)
phExpiry.setText(data.phExpiry) phExpiry.setText(data.phExpiry)
data.phImageString?.setImages { imageView2.setGlideImage(it.second) }
} }
} }
override fun onImageGalleryResult(imageUri: Uri?) { override fun setImage(image: StorageReference, thumbnail: StorageReference) {
binding.imageView2.setGlideImage(thumbnail)
}
override fun onImageGalleryResult(imageUri: Uri) {
super.onImageGalleryResult(imageUri) super.onImageGalleryResult(imageUri)
applyBinding { binding.imageView2.setGlideImage(imageUri)
imageView2.setGlideImage(imageUri)
}
} }
} }

View File

@@ -5,7 +5,8 @@ import android.os.Bundle
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment import h_mal.appttude.com.driver.base.MultiImageFormSubmissionFragment3
import h_mal.appttude.com.driver.data.ImageCollection
import h_mal.appttude.com.driver.databinding.FragmentInsuranceBinding import h_mal.appttude.com.driver.databinding.FragmentInsuranceBinding
import h_mal.appttude.com.driver.dialogs.DateDialog import h_mal.appttude.com.driver.dialogs.DateDialog
import h_mal.appttude.com.driver.model.Insurance import h_mal.appttude.com.driver.model.Insurance
@@ -15,13 +16,10 @@ import h_mal.appttude.com.driver.viewmodels.InsuranceViewModel
class InsuranceFragment : class InsuranceFragment :
DataSubmissionBaseFragment<InsuranceViewModel, FragmentInsuranceBinding, Insurance>() { MultiImageFormSubmissionFragment3<InsuranceViewModel, FragmentInsuranceBinding, Insurance>() {
private var selectedImages: List<Uri>? = listOf()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setImageSelectionAsMultiple()
applyBinding { applyBinding {
insurer.setTextOnChange { model.insurerName = it } insurer.setTextOnChange { model.insurerName = it }
@@ -32,7 +30,7 @@ class InsuranceFragment :
} }
} }
} }
uploadInsurance.setOnClickListener { openGalleryWithPermissionRequest() } uploadInsurance.setOnClickListener { openGalleryForImageSelection() }
submit.setOnClickListener { submit() } submit.setOnClickListener { submit() }
} }
} }
@@ -57,35 +55,24 @@ class InsuranceFragment :
super.submit() super.submit()
applyBinding { applyBinding {
validateEditTexts(insurer, insuranceExp).isTrue { validateEditTexts(insurer, insuranceExp).isTrue {
viewModel.setDataInDatabase(model, selectedImages) submitDocument()
} }
} }
} }
override fun setFields(data: Insurance) { override fun setFields(data: Insurance) {
super.setFields(data)
applyBinding { applyBinding {
insurer.setText(model.insurerName) insurer.setText(model.insurerName)
insuranceExp.setText(model.expiryDate) insuranceExp.setText(model.expiryDate)
data.photoStrings?.also {
val keys = viewModel.getMultipleImagesAndThumbnails(it).map {i -> i.key }
updateImageCarousal(keys)
}
} }
} }
override fun onImageGalleryResult(imageUris: List<Uri>?) { override fun setImages(collection: ImageCollection) {
selectedImages = imageUris updateImageCarousal(collection.collection.map { it.second })
selectedImages?.let { updateImageCarousal(it) }
} }
override fun onSuccess(data: Any?) { override fun onImageGalleryResult(imageUris: List<Uri>) {
super.onSuccess(data) updateImageCarousal(imageUris)
if (data is Map<*,*>) {
updateImageCarousal(data.map { it.value })
}
} }
} }

View File

@@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.vehicleprofile
import android.net.Uri import android.net.Uri
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment import h_mal.appttude.com.driver.base.ImageFormSubmissionFragment
import h_mal.appttude.com.driver.databinding.FragmentLogbookBinding import h_mal.appttude.com.driver.databinding.FragmentLogbookBinding
import h_mal.appttude.com.driver.model.Logbook import h_mal.appttude.com.driver.model.Logbook
import h_mal.appttude.com.driver.utils.isTrue import h_mal.appttude.com.driver.utils.isTrue
@@ -11,11 +11,11 @@ import h_mal.appttude.com.driver.viewmodels.LogbookViewModel
class LogbookFragment : class LogbookFragment :
DataSubmissionBaseFragment<LogbookViewModel, FragmentLogbookBinding, Logbook>() { ImageFormSubmissionFragment<LogbookViewModel, FragmentLogbookBinding, Logbook>() {
override fun setupView(binding: FragmentLogbookBinding) = binding.run { override fun setupView(binding: FragmentLogbookBinding) = binding.run {
v5cNo.setTextOnChange { model.v5cnumber = it } v5cNo.setTextOnChange { model.v5cnumber = it }
uploadLb.setOnClickListener { openGalleryWithPermissionRequest() } uploadLb.setOnClickListener { openGalleryForImageSelection() }
submit.setOnClickListener { submit() } submit.setOnClickListener { submit() }
} }
@@ -23,24 +23,23 @@ class LogbookFragment :
super.submit() super.submit()
applyBinding { applyBinding {
validateEditTexts(v5cNo).isTrue { validateEditTexts(v5cNo).isTrue {
viewModel.setDataInDatabase(model, picUri) submitDocument()
} }
} }
} }
override fun setFields(data: Logbook) { override fun setFields(data: Logbook) {
super.setFields(data)
applyBinding { applyBinding {
v5cNo.setText(data.v5cnumber) v5cNo.setText(data.v5cnumber)
data.photoString?.setImages { logBookImg.setGlideImage(it.second) }
} }
} }
override fun onImageGalleryResult(imageUri: Uri?) { override fun setImage(image: StorageReference, thumbnail: StorageReference) {
binding.logBookImg.setGlideImage(thumbnail)
}
override fun onImageGalleryResult(imageUri: Uri) {
super.onImageGalleryResult(imageUri) super.onImageGalleryResult(imageUri)
applyBinding { binding.logBookImg.setGlideImage(imageUri)
logBookImg.setGlideImage(picUri)
}
} }
} }

View File

@@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.vehicleprofile
import android.net.Uri import android.net.Uri
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment import h_mal.appttude.com.driver.base.ImageFormSubmissionFragment
import h_mal.appttude.com.driver.databinding.FragmentMotBinding import h_mal.appttude.com.driver.databinding.FragmentMotBinding
import h_mal.appttude.com.driver.dialogs.DateDialog import h_mal.appttude.com.driver.dialogs.DateDialog
import h_mal.appttude.com.driver.model.Mot import h_mal.appttude.com.driver.model.Mot
@@ -11,7 +11,7 @@ import h_mal.appttude.com.driver.utils.setGlideImage
import h_mal.appttude.com.driver.viewmodels.MotViewModel import h_mal.appttude.com.driver.viewmodels.MotViewModel
class MotFragment : DataSubmissionBaseFragment<MotViewModel, FragmentMotBinding, Mot>() { class MotFragment : ImageFormSubmissionFragment<MotViewModel, FragmentMotBinding, Mot>() {
override fun setupView(binding: FragmentMotBinding) = binding.run { override fun setupView(binding: FragmentMotBinding) = binding.run {
motExpiry.apply { motExpiry.apply {
@@ -22,26 +22,26 @@ class MotFragment : DataSubmissionBaseFragment<MotViewModel, FragmentMotBinding,
} }
} }
uploadmot.setOnClickListener { openGalleryWithPermissionRequest() } uploadmot.setOnClickListener { openGalleryForImageSelection() }
submit.setOnClickListener { submit.setOnClickListener {
validateEditTexts(motExpiry).isTrue { validateEditTexts(motExpiry).isTrue {
viewModel.setDataInDatabase(model, picUri) submitDocument()
} }
} }
} }
override fun setFields(data: Mot) { override fun setFields(data: Mot) {
super.setFields(data)
applyBinding { applyBinding {
motExpiry.setText(data.motExpiry) motExpiry.setText(data.motExpiry)
data.motImageString?.setImages { motImg.setGlideImage(it.second) }
} }
} }
override fun onImageGalleryResult(imageUri: Uri?) { override fun setImage(image: StorageReference, thumbnail: StorageReference) {
binding.motImg.setGlideImage(thumbnail)
}
override fun onImageGalleryResult(imageUri: Uri) {
super.onImageGalleryResult(imageUri) super.onImageGalleryResult(imageUri)
applyBinding { binding.motImg.setGlideImage(imageUri)
motImg.setGlideImage(imageUri)
}
} }
} }

View File

@@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.vehicleprofile
import android.net.Uri import android.net.Uri
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment import h_mal.appttude.com.driver.base.ImageFormSubmissionFragment
import h_mal.appttude.com.driver.databinding.FragmentPrivateHireLicenseBinding import h_mal.appttude.com.driver.databinding.FragmentPrivateHireLicenseBinding
import h_mal.appttude.com.driver.dialogs.DateDialog import h_mal.appttude.com.driver.dialogs.DateDialog
import h_mal.appttude.com.driver.model.PrivateHireVehicle import h_mal.appttude.com.driver.model.PrivateHireVehicle
@@ -12,7 +12,7 @@ import h_mal.appttude.com.driver.viewmodels.PrivateHireVehicleViewModel
class PrivateHireVehicleFragment : class PrivateHireVehicleFragment :
DataSubmissionBaseFragment<PrivateHireVehicleViewModel, FragmentPrivateHireLicenseBinding, PrivateHireVehicle>() { ImageFormSubmissionFragment<PrivateHireVehicleViewModel, FragmentPrivateHireLicenseBinding, PrivateHireVehicle>() {
override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run { override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run {
phNo.setTextOnChange { model.phCarNumber = it } phNo.setTextOnChange { model.phCarNumber = it }
@@ -24,28 +24,29 @@ class PrivateHireVehicleFragment :
} }
} }
uploadphlic.setOnClickListener { openGalleryWithPermissionRequest() } uploadphlic.setOnClickListener { openGalleryForImageSelection() }
submit.setOnClickListener { submit.setOnClickListener {
validateEditTexts(phNo, phExpiry).isTrue { validateEditTexts(phNo, phExpiry).isTrue {
viewModel.setDataInDatabase(model, picUri) submitDocument()
} }
} }
} }
override fun setFields(data: PrivateHireVehicle) { override fun setFields(
super.setFields(data) data: PrivateHireVehicle
) {
applyBinding { applyBinding {
phNo.setText(data.phCarNumber) phNo.setText(data.phCarNumber)
phExpiry.setText(data.phCarExpiry) phExpiry.setText(data.phCarExpiry)
data.phCarImageString?.setImages { imageView2.setGlideImage(it.second) }
} }
} }
override fun onImageGalleryResult(imageUri: Uri?) { override fun setImage(image: StorageReference, thumbnail: StorageReference) {
binding.imageView2.setGlideImage(thumbnail)
}
override fun onImageGalleryResult(imageUri: Uri) {
super.onImageGalleryResult(imageUri) super.onImageGalleryResult(imageUri)
applyBinding { binding.imageView2.setGlideImage(imageUri)
imageView2.setGlideImage(imageUri)
}
} }
} }

View File

@@ -1,6 +1,6 @@
package h_mal.appttude.com.driver.ui.vehicleprofile package h_mal.appttude.com.driver.ui.vehicleprofile
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment import h_mal.appttude.com.driver.base.FormSubmissionFragment
import h_mal.appttude.com.driver.databinding.FragmentVehicleSetupBinding import h_mal.appttude.com.driver.databinding.FragmentVehicleSetupBinding
import h_mal.appttude.com.driver.dialogs.DateDialog import h_mal.appttude.com.driver.dialogs.DateDialog
import h_mal.appttude.com.driver.model.VehicleProfile import h_mal.appttude.com.driver.model.VehicleProfile
@@ -8,8 +8,7 @@ import h_mal.appttude.com.driver.utils.isTrue
import h_mal.appttude.com.driver.viewmodels.VehicleProfileViewModel import h_mal.appttude.com.driver.viewmodels.VehicleProfileViewModel
class VehicleProfileFragment : DataSubmissionBaseFragment class VehicleProfileFragment : FormSubmissionFragment<VehicleProfileViewModel, FragmentVehicleSetupBinding, VehicleProfile>() {
<VehicleProfileViewModel, FragmentVehicleSetupBinding, VehicleProfile>() {
override fun setupView(binding: FragmentVehicleSetupBinding) = binding.run { override fun setupView(binding: FragmentVehicleSetupBinding) = binding.run {
reg.setTextOnChange { model.reg = it } reg.setTextOnChange { model.reg = it }
@@ -39,7 +38,7 @@ class VehicleProfileFragment : DataSubmissionBaseFragment
postcode, postcode,
startDate startDate
).isTrue { ).isTrue {
viewModel.setDataInDatabase(model) submitDocument()
} }
} }
} }

View File

@@ -1,35 +1,33 @@
package h_mal.appttude.com.driver.viewmodels package h_mal.appttude.com.driver.viewmodels
import android.net.Uri
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel import h_mal.appttude.com.driver.base.DataSubmissionViewModel2
import h_mal.appttude.com.driver.data.DRIVERS_LICENSE_SREF
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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.Storage
import h_mal.appttude.com.driver.model.DriversLicense import h_mal.appttude.com.driver.model.DriversLicense
import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.DateUtils.parseDateStringIntoCalender
import kotlinx.coroutines.Job import org.joda.time.LocalDate
class DriverLicenseViewModel( class DriverLicenseViewModel(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource,
storage: FirebaseStorageSource storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<DriversLicense>(auth, database, storage) { ) : DataSubmissionViewModel2<DriversLicense>(auth, database, storage, Storage.DRIVERS_LICENSE) {
override val databaseRef: DatabaseReference = database.getDriverLicenseRef(uid) override val databaseRef: DatabaseReference = database.getDriverLicenseRef(uid)
override val storageRef: StorageReference = storage.driversLicenseStorageRef(uid) override val storageRef: StorageReference = storage.driversLicenseStorageRef(uid)
override val objectName: String = "drivers license"
override fun getDataFromDatabase() = retrieveDataFromDatabase<DriversLicense>() override fun validateData(data: DriversLicense): Boolean {
data.licenseNumber.validateStringOrThrow("License number")
override fun setDataInDatabase(data: DriversLicense, localImageUri: Uri?) = io { if (parseDateStringIntoCalender(data.licenseExpiry!!).isBefore(LocalDate.now())) {
doTryOperation("Failed to upload $objectName") { onError("License expiry cannot be before today")
val imageUrl = getImageUrl(localImageUri, data.licenseImageString) return false
data.licenseImageString = imageUrl
postDataToDatabase(data)
} }
return true
} }
} }

View File

@@ -1,40 +0,0 @@
package h_mal.appttude.com.driver.viewmodels
import android.net.Uri
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.base.DataSubmissionViewModel2
import h_mal.appttude.com.driver.data.DRIVERS_LICENSE_SREF
import h_mal.appttude.com.driver.data.FirebaseAuthentication
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.data.FirebaseStorageSource
import h_mal.appttude.com.driver.data.Storage
import h_mal.appttude.com.driver.model.DriversLicense
import h_mal.appttude.com.driver.utils.Coroutines.io
import kotlinx.coroutines.Job
class DriverLicenseViewModel2(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource,
storage: FirebaseStorageSource
) : DataSubmissionViewModel2<DriversLicense>(auth, database, storage) {
override val databaseRef: DatabaseReference = database.getDriverLicenseRef(uid)
override val storageRef: StorageReference = storage.driversLicenseStorageRef(uid)
override fun validateData(data: DriversLicense): Boolean {
// TODO Not yet implemented
return true
}
override fun setDataAfterUpload(data: DriversLicense, filename: String): DriversLicense {
return data.apply {
licenseImageString = filename
}
}
fun postDataToDatabase(imageUri: Uri, data: DriversLicense) {
postDataToDatabase(imageUri, data, Storage.DRIVERS_LICENSE)
}
}

View File

@@ -4,33 +4,39 @@ import android.net.Uri
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.driver.base.DataSubmissionViewModel2
import h_mal.appttude.com.driver.data.DRIVER_PROFILE import h_mal.appttude.com.driver.data.DRIVER_PROFILE
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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.PROFILE_SREF import h_mal.appttude.com.driver.data.PROFILE_SREF
import h_mal.appttude.com.driver.data.Storage
import h_mal.appttude.com.driver.model.DriverProfile import h_mal.appttude.com.driver.model.DriverProfile
import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.Coroutines.io
import h_mal.appttude.com.driver.utils.DateUtils
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import org.joda.time.LocalDate
class DriverProfileViewModel( class DriverProfileViewModel(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource,
storage: FirebaseStorageSource storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<DriverProfile>(auth, database, storage) { ) : DataSubmissionViewModel2<DriverProfile>(auth, database, storage, Storage.PROFILE) {
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 fun getDataFromDatabase() = retrieveDataFromDatabase<DriverProfile>()
override fun setDataInDatabase(data: DriverProfile, localImageUri: Uri?) = io { override fun validateData(data: DriverProfile): Boolean {
doTryOperation("Failed to upload $objectName") { data.ni.validateStringOrThrow("National Insurance number")
data.address.validateStringOrThrow("Address")
val imageUrl = getImageUrl(localImageUri, data.driverPic) data.postcode.validateStringOrThrow("Postcode")
data.driverPic = imageUrl data.forenames.validateStringOrThrow("Name")
postDataToDatabase(data) data.dob
if (DateUtils.parseDateStringIntoCalender(data.dob!!).isAfter(LocalDate.now().minusYears(17))) {
onError("Driver cannot be under 17")
return false
} }
return true
} }
} }

View File

@@ -1,43 +1,32 @@
package h_mal.appttude.com.driver.viewmodels package h_mal.appttude.com.driver.viewmodels
import android.net.Uri
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel import h_mal.appttude.com.driver.base.DataSubmissionViewModel3
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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.INSURANCE_SREF import h_mal.appttude.com.driver.data.Storage
import h_mal.appttude.com.driver.model.Insurance import h_mal.appttude.com.driver.model.Insurance
import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.DateUtils
import kotlinx.coroutines.Job import org.joda.time.LocalDate
class InsuranceViewModel( class InsuranceViewModel(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource,
storage: FirebaseStorageSource storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<Insurance>(auth, database, storage) { ) : DataSubmissionViewModel3<Insurance>(auth, database, storage, Storage.INSURANCE) {
override val databaseRef: DatabaseReference = database.getInsuranceDetailsRef(uid) override val databaseRef: DatabaseReference = database.getInsuranceDetailsRef(uid)
override val storageRef: StorageReference = storage.insuranceStorageRef(uid) override val storageRef: StorageReference = storage.insuranceStorageRef(uid)
override val objectName: String = "insurance"
override fun getDataFromDatabase() = retrieveDataFromDatabase<Insurance>() override fun validateData(data: Insurance): Boolean {
data.insurerName.validateStringOrThrow("Insurer name")
override fun setDataInDatabase(data: Insurance, localImageUris: List<Uri?>?) = io { if (DateUtils.parseDateStringIntoCalender(data.expiryDate!!).isBefore(LocalDate.now())) {
doTryOperation("Failed to upload $objectName") { onError("Insurance expiry cannot be before today")
val imageUrls = if (!localImageUris.isNullOrEmpty()) { return false
getImageUrls(localImageUris).toMutableList()
} else {
data.photoStrings
}
if (imageUrls.isNullOrEmpty()) {
onError("no images selected")
return@doTryOperation
}
data.photoStrings = imageUrls
postDataToDatabase(data)
} }
return true
} }
} }

View File

@@ -1,37 +1,26 @@
package h_mal.appttude.com.driver.viewmodels package h_mal.appttude.com.driver.viewmodels
import android.net.Uri
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel import h_mal.appttude.com.driver.base.DataSubmissionViewModel2
import h_mal.appttude.com.driver.data.DRIVER_PROFILE
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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.LOG_BOOK_SREF import h_mal.appttude.com.driver.data.Storage
import h_mal.appttude.com.driver.model.DriverProfile
import h_mal.appttude.com.driver.model.Logbook import h_mal.appttude.com.driver.model.Logbook
import h_mal.appttude.com.driver.utils.Coroutines.io
import kotlinx.coroutines.Job
class LogbookViewModel( class LogbookViewModel(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource,
storage: FirebaseStorageSource storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<Logbook>(auth, database, storage) { ) : DataSubmissionViewModel2<Logbook>(auth, database, storage, Storage.LOG_BOOK) {
override val databaseRef: DatabaseReference = database.getLogbookRef(uid) override val databaseRef: DatabaseReference = database.getLogbookRef(uid)
override val storageRef: StorageReference = storage.logBookStorageRef(uid) override val storageRef: StorageReference = storage.logBookStorageRef(uid)
override val objectName: String = "Log book"
override fun getDataFromDatabase() = retrieveDataFromDatabase<Logbook>() override fun validateData(data: Logbook): Boolean {
data.v5cnumber.validateStringOrThrow("V5C number")
override fun setDataInDatabase(data: Logbook, localImageUri: Uri?) = io { return true
doTryOperation("Failed to upload $objectName") {
val imageUrl = getImageUrl(localImageUri, data.photoString)
data.photoString = imageUrl
postDataToDatabase(data)
}
} }
} }

View File

@@ -4,35 +4,36 @@ import android.net.Uri
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.driver.base.DataSubmissionViewModel2
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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.LOG_BOOK_SREF import h_mal.appttude.com.driver.data.LOG_BOOK_SREF
import h_mal.appttude.com.driver.data.MOT import h_mal.appttude.com.driver.data.MOT
import h_mal.appttude.com.driver.data.MOT_SREF import h_mal.appttude.com.driver.data.MOT_SREF
import h_mal.appttude.com.driver.data.Storage
import h_mal.appttude.com.driver.model.Logbook import h_mal.appttude.com.driver.model.Logbook
import h_mal.appttude.com.driver.model.Mot import h_mal.appttude.com.driver.model.Mot
import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.Coroutines.io
import h_mal.appttude.com.driver.utils.DateUtils
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import org.joda.time.LocalDate
class MotViewModel( class MotViewModel(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource,
storage: FirebaseStorageSource storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<Mot>(auth, database, storage) { ) : DataSubmissionViewModel2<Mot>(auth, database, storage, Storage.MOT) {
override val databaseRef: DatabaseReference = database.getMotDetailsRef(uid) override val databaseRef: DatabaseReference = database.getMotDetailsRef(uid)
override val storageRef: StorageReference? = storage.motStorageRef(uid) override val storageRef: StorageReference = storage.motStorageRef(uid)
override val objectName: String = "vehicle profile"
override fun getDataFromDatabase() = retrieveDataFromDatabase<Mot>() override fun validateData(data: Mot): Boolean {
if (DateUtils.parseDateStringIntoCalender(data.motExpiry!!).isBefore(LocalDate.now())) {
override fun setDataInDatabase(data: Mot, localImageUri: Uri?) = io { onError("MOT expiry cannot be before today")
doTryOperation("Failed to upload $objectName") { return false
val imageUrl = getImageUrl(localImageUri, data.motImageString)
data.motImageString = imageUrl
postDataToDatabase(data)
} }
return true
} }
} }

View File

@@ -4,39 +4,37 @@ import android.net.Uri
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.driver.base.DataSubmissionViewModel2
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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.MOT_SREF import h_mal.appttude.com.driver.data.MOT_SREF
import h_mal.appttude.com.driver.data.PRIVATE_HIRE_SREF import h_mal.appttude.com.driver.data.PRIVATE_HIRE_SREF
import h_mal.appttude.com.driver.data.Storage
import h_mal.appttude.com.driver.model.Mot import h_mal.appttude.com.driver.model.Mot
import h_mal.appttude.com.driver.model.PrivateHireLicense import h_mal.appttude.com.driver.model.PrivateHireLicense
import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.Coroutines.io
import h_mal.appttude.com.driver.utils.DateUtils
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import org.joda.time.LocalDate
class PrivateHireLicenseViewModel( class PrivateHireLicenseViewModel(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource,
storage: FirebaseStorageSource storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<PrivateHireLicense>(auth, database, storage) { ) : DataSubmissionViewModel2<PrivateHireLicense>(auth, database, storage, Storage.DRIVERS_LICENSE) {
override val databaseRef: DatabaseReference = database.getPrivateHireRef(uid) override val databaseRef: DatabaseReference = database.getPrivateHireRef(uid)
override val storageRef: StorageReference = storage.privateHireStorageRef(uid) override val storageRef: StorageReference = storage.privateHireStorageRef(uid)
override val objectName: String = "private hire license"
override fun getDataFromDatabase() = retrieveDataFromDatabase<PrivateHireLicense>() override fun validateData(data: PrivateHireLicense): Boolean {
data.phNumber.validateStringOrThrow("License number")
override fun setDataInDatabase(data: PrivateHireLicense, localImageUri: Uri?) = io { if (DateUtils.parseDateStringIntoCalender(data.phExpiry!!).isBefore(LocalDate.now())) {
doTryOperation("Failed to upload private hire license") { onError("License expiry cannot be before today")
val imageUrl = getImageUrl(localImageUri, data.phImageString) return false
val driverLicense = PrivateHireLicense(
phExpiry = data.phExpiry,
phNumber = data.phNumber,
phImageString = imageUrl
)
postDataToDatabase(driverLicense)
} }
return true
} }
} }

View File

@@ -1,36 +1,37 @@
package h_mal.appttude.com.driver.viewmodels package h_mal.appttude.com.driver.viewmodels
import android.net.Uri
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel import h_mal.appttude.com.driver.base.DataSubmissionViewModel2
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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.PRIVATE_HIRE_SREF import h_mal.appttude.com.driver.data.Storage
import h_mal.appttude.com.driver.data.PRIVATE_HIRE_VEHICLE_SREF
import h_mal.appttude.com.driver.model.PrivateHireLicense
import h_mal.appttude.com.driver.model.PrivateHireVehicle import h_mal.appttude.com.driver.model.PrivateHireVehicle
import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.DateUtils
import kotlinx.coroutines.Job import org.joda.time.LocalDate
class PrivateHireVehicleViewModel( class PrivateHireVehicleViewModel(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource,
storage: FirebaseStorageSource storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<PrivateHireVehicle>(auth, database, storage) { ) : DataSubmissionViewModel2<PrivateHireVehicle>(
auth,
database,
storage,
Storage.PRIVATE_HIRE_VEHICLE
) {
override val databaseRef: DatabaseReference = database.getPrivateHireVehicleRef(uid) override val databaseRef: DatabaseReference = database.getPrivateHireVehicleRef(uid)
override val storageRef: StorageReference = storage.privateHireVehicleStorageRef(uid) override val storageRef: StorageReference = storage.privateHireVehicleStorageRef(uid)
override val objectName: String = "private hire vehicle license"
override fun getDataFromDatabase() = retrieveDataFromDatabase<PrivateHireVehicle>() override fun validateData(data: PrivateHireVehicle): Boolean {
data.phCarNumber.validateStringOrThrow("License number")
override fun setDataInDatabase(data: PrivateHireVehicle, localImageUri: Uri?) = io { if (DateUtils.parseDateStringIntoCalender(data.phCarExpiry!!).isBefore(LocalDate.now())) {
doTryOperation("Failed to upload $objectName") { onError("License expiry cannot be before today")
val imageUrl = getImageUrl(localImageUri, data.phCarImageString) return false
data.phCarImageString = imageUrl
postDataToDatabase(data)
} }
return true
} }
} }

View File

@@ -3,6 +3,7 @@ package h_mal.appttude.com.driver.viewmodels
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.driver.base.DataSubmissionViewModel
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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
@@ -11,22 +12,22 @@ import h_mal.appttude.com.driver.utils.Coroutines.io
class VehicleProfileViewModel( class VehicleProfileViewModel(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource
storage: FirebaseStorageSource ) : DataSubmissionViewModel<VehicleProfile>(auth, database) {
) : DataSubmissionBaseViewModel<VehicleProfile>(auth, database, storage) {
override val databaseRef: DatabaseReference = database.getVehicleDetailsRef(uid) override val databaseRef: DatabaseReference = database.getVehicleDetailsRef(uid)
override val storageRef: StorageReference? = null
override val objectName: String = "vehicle profile"
override fun getDataFromDatabase() = retrieveDataFromDatabase<VehicleProfile>() override fun validateData(data: VehicleProfile): Boolean {
data.colour.validateStringOrThrow("Vehicle colour")
data.model.validateStringOrThrow("Vehicle model")
data.reg.validateStringOrThrow("Vehicle registration plate")
data.make.validateStringOrThrow("Vehicle make")
data.keeperAddress.validateStringOrThrow("Keeper address")
data.keeperName.validateStringOrThrow("Keeper name")
data.keeperPostCode.validateStringOrThrow("Keeper post code")
override fun setDataInDatabase(data: VehicleProfile) {
io { return true
doTryOperation("Failed to upload $objectName") {
postDataToDatabase(data)
}
}
} }

View File

@@ -0,0 +1,46 @@
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.base.DataSubmissionViewModel
import h_mal.appttude.com.driver.data.FirebaseAuthentication
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.data.FirebaseStorageSource
import h_mal.appttude.com.driver.model.VehicleProfile
import h_mal.appttude.com.driver.utils.Coroutines.io
import java.io.IOException
class VehicleProfileViewModel2(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource
) : DataSubmissionViewModel<VehicleProfile>(auth, database) {
override val databaseRef: DatabaseReference = database.getVehicleDetailsRef(uid)
override fun validateData(data: VehicleProfile): Boolean {
if (data.model.isNullOrEmpty()) {
throw IOException("Vehicle model cannot be empty")
}
if (data.reg.isNullOrEmpty()) {
throw IOException("Vehicle registration cannot be empty")
}
if (data.make.isNullOrEmpty()) {
throw IOException("Vehicle make cannot be empty")
}
if (data.colour.isNullOrEmpty()) {
throw IOException("Vehicle colour cannot be empty")
}
if (data.keeperName.isNullOrEmpty()) {
throw IOException("Keepers name cannot be empty")
}
if (data.keeperAddress.isNullOrEmpty()) {
throw IOException("Keepers address cannot be empty")
}
if (data.keeperPostCode.isNullOrEmpty()) {
throw IOException("Keepers post code cannot be empty")
}
return true
}
}

View File

@@ -18,7 +18,7 @@ import org.kodein.di.generic.instance
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 val binding: VB
get() = _binding ?: error("Must only access binding while fragment is attached.") get() = _binding ?: error("Must only access binding while fragment is attached.")
var mActivity: BaseActivity<V, *>? = null var mActivity: BaseActivity<V, *>? = null

View File

@@ -1,7 +1,6 @@
package h_mal.appttude.com.driver.base package h_mal.appttude.com.driver.base
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.EditText import android.widget.EditText
@@ -9,11 +8,9 @@ import androidx.core.widget.doAfterTextChanged
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.data.UserAuthState import h_mal.appttude.com.driver.data.UserAuthState
import h_mal.appttude.com.driver.model.Model
import h_mal.appttude.com.driver.ui.user.LoginActivity import h_mal.appttude.com.driver.ui.user.LoginActivity
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
import h_mal.appttude.com.driver.utils.TextValidationUtils.validateEditText import h_mal.appttude.com.driver.utils.TextValidationUtils.validateEditText
import h_mal.appttude.com.driver.utils.setGlideImage
import kotlin.reflect.full.createInstance import kotlin.reflect.full.createInstance
abstract class DataSubmissionBaseFragment<V : DataSubmissionBaseViewModel<T>, VB : ViewBinding, T : Model> : abstract class DataSubmissionBaseFragment<V : DataSubmissionBaseViewModel<T>, VB : ViewBinding, T : Model> :

View File

@@ -53,8 +53,7 @@ abstract class DataSubmissionBaseViewModel<T : Any>(
return localImageUri?.let { uri -> return localImageUri?.let { uri ->
storageRef?.let { storageRef?.let {
val image = storage?.uploadImage(uri, it, imageString) storage!!.uploadImageReturnRef(uri, it, imageString)
image.toString()
} }
} }
} }
@@ -67,6 +66,14 @@ abstract class DataSubmissionBaseViewModel<T : Any>(
return uploadImage(localImageUri) ?: imageUrl!! return uploadImage(localImageUri) ?: imageUrl!!
} }
suspend fun getImageStorageRefAfterUpload(localImageUri: Uri?): String {
if (localImageUri == null) {
throw IOException("No image is selected")
}
return uploadImage(localImageUri) ?: throw IOException("Image ")
}
suspend fun getImageUrls(localImageUris: List<Uri?>?): List<String?> { suspend fun getImageUrls(localImageUris: List<Uri?>?): List<String?> {
if (localImageUris.isNullOrEmpty()) { if (localImageUris.isNullOrEmpty()) {
throw IOException("No images is selected") throw IOException("No images is selected")

View File

@@ -2,17 +2,22 @@ package h_mal.appttude.com.driver.base
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.ValueEventListener
import h_mal.appttude.com.driver.data.DataState import h_mal.appttude.com.driver.data.DataState
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
import h_mal.appttude.com.driver.data.FirebaseCompletion import h_mal.appttude.com.driver.data.FirebaseCompletion
import h_mal.appttude.com.driver.data.FirebaseDataLiveData
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.utils.Coroutines.io 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.toLiveData import h_mal.appttude.com.driver.utils.toLiveData
import java.io.IOException import java.io.IOException
abstract class DataSubmissionViewModel<T : Any>( abstract class DataSubmissionViewModel<T : Document>(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
private val database: FirebaseDatabaseSource private val database: FirebaseDatabaseSource
) : BaseViewModel() { ) : BaseViewModel() {
@@ -21,15 +26,33 @@ abstract class DataSubmissionViewModel<T : Any>(
abstract val databaseRef: DatabaseReference abstract val databaseRef: DatabaseReference
private val refLiveData: LiveData<DataState> by lazy { databaseRef.toLiveData<T>() } private val refLiveData: LiveData<DataState> by lazy { FirebaseDataLiveData<T>(databaseRef, getGenericClassAt<T>(0).java) }
@Suppress("UNCHECKED_CAST")
private val observer = Observer<DataState> { private val observer = Observer<DataState> {
when (it) { when (it) {
is DataState.HasData<*> -> onSuccess(it.data) is DataState.HasData<*> -> {
if (it.data is Document) setData(it.data as T)
}
is DataState.HasError -> onError(it.error.message) is DataState.HasError -> onError(it.error.message)
} }
} }
init { private val valueListener = object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
snapshot.getValue(getGenericClassAt<T>(0).javaObjectType)
}
override fun onCancelled(error: DatabaseError) {
}
}
open fun setData(data: T) {
onSuccess(data)
}
fun init() {
// databaseRef.addValueEventListener()
refLiveData.observeForever(observer) refLiveData.observeForever(observer)
} }
@@ -51,13 +74,19 @@ abstract class DataSubmissionViewModel<T : Any>(
open fun postDataToDatabase(data: T) { open fun postDataToDatabase(data: T) {
io { io {
if (!validateData(data)) return@io
if (getDataFromDatabase() == data) return@io
doTryOperation("Failed to submit document") { doTryOperation("Failed to submit document") {
if (!validateData(data)) return@doTryOperation
val postObject = database.postToDatabaseRed(databaseRef, data) val postObject = database.postToDatabaseRed(databaseRef, data)
onSuccess(postObject) onSuccess(postObject)
} }
} }
} }
fun String?.validateStringOrThrow(fieldName: String) {
if (isNullOrEmpty()) {
throw IOException("$fieldName cannot be empty")
}
}
} }

View File

@@ -1,32 +1,38 @@
package h_mal.appttude.com.driver.base package h_mal.appttude.com.driver.base
import android.net.Uri import android.net.Uri
import com.google.android.gms.tasks.Tasks
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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.ImageDocumentFile
import h_mal.appttude.com.driver.data.ImageResults
import h_mal.appttude.com.driver.data.Storage import h_mal.appttude.com.driver.data.Storage
import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.Coroutines.io
import h_mal.appttude.com.driver.utils.DateUtils import h_mal.appttude.com.driver.utils.DateUtils
import h_mal.appttude.com.driver.utils.isNotNull
import kotlinx.coroutines.tasks.await
abstract class DataSubmissionViewModel2<T : Any>( abstract class DataSubmissionViewModel2<T : ImageDocument>(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource,
private val storage: FirebaseStorageSource private val storage: FirebaseStorageSource,
private val storageType: Storage
) : DataSubmissionViewModel<T>(auth, database) { ) : DataSubmissionViewModel<T>(auth, database) {
abstract val storageRef: StorageReference abstract val storageRef: StorageReference
// retrieve image and thumbnail as a pair // retrieve image and thumbnail as a pair
fun getImageAndThumbnail(filename: String): Pair<StorageReference, StorageReference> { fun getImageAndThumbnail(filename: String): ImageResults {
val thumbnail = StringBuilder() val thumbnail = StringBuilder()
.append("thumb_") .append("thumb_")
.append(filename.split(".")[0]) .append(filename.split(".")[0])
.append(".png") .append(".png")
.toString() .toString()
return Pair( return ImageResults(
storageRef.child(filename), storageRef.child(filename),
storageRef.child(thumbnail) storageRef.child(thumbnail)
) )
@@ -42,14 +48,44 @@ abstract class DataSubmissionViewModel2<T : Any>(
return storage.uploadImageReturnName(localImageUri, storageRef, imageString) return storage.uploadImageReturnName(localImageUri, storageRef, imageString)
} }
fun postDataToDatabase(localImageUri: Uri, data: T, storages: Storage) { fun postDataToDatabase(localImageUri: Uri?, data: T) {
io { io {
val fileName = uploadImage(localImageUri, storages) val documentFileName = data.getImageFileName()
val afterData = setDataAfterUpload(data, fileName) // Validate at least image selector image or previously uploaded documents photo available
if (localImageUri == null && documentFileName.isNullOrEmpty()) {
onError("Please select an image")
return@io
}
// Upload a new image or keep old image
val fileName = localImageUri?.let { uploadImage(it, storageType) } ?: documentFileName
super.postDataToDatabase(afterData) fileName.isNotNull {
data.setImageFileName(it)
super.postDataToDatabase(data)
return@io
}
onError("Could not upload document")
} }
} }
abstract fun setDataAfterUpload(data: T, filename: String): T override fun postDataToDatabase(data: T) {}
override fun setData(data: T) {
val fileName = data.getImageFileName()
io {
fileName?.let {
val pair = getImageAndThumbnail(it)
val img = if (pair.image?.downloadUrl?.await() != null) pair.image else null
val tmb = if (pair.thumbnail?.downloadUrl?.await() != null) pair.image else null
val image = ImageResults(img, tmb)
val document = ImageDocumentFile(data, image)
onSuccess(document)
}
super.setData(data)
}
}
} }

View File

@@ -1,26 +1,30 @@
package h_mal.appttude.com.driver.base package h_mal.appttude.com.driver.base
import android.net.Uri import android.net.Uri
import com.google.android.gms.tasks.Tasks
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.data.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseAuthentication
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.ImageCollection
import h_mal.appttude.com.driver.data.Storage import h_mal.appttude.com.driver.data.Storage
import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.Coroutines.io
import h_mal.appttude.com.driver.utils.DateUtils import h_mal.appttude.com.driver.utils.DateUtils
import h_mal.appttude.com.driver.utils.isNotNull
import h_mal.appttude.com.driver.utils.mapIndexSuspend import h_mal.appttude.com.driver.utils.mapIndexSuspend
abstract class DataSubmissionViewModel3<T : Any>( abstract class DataSubmissionViewModel3<T : MultiImageDocument>(
auth: FirebaseAuthentication, auth: FirebaseAuthentication,
database: FirebaseDatabaseSource, database: FirebaseDatabaseSource,
private val storage: FirebaseStorageSource private val storage: FirebaseStorageSource,
private val storageType: Storage
) : DataSubmissionViewModel<T>(auth, database) { ) : DataSubmissionViewModel<T>(auth, database) {
abstract val storageRef: StorageReference abstract val storageRef: StorageReference
fun getImagesAndThumbnails(filenames: List<String>): List<Pair<StorageReference, StorageReference>> { fun getImagesAndThumbnails(filenames: List<String>): ImageCollection {
return filenames.map { val listMap = filenames.map {
val thumbnail = StringBuilder() val thumbnail = StringBuilder()
.append("thumb_") .append("thumb_")
.append(it.split(".")[0]) .append(it.split(".")[0])
@@ -32,6 +36,7 @@ abstract class DataSubmissionViewModel3<T : Any>(
storageRef.child(thumbnail) storageRef.child(thumbnail)
) )
} }
return ImageCollection(listMap)
} }
suspend fun uploadImages(localImageUris: List<Uri>, storages: Storage): List<String> { suspend fun uploadImages(localImageUris: List<Uri>, storages: Storage): List<String> {
@@ -47,15 +52,43 @@ abstract class DataSubmissionViewModel3<T : Any>(
} }
} }
fun postDataToDatabase(localImageUris: List<Uri>, data: T, storages: Storage) { fun postDataToDatabase(localImageUris: List<Uri>, data: T) {
io { io {
val files = uploadImages(localImageUris, storages) // Validate document fore upload
val postData = setDataAfterUpload(data, files) if (!validateData(data)) return@io
val documentFileNames = data.getImageFileNames()
// Validate at least image selector image or previously uploaded documents photo available
if (localImageUris.isEmpty() && documentFileNames.isNullOrEmpty()) {
onError("Please select an image")
return@io
}
// Upload a new image or keep old image
val fileNames =
localImageUris.takeIf { it.isNotEmpty() }?.let { uploadImages(it, storageType) }
?: documentFileNames
super.postDataToDatabase(postData) fileNames.isNotNull {
data.setImageFileNames(it)
super.postDataToDatabase(data)
return@io
}
onError("Could not upload document")
} }
} }
abstract fun setDataAfterUpload(data: T, filename: List<String>): T override fun setData(data: T) {
val fileNames = data.getImageFileNames()
fileNames?.let { l ->
val list = getImagesAndThumbnails(l).collection
val results = list.map { it.second.downloadUrl }.let { Tasks.whenAllComplete(it) }
results.addOnSuccessListener {
onSuccess(list)
}
}
super.setData(data)
}
override fun postDataToDatabase(data: T) {}
} }

View File

@@ -0,0 +1,73 @@
package h_mal.appttude.com.driver.base
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.EditText
import androidx.core.widget.doAfterTextChanged
import androidx.viewbinding.ViewBinding
import h_mal.appttude.com.driver.data.UserAuthState
import h_mal.appttude.com.driver.ui.user.LoginActivity
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
import h_mal.appttude.com.driver.utils.TextValidationUtils.validateEditText
import kotlin.reflect.full.createInstance
abstract class FormSubmissionFragment<V : DataSubmissionViewModel<T>, VB : ViewBinding, T : Document> :
BaseFragment<V, VB>() {
var model: T = getGenericClassAt<T>(2).createInstance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.init()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// If user is logged out then navigate to LoginActivity
viewModel.stateLiveData.observe(viewLifecycleOwner) {
if (it is UserAuthState.LoggedOut) {
val intent = Intent(requireContext(), LoginActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
requireActivity().finish()
return@observe
}
}
}
@Suppress("UNCHECKED_CAST")
override fun onSuccess(data: Any?) {
super.onSuccess(data)
when (data) {
is Model -> {
model = data as T
setFields(data)
}
}
}
open fun setFields(data: T) { }
open fun submit() {}
fun validateEditTexts(vararg editTexts: EditText): Boolean {
editTexts.forEach {
if (it.text.isNullOrBlank()) {
it.validateEditText()
return false
}
}
return true
}
fun EditText.setTextOnChange(output: (m: String) -> Unit) {
doAfterTextChanged {
output(text.toString())
}
}
open fun submitDocument() {
viewModel.postDataToDatabase(model)
}
}

View File

@@ -0,0 +1,96 @@
package h_mal.appttude.com.driver.base
import android.Manifest
import android.net.Uri
import androidx.activity.result.contract.ActivityResultContracts
import androidx.viewbinding.ViewBinding
import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.data.ImageDocumentFile
import h_mal.appttude.com.driver.data.ImageResults
import h_mal.appttude.com.driver.ui.permission.PermissionsDeclarationDialog
import permissions.dispatcher.NeedsPermission
import permissions.dispatcher.OnNeverAskAgain
import permissions.dispatcher.OnPermissionDenied
import permissions.dispatcher.OnShowRationale
import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions
import java.util.concurrent.atomic.AtomicReference
@RuntimePermissions
abstract class ImageFormSubmissionFragment<V : DataSubmissionViewModel2<T>, VB : ViewBinding, T : ImageDocument> :
FormSubmissionFragment<V, VB, T>(), ImageSelectionHelper {
private val selectedImage: AtomicReference<Uri> = AtomicReference()
private fun setSelectedImages(image: Uri) {
selectedImage.set(image)
}
fun getSelectedImages(): Uri? {
return selectedImage.get()
}
override fun onSuccess(data: Any?) {
super.onSuccess(data)
if (data is ImageDocumentFile<*>) {
setImage(data.image.image, data.image.thumbnail)
setFields(data.document as T)
}
}
open fun setImage(image: StorageReference?, thumbnail: StorageReference?) {}
private val permissionRequest =
registerForActivityResult(ActivityResultContracts.GetContent()) { result ->
result?.let { onImageGalleryResult(result) }
}
override fun openGalleryForImageSelection() {
permissionRequest.launch("image/*")
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// NOTE: delegate the permission handling to generated method
onRequestPermissionsResult(requestCode, grantResults)
}
@NeedsPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
fun showStorage() {
openGalleryForImageSelection()
}
@OnShowRationale(Manifest.permission.READ_EXTERNAL_STORAGE)
fun showRationaleForStorage(request: PermissionRequest) {
PermissionsDeclarationDialog(requireContext()).showDialog({
request.proceed()
}, {
request.cancel()
})
}
@OnPermissionDenied(Manifest.permission.READ_EXTERNAL_STORAGE)
fun onStorageDenied() {
showToast("Storage permissions have been denied")
}
@OnNeverAskAgain(Manifest.permission.READ_EXTERNAL_STORAGE)
fun onStorageNeverAskAgain() {
showToast("Storage permissions have been to never ask again")
}
/**
* Called on the result of image selection
*/
open fun onImageGalleryResult(imageUri: Uri) {
setSelectedImages(imageUri)
}
override fun submitDocument() {
viewModel.postDataToDatabase(getSelectedImages(), model)
}
}

View File

@@ -0,0 +1,27 @@
package h_mal.appttude.com.driver.base
import android.content.ClipData
import android.content.Intent
import android.net.Uri
interface ImageSelectionHelper {
fun openGalleryForImageSelection()
fun imageSelectorIntent(multiImage: Boolean = false) = Intent(Intent.ACTION_GET_CONTENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiImage)
.setType("image/*")
fun Intent?.parseMultiImageIntent(): List<Uri> {
this?.clipData?.takeIf { it.itemCount > 1 }?.convertToList()?.let { clip ->
val list = clip.takeIf { it.size > 10 }?.let {
clip.subList(0, 9)
} ?: clip
return list
}
return listOfNotNull(this?.data)
}
private fun ClipData.convertToList(): List<Uri> = 0.rangeTo(itemCount).map { getItemAt(it).uri }
}

View File

@@ -16,14 +16,11 @@ import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions import permissions.dispatcher.RuntimePermissions
@RuntimePermissions @RuntimePermissions
open class ImageSelectorFragment<V : BaseViewModel, VB : ViewBinding> : BaseFragment<V, VB>() { open class ImageSelectorFragment<V : BaseViewModel, VB : ViewBinding>(
private var multipleImage: Boolean = false private val multipleImage: Boolean = false
) : BaseFragment<V, VB>() {
var picUri: Uri? = null var picUri: Uri? = null
fun setImageSelectionAsMultiple() {
multipleImage = true
}
fun openGalleryForImage() { fun openGalleryForImage() {
permissionRequest.launch(multipleImage) permissionRequest.launch(multipleImage)
} }

View File

@@ -0,0 +1,14 @@
package h_mal.appttude.com.driver.base
interface Model
interface Document : Model
interface ImageDocument : Document {
fun getImageFileName(): String?
fun setImageFileName(fileName: String)
}
interface MultiImageDocument : Document {
fun getImageFileNames(): List<String>?
fun setImageFileNames(images: List<String>)
}

View File

@@ -0,0 +1,94 @@
package h_mal.appttude.com.driver.base
import android.Manifest
import android.net.Uri
import androidx.activity.result.contract.ActivityResultContracts
import androidx.viewbinding.ViewBinding
import h_mal.appttude.com.driver.data.ImageCollection
import h_mal.appttude.com.driver.ui.permission.PermissionsDeclarationDialog
import permissions.dispatcher.NeedsPermission
import permissions.dispatcher.OnNeverAskAgain
import permissions.dispatcher.OnPermissionDenied
import permissions.dispatcher.OnShowRationale
import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions
@RuntimePermissions
abstract class MultiImageFormSubmissionFragment3<V : DataSubmissionViewModel3<T>, VB : ViewBinding, T : MultiImageDocument> :
FormSubmissionFragment<V, VB, T>(), ImageSelectionHelper {
private val selectedImages: MutableList<Uri> = mutableListOf()
private fun setSelectedImages(images: List<Uri>) {
selectedImages.clear()
selectedImages.addAll(images)
}
fun getSelectedImages(): List<Uri> {
return selectedImages
}
override fun onSuccess(data: Any?) {
super.onSuccess(data)
if (data is ImageCollection) {
setImages(data)
}
}
open fun setImages(collection: ImageCollection) {}
private val permissionRequest =
registerForActivityResult(ActivityResultContracts.GetMultipleContents()) { result ->
result?.let { onImageGalleryResult(result) }
}
override fun openGalleryForImageSelection() {
permissionRequest.launch("image/*")
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// NOTE: delegate the permission handling to generated method
onRequestPermissionsResult(requestCode, grantResults)
}
@NeedsPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
fun showStorage() {
openGalleryForImageSelection()
}
@OnShowRationale(Manifest.permission.READ_EXTERNAL_STORAGE)
fun showRationaleForStorage(request: PermissionRequest) {
PermissionsDeclarationDialog(requireContext()).showDialog({
request.proceed()
}, {
request.cancel()
})
}
@OnPermissionDenied(Manifest.permission.READ_EXTERNAL_STORAGE)
fun onStorageDenied() {
showToast("Storage permissions have been denied")
}
@OnNeverAskAgain(Manifest.permission.READ_EXTERNAL_STORAGE)
fun onStorageNeverAskAgain() {
showToast("Storage permissions have been to never ask again")
}
/**
* Called on the result of image selection
*/
open fun onImageGalleryResult(imageUris: List<Uri>) {
setSelectedImages(imageUris)
}
override fun submitDocument() {
viewModel.postDataToDatabase(getSelectedImages(), model)
}
}

View File

@@ -0,0 +1,37 @@
package h_mal.appttude.com.driver.data
import androidx.lifecycle.LiveData
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.ValueEventListener
/**
* Creates #LiveDate out of {UserAuthState} for firebase user state
*/
class FirebaseDataLiveData<T : Any>(
private val reference: DatabaseReference,
private val cls: Class<T>
) : LiveData<DataState>() {
override fun onActive() {
super.onActive()
reference.addValueEventListener(stateListener)
}
override fun onInactive() {
super.onInactive()
reference.addValueEventListener(stateListener)
}
private val stateListener = object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val data = snapshot.getValue(cls)
postValue(DataState.HasData(data ?: FirebaseCompletion.Default))
}
override fun onCancelled(error: DatabaseError) {
postValue(DataState.HasError(error))
}
}
}

View File

@@ -2,6 +2,7 @@ package h_mal.appttude.com.driver.data
import android.net.Uri import android.net.Uri
import com.google.firebase.storage.FirebaseStorage import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageMetadata
import com.google.firebase.storage.StorageReference import com.google.firebase.storage.StorageReference
import kotlinx.coroutines.tasks.await import kotlinx.coroutines.tasks.await
@@ -27,7 +28,7 @@ class FirebaseStorageSource {
suspend fun uploadImageReturnName(localFilePath: Uri, path: StorageReference, filename: String): String { suspend fun uploadImageReturnName(localFilePath: Uri, path: StorageReference, filename: String): String {
val ref = path.child("$filename.jpeg") val ref = path.child("$filename.jpeg")
return ref.putFile(localFilePath) return ref.putFile(localFilePath, StorageMetadata.Builder().setContentType("image/jpeg").build())
.continueWith { ref.name } .continueWith { ref.name }
.await() .await()
} }

View File

@@ -0,0 +1,8 @@
package h_mal.appttude.com.driver.data
import h_mal.appttude.com.driver.base.ImageDocument
data class ImageDocumentFile<T : ImageDocument>(
val document: T,
val image: ImageResults
)

View File

@@ -0,0 +1,12 @@
package h_mal.appttude.com.driver.data
import com.google.firebase.storage.StorageReference
data class ImageResults(
val image: StorageReference?,
val thumbnail: StorageReference?
)
data class ImageCollection(
val collection: List<Pair<StorageReference, StorageReference>>
)

View File

@@ -0,0 +1,14 @@
package h_mal.appttude.com.driver.data
import com.google.firebase.database.DataSnapshot
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
class SnapshotResults<T: Any>(
private val snapshot: DataSnapshot
) {
private val cls = this.getGenericClassAt<T>(0).getGenericClassAt<T>(0).java
fun getData(): T? {
return snapshot.getValue(cls)
}
}

View File

@@ -1,5 +1,11 @@
package h_mal.appttude.com.driver.model package h_mal.appttude.com.driver.model
import com.google.firebase.database.Exclude
import com.google.firebase.database.IgnoreExtraProperties
import h_mal.appttude.com.driver.base.Document
import h_mal.appttude.com.driver.base.ImageDocument
@IgnoreExtraProperties
data class DriverProfile( data class DriverProfile(
var driverPic: String? = null, var driverPic: String? = null,
var forenames: String? = null, var forenames: String? = null,
@@ -8,4 +14,14 @@ data class DriverProfile(
var dob: String? = null, var dob: String? = null,
var ni: String? = null, var ni: String? = null,
var dateFirst: String? = null var dateFirst: String? = null
) : Model ) : ImageDocument {
@Exclude
override fun getImageFileName(): String? {
return driverPic
}
@Exclude
override fun setImageFileName(fileName: String) {
driverPic = fileName
}
}

View File

@@ -1,8 +1,22 @@
package h_mal.appttude.com.driver.model package h_mal.appttude.com.driver.model
import com.google.firebase.database.Exclude
import com.google.firebase.database.IgnoreExtraProperties
import h_mal.appttude.com.driver.base.ImageDocument
@IgnoreExtraProperties
data class DriversLicense( data class DriversLicense(
var licenseImageString: String? = null, var licenseImageString: String? = null,
var licenseNumber: String? = null, var licenseNumber: String? = null,
var licenseExpiry: String? = null var licenseExpiry: String? = null
) : Model ) : ImageDocument {
@Exclude
override fun getImageFileName(): String? {
return licenseImageString
}
@Exclude
override fun setImageFileName(fileName: String) {
licenseImageString = fileName
}
}

View File

@@ -1,7 +1,31 @@
package h_mal.appttude.com.driver.model package h_mal.appttude.com.driver.model
import com.google.firebase.database.Exclude
import com.google.firebase.database.IgnoreExtraProperties
import h_mal.appttude.com.driver.base.MultiImageDocument
@IgnoreExtraProperties
data class Insurance( data class Insurance(
var photoStrings: MutableList<String>? = null, var photoStrings: MutableList<String>? = null,
var insurerName: String? = null, var insurerName: String? = null,
var expiryDate: String? = null var expiryDate: String? = null
) : Model ) : MultiImageDocument {
@Exclude
override fun getImageFileNames(): List<String>? {
return photoStrings
}
@Exclude
override fun setImageFileNames(images: List<String>) {
// update existing array
photoStrings?.run{
clear()
addAll(images)
return
}
// set new array of images
photoStrings = mutableListOf<String>().apply {
addAll(images)
}
}
}

View File

@@ -1,7 +1,20 @@
package h_mal.appttude.com.driver.model package h_mal.appttude.com.driver.model
import com.google.firebase.database.Exclude
import h_mal.appttude.com.driver.base.Document
import h_mal.appttude.com.driver.base.ImageDocument
data class Logbook( data class Logbook(
var photoString: String? = null, var photoString: String? = null,
var v5cnumber: String? = null var v5cnumber: String? = null
) : Model ) : ImageDocument{
@Exclude
override fun getImageFileName(): String? {
return photoString
}
@Exclude
override fun setImageFileName(fileName: String) {
photoString = fileName
}
}

View File

@@ -1,3 +0,0 @@
package h_mal.appttude.com.driver.model
interface Model

View File

@@ -1,7 +1,21 @@
package h_mal.appttude.com.driver.model package h_mal.appttude.com.driver.model
import com.google.firebase.database.Exclude
import com.google.firebase.database.IgnoreExtraProperties
import h_mal.appttude.com.driver.base.ImageDocument
@IgnoreExtraProperties
data class Mot( data class Mot(
var motImageString: String? = null, var motImageString: String? = null,
var motExpiry: String? = null var motExpiry: String? = null
) : Model ) : ImageDocument {
@Exclude
override fun getImageFileName(): String? {
return motImageString
}
@Exclude
override fun setImageFileName(fileName: String) {
motImageString = fileName
}
}

View File

@@ -1,8 +1,23 @@
package h_mal.appttude.com.driver.model package h_mal.appttude.com.driver.model
import com.google.firebase.database.Exclude
import com.google.firebase.database.IgnoreExtraProperties
import h_mal.appttude.com.driver.base.ImageDocument
@IgnoreExtraProperties
data class PrivateHireLicense( data class PrivateHireLicense(
var phImageString: String? = null, var phImageString: String? = null,
var phNumber: String? = null, var phNumber: String? = null,
var phExpiry: String? = null var phExpiry: String? = null
) : Model ) : ImageDocument {
@Exclude
override fun getImageFileName(): String? {
return phImageString
}
@Exclude
override fun setImageFileName(fileName: String) {
phImageString = fileName
}
}

View File

@@ -1,8 +1,23 @@
package h_mal.appttude.com.driver.model package h_mal.appttude.com.driver.model
import com.google.firebase.database.Exclude
import com.google.firebase.database.IgnoreExtraProperties
import h_mal.appttude.com.driver.base.ImageDocument
@IgnoreExtraProperties
data class PrivateHireVehicle( data class PrivateHireVehicle(
var phCarImageString: String? = null, var phCarImageString: String? = null,
var phCarNumber: String? = null, var phCarNumber: String? = null,
var phCarExpiry: String? = null var phCarExpiry: String? = null
) : Model ) : ImageDocument {
@Exclude
override fun getImageFileName(): String? {
return phCarImageString
}
@Exclude
override fun setImageFileName(fileName: String) {
phCarImageString = fileName
}
}

View File

@@ -1,6 +1,9 @@
package h_mal.appttude.com.driver.model package h_mal.appttude.com.driver.model
import com.google.firebase.database.IgnoreExtraProperties
import h_mal.appttude.com.driver.base.Document
@IgnoreExtraProperties
data class VehicleProfile( data class VehicleProfile(
var reg: String? = null, var reg: String? = null,
var make: String? = null, var make: String? = null,
@@ -11,4 +14,4 @@ data class VehicleProfile(
var keeperPostCode: String? = null, var keeperPostCode: String? = null,
var startDate: String? = null, var startDate: String? = null,
var isSeized: Boolean = false var isSeized: Boolean = false
) : Model ) : Document

View File

@@ -76,7 +76,7 @@ suspend fun <T : Any> DatabaseReference.getDataFromDatabaseRef(clazz: Class<T>):
fun <T : Any> DatabaseReference.toLiveData(): LiveData<DataState> { fun <T : Any> DatabaseReference.toLiveData(): LiveData<DataState> {
return object : LiveData<DataState>() { return object : LiveData<DataState>() {
private val listener = addValueEventListener(object : ValueEventListener{ private val listener = object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) { override fun onDataChange(snapshot: DataSnapshot) {
val data = snapshot.getValue(object : GenericTypeIndicator<T>() {}) val data = snapshot.getValue(object : GenericTypeIndicator<T>() {})
postValue(DataState.HasData(data ?: FirebaseCompletion.Default)) postValue(DataState.HasData(data ?: FirebaseCompletion.Default))
@@ -84,7 +84,7 @@ fun <T : Any> DatabaseReference.toLiveData(): LiveData<DataState> {
override fun onCancelled(error: DatabaseError) { override fun onCancelled(error: DatabaseError) {
postValue(DataState.HasError(error)) postValue(DataState.HasError(error))
} }
}) }
override fun onActive() { override fun onActive() {
super.onActive() super.onActive()
// add listener // add listener

View File

@@ -12,7 +12,7 @@ object GenericsHelper {
((javaClass.genericSuperclass as? ParameterizedType) ((javaClass.genericSuperclass as? ParameterizedType)
?.actualTypeArguments?.getOrNull(position) as? Class<CLASS>) ?.actualTypeArguments?.getOrNull(position) as? Class<CLASS>)
?.kotlin ?.kotlin
?: throw IllegalStateException("Can not find class from generic argument") ?: throw IllegalStateException("Can not find class from generic argument ${this}")
/** /**
* Create a view binding out of the the generic [VB] * Create a view binding out of the the generic [VB]

View File

@@ -4,7 +4,7 @@
"port": 9099 "port": 9099
}, },
"database": { "database": {
"port": 9000 "port": 9001
}, },
"storage": { "storage": {
"port": 9199 "port": 9199
@@ -12,12 +12,27 @@
"ui": { "ui": {
"enabled": true "enabled": true
}, },
"singleProjectMode": true "singleProjectMode": true,
"functions": {
"port": 5001
}
}, },
"database": { "database": {
"rules": "database.rules.json" "rules": "database.rules.json"
}, },
"storage": { "storage": {
"rules": "storage.rules" "rules": "storage.rules"
} },
"functions": [
{
"source": "functions",
"codebase": "default",
"ignore": [
"venv",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
]
}
]
} }