From 32def043f9a5e1b8e60d82e14016748ea3bb3c60 Mon Sep 17 00:00:00 2001 From: "h.malik144@gmail.com" Date: Thu, 19 Oct 2023 21:39:59 +0100 Subject: [PATCH] - retrieve thumbnail or no image - changes to storage ref in process retrieval in process ** broken commit ** --- .../androidTest/assets/drivers_license.json | 2 +- .../h_mal/appttude/com/driver/BaseUiTest.kt | 8 +- .../h_mal/appttude/com/driver/FirebaseTest.kt | 5 +- .../h_mal/appttude/com/driver/FormRobot.kt | 2 +- .../appttude/com/driver/robots/HomeRobot.kt | 2 +- .../tests/newUser/DataSubmissionTest.kt | 2 +- .../driver/tests/newUser/DriverProfileTest.kt | 1 + .../tests/newUser/SubmitNewDriverDataTest.kt | 2 + .../ApplicationViewModelFactory.kt | 3 +- .../ui/driverprofile/DriverLicenseFragment.kt | 29 +++--- .../ui/driverprofile/DriverProfileFragment.kt | 23 ++--- .../PrivateHireLicenseFragment.kt | 19 ++-- .../ui/vehicleprofile/InsuranceFragment.kt | 31 ++---- .../ui/vehicleprofile/LogbookFragment.kt | 21 ++-- .../driver/ui/vehicleprofile/MotFragment.kt | 20 ++-- .../PrivateHireVehicleFragment.kt | 25 ++--- .../vehicleprofile/VehicleProfileFragment.kt | 7 +- .../viewmodels/DriverLicenseViewModel.kt | 26 +++-- .../viewmodels/DriverLicenseViewModel2.kt | 40 -------- .../viewmodels/DriverProfileViewModel.kt | 24 +++-- .../driver/viewmodels/InsuranceViewModel.kt | 35 +++---- .../com/driver/viewmodels/LogbookViewModel.kt | 23 ++--- .../com/driver/viewmodels/MotViewModel.kt | 21 ++-- .../viewmodels/PrivateHireLicenseViewModel.kt | 26 +++-- .../viewmodels/PrivateHireVehicleViewModel.kt | 33 +++---- .../viewmodels/VehicleProfileViewModel.kt | 25 ++--- .../viewmodels/VehicleProfileViewModel2.kt | 46 +++++++++ .../appttude/com/driver/base/BaseFragment.kt | 2 +- .../driver/base/DataSubmissionBaseFragment.kt | 3 - .../base/DataSubmissionBaseViewModel.kt | 11 ++- .../driver/base/DataSubmissionViewModel.kt | 41 ++++++-- .../driver/base/DataSubmissionViewModel2.kt | 54 +++++++++-- .../driver/base/DataSubmissionViewModel3.kt | 51 ++++++++-- .../com/driver/base/FormSubmissionFragment.kt | 73 ++++++++++++++ .../base/ImageFormSubmissionFragment.kt | 96 +++++++++++++++++++ .../com/driver/base/ImageSelectionHelper.kt | 27 ++++++ .../com/driver/base/ImageSelectorFragment.kt | 9 +- .../h_mal/appttude/com/driver/base/Model.kt | 14 +++ .../base/MultiImageFormSubmissionFragment3.kt | 94 ++++++++++++++++++ .../com/driver/data/FirebaseDataLiveData.kt | 37 +++++++ .../com/driver/data/FirebaseStorageSource.kt | 3 +- .../appttude/com/driver/data/ImageDocument.kt | 8 ++ .../appttude/com/driver/data/ImageResults.kt | 12 +++ .../com/driver/data/SnapshotResults.kt | 14 +++ .../com/driver/model/DriverProfile.kt | 18 +++- .../com/driver/model/DriversLicense.kt | 16 +++- .../appttude/com/driver/model/Insurance.kt | 26 ++++- .../appttude/com/driver/model/Logbook.kt | 15 ++- .../h_mal/appttude/com/driver/model/Model.kt | 3 - .../h_mal/appttude/com/driver/model/Mot.kt | 16 +++- .../com/driver/model/PrivateHireLicense.kt | 17 +++- .../com/driver/model/PrivateHireVehicle.kt | 17 +++- .../com/driver/model/VehicleProfile.kt | 5 +- .../com/driver/utils/FirebaseUtils.kt | 4 +- .../com/driver/utils/GenericsHelper.kt | 2 +- firebase.json | 21 +++- 56 files changed, 896 insertions(+), 314 deletions(-) delete mode 100644 app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverLicenseViewModel2.kt create mode 100644 app/src/driver/java/h_mal/appttude/com/driver/viewmodels/VehicleProfileViewModel2.kt create mode 100644 app/src/main/java/h_mal/appttude/com/driver/base/FormSubmissionFragment.kt create mode 100644 app/src/main/java/h_mal/appttude/com/driver/base/ImageFormSubmissionFragment.kt create mode 100644 app/src/main/java/h_mal/appttude/com/driver/base/ImageSelectionHelper.kt create mode 100644 app/src/main/java/h_mal/appttude/com/driver/base/Model.kt create mode 100644 app/src/main/java/h_mal/appttude/com/driver/base/MultiImageFormSubmissionFragment3.kt create mode 100644 app/src/main/java/h_mal/appttude/com/driver/data/FirebaseDataLiveData.kt create mode 100644 app/src/main/java/h_mal/appttude/com/driver/data/ImageDocument.kt create mode 100644 app/src/main/java/h_mal/appttude/com/driver/data/ImageResults.kt create mode 100644 app/src/main/java/h_mal/appttude/com/driver/data/SnapshotResults.kt delete mode 100644 app/src/main/java/h_mal/appttude/com/driver/model/Model.kt diff --git a/app/src/androidTest/assets/drivers_license.json b/app/src/androidTest/assets/drivers_license.json index 2dd2ec9..10bb8f6 100644 --- a/app/src/androidTest/assets/drivers_license.json +++ b/app/src/androidTest/assets/drivers_license.json @@ -1,5 +1,5 @@ { - "licenseExpiry": "27/04/2019", + "licenseExpiry": "27/04/2032", "licenseImageString": "driver_license_driver.jpg", "licenseNumber": "FARME100165AB5EW" } \ No newline at end of file diff --git a/app/src/androidTest/java/h_mal/appttude/com/driver/BaseUiTest.kt b/app/src/androidTest/java/h_mal/appttude/com/driver/BaseUiTest.kt index 1dd9acc..55d3285 100644 --- a/app/src/androidTest/java/h_mal/appttude/com/driver/BaseUiTest.kt +++ b/app/src/androidTest/java/h_mal/appttude/com/driver/BaseUiTest.kt @@ -16,12 +16,14 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.internal.util.Checks import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import androidx.test.rule.GrantPermissionRule import com.google.gson.Gson import h_mal.appttude.com.driver.base.BaseActivity import h_mal.appttude.com.driver.helpers.BaseViewAction import h_mal.appttude.com.driver.helpers.SnapshotRule +import kotlinx.coroutines.runBlocking import org.hamcrest.CoreMatchers import org.hamcrest.Matcher import org.hamcrest.core.AllOf @@ -60,8 +62,10 @@ open class BaseUiTest>( beforeLaunch() mActivityScenarioRule = ActivityScenario.launch(activity) mActivityScenarioRule.onActivity { - mIdlingResource = it.getIdlingResource()!! - IdlingRegistry.getInstance().register(mIdlingResource) + runBlocking { + mIdlingResource = it.getIdlingResource()!! + IdlingRegistry.getInstance().register(mIdlingResource) + } } afterLaunch() } diff --git a/app/src/androidTest/java/h_mal/appttude/com/driver/FirebaseTest.kt b/app/src/androidTest/java/h_mal/appttude/com/driver/FirebaseTest.kt index 6c93287..086275a 100644 --- a/app/src/androidTest/java/h_mal/appttude/com/driver/FirebaseTest.kt +++ b/app/src/androidTest/java/h_mal/appttude/com/driver/FirebaseTest.kt @@ -35,12 +35,15 @@ open class FirebaseTest>( val localHost = "10.0.2.2" FirebaseAuth.getInstance().useEmulator(localHost, 9099) - FirebaseDatabase.getInstance().useEmulator(localHost, 9000) + FirebaseDatabase.getInstance().useEmulator(localHost, 9001) FirebaseStorage.getInstance().useEmulator(localHost, 9199) } } override fun beforeLaunch() { +// // sign out if it failed to sign out +// if (firebaseAuthSource.getUser() != null) firebaseAuthSource.logOut() + if (registered) { runBlocking { setupUser() diff --git a/app/src/androidTest/java/h_mal/appttude/com/driver/FormRobot.kt b/app/src/androidTest/java/h_mal/appttude/com/driver/FormRobot.kt index 4cd967a..0014e82 100644 --- a/app/src/androidTest/java/h_mal/appttude/com/driver/FormRobot.kt +++ b/app/src/androidTest/java/h_mal/appttude/com/driver/FormRobot.kt @@ -7,7 +7,7 @@ import androidx.test.espresso.action.ViewActions.scrollTo import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom import androidx.test.espresso.matcher.ViewMatchers.withId 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 java.time.LocalDate import java.time.format.DateTimeFormatter diff --git a/app/src/androidTestDriver/java/h_mal/appttude/com/driver/robots/HomeRobot.kt b/app/src/androidTestDriver/java/h_mal/appttude/com/driver/robots/HomeRobot.kt index 7e4fe9f..a78e9cf 100644 --- a/app/src/androidTestDriver/java/h_mal/appttude/com/driver/robots/HomeRobot.kt +++ b/app/src/androidTestDriver/java/h_mal/appttude/com/driver/robots/HomeRobot.kt @@ -21,7 +21,7 @@ class HomeRobot : BaseTestRobot() { fun updateProfile() { openDrawer() - clickButton(R.id.nav_user_settings) + clickButton(R.id.nav_update_profile) } fun openDriverProfile() = clickButton(R.id.driver) diff --git a/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/DataSubmissionTest.kt b/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/DataSubmissionTest.kt index d42ec68..8ec9e21 100644 --- a/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/DataSubmissionTest.kt +++ b/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/DataSubmissionTest.kt @@ -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.Insurance 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.PrivateHireLicense import h_mal.appttude.com.driver.model.PrivateHireVehicle diff --git a/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/DriverProfileTest.kt b/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/DriverProfileTest.kt index 5ff1eb8..3772ef6 100644 --- a/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/DriverProfileTest.kt +++ b/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/DriverProfileTest.kt @@ -7,6 +7,7 @@ open class DriverProfileTest : DataSubmissionTest() { override fun afterLaunch() { super.afterLaunch() home { + waitFor(1500) openDriverProfile() } } diff --git a/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/SubmitNewDriverDataTest.kt b/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/SubmitNewDriverDataTest.kt index bde80c5..35c7f88 100644 --- a/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/SubmitNewDriverDataTest.kt +++ b/app/src/androidTestDriver/java/h_mal/appttude/com/driver/tests/newUser/SubmitNewDriverDataTest.kt @@ -26,6 +26,8 @@ class SubmitNewDriverDataTest : DriverProfileTest() { driversLicense { val data = getAssetData() submitAndValidate(data) + + waitFor(17000) } } diff --git a/app/src/driver/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt b/app/src/driver/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt index 53458fb..01ab752 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt @@ -45,8 +45,7 @@ class ApplicationViewModelFactory( ) isAssignableFrom(VehicleProfileViewModel::class.java) -> VehicleProfileViewModel( auth, - database, - storage + database ) isAssignableFrom(InsuranceViewModel::class.java) -> InsuranceViewModel( auth, diff --git a/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/DriverLicenseFragment.kt b/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/DriverLicenseFragment.kt index eddbc2c..cf16867 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/DriverLicenseFragment.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/DriverLicenseFragment.kt @@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.driverprofile import android.net.Uri 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.dialogs.DateDialog 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 class DriverLicenseFragment : - DataSubmissionBaseFragment() { + ImageFormSubmissionFragment() { override fun setupView(binding: FragmentDriverLicenseBinding) { binding.apply { @@ -25,32 +25,31 @@ class DriverLicenseFragment : } licNo.setTextOnChange { model.licenseNumber = it } - searchImage.setOnClickListener { openGalleryWithPermissionRequest() } + searchImage.setOnClickListener { openGalleryForImageSelection() } submit.setOnClickListener { - validateEditTexts(licExpiry, licNo).isTrue { - viewModel.setDataInDatabase(model, picUri) - } + validateEditTexts(licExpiry, licNo).isTrue { submitDocument() } } } } override fun setFields(data: DriversLicense) { - super.setFields(data) applyBinding { licNo.setText(data.licenseNumber) licExpiry.setText(data.licenseExpiry) - - data.licenseImageString?.setImages{ - driversliImg.setGlideImage(it.second) - } } } - override fun onImageGalleryResult(imageUri: Uri?) { - super.onImageGalleryResult(imageUri) - applyBinding { - driversliImg.setGlideImage(imageUri) + override fun setImage(image: StorageReference?, thumbnail: StorageReference?) { + thumbnail?.let { + binding.driversliImg.setGlideImage(it) + return } + binding.driversliImg.setGlideImage(image) + } + + override fun onImageGalleryResult(imageUri: Uri) { + super.onImageGalleryResult(imageUri) + binding.driversliImg.setGlideImage(imageUri) } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/DriverProfileFragment.kt b/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/DriverProfileFragment.kt index e23537d..77f5d16 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/DriverProfileFragment.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/DriverProfileFragment.kt @@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.driverprofile import android.net.Uri 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.dialogs.DateDialog import h_mal.appttude.com.driver.model.DriverProfile @@ -12,7 +12,7 @@ import h_mal.appttude.com.driver.viewmodels.DriverProfileViewModel class DriverProfileFragment : - DataSubmissionBaseFragment() { + ImageFormSubmissionFragment() { override fun setupView(binding: FragmentDriverProfileBinding) = binding.run { namesInput.setTextOnChange { model.forenames = it } @@ -35,7 +35,7 @@ class DriverProfileFragment : } } } - addPhoto.setOnClickListener { openGalleryWithPermissionRequest() } + addPhoto.setOnClickListener { openGalleryForImageSelection() } submit.setOnClickListener { submit() } } @@ -45,13 +45,12 @@ class DriverProfileFragment : namesInput, addressInput, postcodeInput, dobInput, niNumber, dateFirst ).isTrue { - viewModel.setDataInDatabase(model, picUri) + submitDocument() } } } override fun setFields(data: DriverProfile) { - super.setFields(data) applyBinding { namesInput.setText(data.forenames) addressInput.setText(data.address) @@ -59,18 +58,16 @@ class DriverProfileFragment : dobInput.setText(data.dob) niNumber.setText(data.ni) 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) - applyBinding { - driverPic.setGlideImage(imageUri) - } + binding.driverPic.setGlideImage(imageUri) } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/PrivateHireLicenseFragment.kt b/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/PrivateHireLicenseFragment.kt index 0ef79a5..e55e993 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/PrivateHireLicenseFragment.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/ui/driverprofile/PrivateHireLicenseFragment.kt @@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.driverprofile import android.net.Uri 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.dialogs.DateDialog 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 -class PrivateHireLicenseFragment : DataSubmissionBaseFragment +class PrivateHireLicenseFragment : ImageFormSubmissionFragment () { override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run { @@ -25,14 +25,14 @@ class PrivateHireLicenseFragment : DataSubmissionBaseFragment } } - uploadphlic.setOnClickListener { openGalleryWithPermissionRequest() } + uploadphlic.setOnClickListener { openGalleryForImageSelection() } submit.setOnClickListener { submit() } } override fun submit() { applyBinding { validateEditTexts(phNo, phExpiry).isTrue { - viewModel.setDataInDatabase(model, picUri) + submitDocument() } } } @@ -42,15 +42,16 @@ class PrivateHireLicenseFragment : DataSubmissionBaseFragment applyBinding { phNo.setText(data.phNumber) 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) - applyBinding { - imageView2.setGlideImage(imageUri) - } + binding.imageView2.setGlideImage(imageUri) } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/InsuranceFragment.kt b/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/InsuranceFragment.kt index 835de63..7ec1e3b 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/InsuranceFragment.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/InsuranceFragment.kt @@ -5,7 +5,8 @@ import android.os.Bundle import android.view.View import android.widget.ImageView 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.dialogs.DateDialog import h_mal.appttude.com.driver.model.Insurance @@ -15,13 +16,10 @@ import h_mal.appttude.com.driver.viewmodels.InsuranceViewModel class InsuranceFragment : - DataSubmissionBaseFragment() { - - private var selectedImages: List? = listOf() + MultiImageFormSubmissionFragment3() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setImageSelectionAsMultiple() applyBinding { insurer.setTextOnChange { model.insurerName = it } @@ -32,7 +30,7 @@ class InsuranceFragment : } } } - uploadInsurance.setOnClickListener { openGalleryWithPermissionRequest() } + uploadInsurance.setOnClickListener { openGalleryForImageSelection() } submit.setOnClickListener { submit() } } } @@ -57,35 +55,24 @@ class InsuranceFragment : super.submit() applyBinding { validateEditTexts(insurer, insuranceExp).isTrue { - viewModel.setDataInDatabase(model, selectedImages) + submitDocument() } } } override fun setFields(data: Insurance) { - super.setFields(data) applyBinding { insurer.setText(model.insurerName) insuranceExp.setText(model.expiryDate) - - data.photoStrings?.also { - val keys = viewModel.getMultipleImagesAndThumbnails(it).map {i -> i.key } - updateImageCarousal(keys) - } } } - override fun onImageGalleryResult(imageUris: List?) { - selectedImages = imageUris - selectedImages?.let { updateImageCarousal(it) } + override fun setImages(collection: ImageCollection) { + updateImageCarousal(collection.collection.map { it.second }) } - override fun onSuccess(data: Any?) { - super.onSuccess(data) - - if (data is Map<*,*>) { - updateImageCarousal(data.map { it.value }) - } + override fun onImageGalleryResult(imageUris: List) { + updateImageCarousal(imageUris) } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/LogbookFragment.kt b/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/LogbookFragment.kt index d5ef4a3..c78528a 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/LogbookFragment.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/LogbookFragment.kt @@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.vehicleprofile import android.net.Uri 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.model.Logbook import h_mal.appttude.com.driver.utils.isTrue @@ -11,11 +11,11 @@ import h_mal.appttude.com.driver.viewmodels.LogbookViewModel class LogbookFragment : - DataSubmissionBaseFragment() { + ImageFormSubmissionFragment() { override fun setupView(binding: FragmentLogbookBinding) = binding.run { v5cNo.setTextOnChange { model.v5cnumber = it } - uploadLb.setOnClickListener { openGalleryWithPermissionRequest() } + uploadLb.setOnClickListener { openGalleryForImageSelection() } submit.setOnClickListener { submit() } } @@ -23,24 +23,23 @@ class LogbookFragment : super.submit() applyBinding { validateEditTexts(v5cNo).isTrue { - viewModel.setDataInDatabase(model, picUri) + submitDocument() } } } override fun setFields(data: Logbook) { - super.setFields(data) applyBinding { 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) - applyBinding { - logBookImg.setGlideImage(picUri) - } + binding.logBookImg.setGlideImage(imageUri) } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/MotFragment.kt b/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/MotFragment.kt index 77cb253..b4f3d8a 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/MotFragment.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/MotFragment.kt @@ -2,7 +2,7 @@ package h_mal.appttude.com.driver.ui.vehicleprofile import android.net.Uri 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.dialogs.DateDialog 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 -class MotFragment : DataSubmissionBaseFragment() { +class MotFragment : ImageFormSubmissionFragment() { override fun setupView(binding: FragmentMotBinding) = binding.run { motExpiry.apply { @@ -22,26 +22,26 @@ class MotFragment : DataSubmissionBaseFragment() { + ImageFormSubmissionFragment() { override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run { phNo.setTextOnChange { model.phCarNumber = it } @@ -24,28 +24,29 @@ class PrivateHireVehicleFragment : } } - uploadphlic.setOnClickListener { openGalleryWithPermissionRequest() } + uploadphlic.setOnClickListener { openGalleryForImageSelection() } submit.setOnClickListener { validateEditTexts(phNo, phExpiry).isTrue { - viewModel.setDataInDatabase(model, picUri) + submitDocument() } } } - override fun setFields(data: PrivateHireVehicle) { - super.setFields(data) + override fun setFields( + data: PrivateHireVehicle + ) { applyBinding { phNo.setText(data.phCarNumber) 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) - applyBinding { - imageView2.setGlideImage(imageUri) - } + binding.imageView2.setGlideImage(imageUri) } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/VehicleProfileFragment.kt b/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/VehicleProfileFragment.kt index 17c91c2..8956647 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/VehicleProfileFragment.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/ui/vehicleprofile/VehicleProfileFragment.kt @@ -1,6 +1,6 @@ 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.dialogs.DateDialog 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 -class VehicleProfileFragment : DataSubmissionBaseFragment -() { +class VehicleProfileFragment : FormSubmissionFragment() { override fun setupView(binding: FragmentVehicleSetupBinding) = binding.run { reg.setTextOnChange { model.reg = it } @@ -39,7 +38,7 @@ class VehicleProfileFragment : DataSubmissionBaseFragment postcode, startDate ).isTrue { - viewModel.setDataInDatabase(model) + submitDocument() } } } diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverLicenseViewModel.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverLicenseViewModel.kt index 143e627..00d4adc 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverLicenseViewModel.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverLicenseViewModel.kt @@ -1,35 +1,33 @@ 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.data.DRIVERS_LICENSE_SREF +import h_mal.appttude.com.driver.base.DataSubmissionViewModel2 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 +import h_mal.appttude.com.driver.utils.DateUtils.parseDateStringIntoCalender +import org.joda.time.LocalDate class DriverLicenseViewModel( auth: FirebaseAuthentication, database: FirebaseDatabaseSource, storage: FirebaseStorageSource -) : DataSubmissionBaseViewModel(auth, database, storage) { +) : DataSubmissionViewModel2(auth, database, storage, Storage.DRIVERS_LICENSE) { override val databaseRef: DatabaseReference = database.getDriverLicenseRef(uid) override val storageRef: StorageReference = storage.driversLicenseStorageRef(uid) - override val objectName: String = "drivers license" - override fun getDataFromDatabase() = retrieveDataFromDatabase() - - override fun setDataInDatabase(data: DriversLicense, localImageUri: Uri?) = io { - doTryOperation("Failed to upload $objectName") { - val imageUrl = getImageUrl(localImageUri, data.licenseImageString) - data.licenseImageString = imageUrl - postDataToDatabase(data) + override fun validateData(data: DriversLicense): Boolean { + data.licenseNumber.validateStringOrThrow("License number") + if (parseDateStringIntoCalender(data.licenseExpiry!!).isBefore(LocalDate.now())) { + onError("License expiry cannot be before today") + return false } + + return true } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverLicenseViewModel2.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverLicenseViewModel2.kt deleted file mode 100644 index 06e665c..0000000 --- a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverLicenseViewModel2.kt +++ /dev/null @@ -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(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) - } - -} \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverProfileViewModel.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverProfileViewModel.kt index c7aed26..3078bc6 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverProfileViewModel.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/DriverProfileViewModel.kt @@ -4,33 +4,39 @@ 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.DRIVER_PROFILE 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.PROFILE_SREF +import h_mal.appttude.com.driver.data.Storage import h_mal.appttude.com.driver.model.DriverProfile 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 DriverProfileViewModel( auth: FirebaseAuthentication, database: FirebaseDatabaseSource, storage: FirebaseStorageSource -) : DataSubmissionBaseViewModel(auth, database, storage) { +) : DataSubmissionViewModel2(auth, database, storage, Storage.PROFILE) { override val databaseRef: DatabaseReference = database.getDriverDetailsRef(uid) override val storageRef: StorageReference = storage.profileImageStorageRef(uid) - override val objectName: String = "drivers profile" - override fun getDataFromDatabase() = retrieveDataFromDatabase() - override fun setDataInDatabase(data: DriverProfile, localImageUri: Uri?) = io { - doTryOperation("Failed to upload $objectName") { - - val imageUrl = getImageUrl(localImageUri, data.driverPic) - data.driverPic = imageUrl - postDataToDatabase(data) + override fun validateData(data: DriverProfile): Boolean { + data.ni.validateStringOrThrow("National Insurance number") + data.address.validateStringOrThrow("Address") + data.postcode.validateStringOrThrow("Postcode") + data.forenames.validateStringOrThrow("Name") + data.dob + if (DateUtils.parseDateStringIntoCalender(data.dob!!).isAfter(LocalDate.now().minusYears(17))) { + onError("Driver cannot be under 17") + return false } + return true } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/InsuranceViewModel.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/InsuranceViewModel.kt index 71055eb..0424312 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/InsuranceViewModel.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/InsuranceViewModel.kt @@ -1,43 +1,32 @@ 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.DataSubmissionViewModel3 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.INSURANCE_SREF +import h_mal.appttude.com.driver.data.Storage import h_mal.appttude.com.driver.model.Insurance -import h_mal.appttude.com.driver.utils.Coroutines.io -import kotlinx.coroutines.Job +import h_mal.appttude.com.driver.utils.DateUtils +import org.joda.time.LocalDate class InsuranceViewModel( auth: FirebaseAuthentication, database: FirebaseDatabaseSource, storage: FirebaseStorageSource -) : DataSubmissionBaseViewModel(auth, database, storage) { +) : DataSubmissionViewModel3(auth, database, storage, Storage.INSURANCE) { override val databaseRef: DatabaseReference = database.getInsuranceDetailsRef(uid) override val storageRef: StorageReference = storage.insuranceStorageRef(uid) - override val objectName: String = "insurance" - override fun getDataFromDatabase() = retrieveDataFromDatabase() - - override fun setDataInDatabase(data: Insurance, localImageUris: List?) = io { - doTryOperation("Failed to upload $objectName") { - val imageUrls = if (!localImageUris.isNullOrEmpty()) { - getImageUrls(localImageUris).toMutableList() - } else { - data.photoStrings - } - if (imageUrls.isNullOrEmpty()) { - onError("no images selected") - return@doTryOperation - } - - data.photoStrings = imageUrls - postDataToDatabase(data) + override fun validateData(data: Insurance): Boolean { + data.insurerName.validateStringOrThrow("Insurer name") + if (DateUtils.parseDateStringIntoCalender(data.expiryDate!!).isBefore(LocalDate.now())) { + onError("Insurance expiry cannot be before today") + return false } + return true } + } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/LogbookViewModel.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/LogbookViewModel.kt index e6921eb..6d552f0 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/LogbookViewModel.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/LogbookViewModel.kt @@ -1,37 +1,26 @@ 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.data.DRIVER_PROFILE +import h_mal.appttude.com.driver.base.DataSubmissionViewModel2 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.LOG_BOOK_SREF -import h_mal.appttude.com.driver.model.DriverProfile +import h_mal.appttude.com.driver.data.Storage import h_mal.appttude.com.driver.model.Logbook -import h_mal.appttude.com.driver.utils.Coroutines.io -import kotlinx.coroutines.Job class LogbookViewModel( auth: FirebaseAuthentication, database: FirebaseDatabaseSource, storage: FirebaseStorageSource -) : DataSubmissionBaseViewModel(auth, database, storage) { +) : DataSubmissionViewModel2(auth, database, storage, Storage.LOG_BOOK) { override val databaseRef: DatabaseReference = database.getLogbookRef(uid) override val storageRef: StorageReference = storage.logBookStorageRef(uid) - override val objectName: String = "Log book" - override fun getDataFromDatabase() = retrieveDataFromDatabase() - - override fun setDataInDatabase(data: Logbook, localImageUri: Uri?) = io { - doTryOperation("Failed to upload $objectName") { - val imageUrl = getImageUrl(localImageUri, data.photoString) - data.photoString = imageUrl - postDataToDatabase(data) - } + override fun validateData(data: Logbook): Boolean { + data.v5cnumber.validateStringOrThrow("V5C number") + return true } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/MotViewModel.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/MotViewModel.kt index 5339610..b2596cc 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/MotViewModel.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/MotViewModel.kt @@ -4,35 +4,36 @@ 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.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseDatabaseSource 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.MOT 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.Mot 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 MotViewModel( auth: FirebaseAuthentication, database: FirebaseDatabaseSource, storage: FirebaseStorageSource -) : DataSubmissionBaseViewModel(auth, database, storage) { +) : DataSubmissionViewModel2(auth, database, storage, Storage.MOT) { override val databaseRef: DatabaseReference = database.getMotDetailsRef(uid) - override val storageRef: StorageReference? = storage.motStorageRef(uid) - override val objectName: String = "vehicle profile" + override val storageRef: StorageReference = storage.motStorageRef(uid) - override fun getDataFromDatabase() = retrieveDataFromDatabase() - - override fun setDataInDatabase(data: Mot, localImageUri: Uri?) = io { - doTryOperation("Failed to upload $objectName") { - val imageUrl = getImageUrl(localImageUri, data.motImageString) - data.motImageString = imageUrl - postDataToDatabase(data) + override fun validateData(data: Mot): Boolean { + if (DateUtils.parseDateStringIntoCalender(data.motExpiry!!).isBefore(LocalDate.now())) { + onError("MOT expiry cannot be before today") + return false } + return true } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/PrivateHireLicenseViewModel.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/PrivateHireLicenseViewModel.kt index 40830bf..12b66be 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/PrivateHireLicenseViewModel.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/PrivateHireLicenseViewModel.kt @@ -4,39 +4,37 @@ 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.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseDatabaseSource import h_mal.appttude.com.driver.data.FirebaseStorageSource 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.Storage import h_mal.appttude.com.driver.model.Mot import h_mal.appttude.com.driver.model.PrivateHireLicense 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 PrivateHireLicenseViewModel( auth: FirebaseAuthentication, database: FirebaseDatabaseSource, storage: FirebaseStorageSource -) : DataSubmissionBaseViewModel(auth, database, storage) { +) : DataSubmissionViewModel2(auth, database, storage, Storage.DRIVERS_LICENSE) { override val databaseRef: DatabaseReference = database.getPrivateHireRef(uid) override val storageRef: StorageReference = storage.privateHireStorageRef(uid) - override val objectName: String = "private hire license" - override fun getDataFromDatabase() = retrieveDataFromDatabase() - - override fun setDataInDatabase(data: PrivateHireLicense, localImageUri: Uri?) = io { - doTryOperation("Failed to upload private hire license") { - val imageUrl = getImageUrl(localImageUri, data.phImageString) - val driverLicense = PrivateHireLicense( - phExpiry = data.phExpiry, - phNumber = data.phNumber, - phImageString = imageUrl - ) - - postDataToDatabase(driverLicense) + override fun validateData(data: PrivateHireLicense): Boolean { + data.phNumber.validateStringOrThrow("License number") + if (DateUtils.parseDateStringIntoCalender(data.phExpiry!!).isBefore(LocalDate.now())) { + onError("License expiry cannot be before today") + return false } + + return true } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/PrivateHireVehicleViewModel.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/PrivateHireVehicleViewModel.kt index 64a8392..1b2afc3 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/PrivateHireVehicleViewModel.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/PrivateHireVehicleViewModel.kt @@ -1,36 +1,37 @@ 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.FirebaseAuthentication import h_mal.appttude.com.driver.data.FirebaseDatabaseSource 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.PRIVATE_HIRE_VEHICLE_SREF -import h_mal.appttude.com.driver.model.PrivateHireLicense +import h_mal.appttude.com.driver.data.Storage import h_mal.appttude.com.driver.model.PrivateHireVehicle -import h_mal.appttude.com.driver.utils.Coroutines.io -import kotlinx.coroutines.Job +import h_mal.appttude.com.driver.utils.DateUtils +import org.joda.time.LocalDate class PrivateHireVehicleViewModel( auth: FirebaseAuthentication, database: FirebaseDatabaseSource, storage: FirebaseStorageSource -) : DataSubmissionBaseViewModel(auth, database, storage) { +) : DataSubmissionViewModel2( + auth, + database, + storage, + Storage.PRIVATE_HIRE_VEHICLE +) { override val databaseRef: DatabaseReference = database.getPrivateHireVehicleRef(uid) override val storageRef: StorageReference = storage.privateHireVehicleStorageRef(uid) - override val objectName: String = "private hire vehicle license" - override fun getDataFromDatabase() = retrieveDataFromDatabase() - - override fun setDataInDatabase(data: PrivateHireVehicle, localImageUri: Uri?) = io { - doTryOperation("Failed to upload $objectName") { - val imageUrl = getImageUrl(localImageUri, data.phCarImageString) - data.phCarImageString = imageUrl - postDataToDatabase(data) + override fun validateData(data: PrivateHireVehicle): Boolean { + data.phCarNumber.validateStringOrThrow("License number") + if (DateUtils.parseDateStringIntoCalender(data.phCarExpiry!!).isBefore(LocalDate.now())) { + onError("License expiry cannot be before today") + return false } + + return true } } \ No newline at end of file diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/VehicleProfileViewModel.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/VehicleProfileViewModel.kt index 143a935..729c278 100644 --- a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/VehicleProfileViewModel.kt +++ b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/VehicleProfileViewModel.kt @@ -3,6 +3,7 @@ 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 @@ -11,22 +12,22 @@ import h_mal.appttude.com.driver.utils.Coroutines.io class VehicleProfileViewModel( auth: FirebaseAuthentication, - database: FirebaseDatabaseSource, - storage: FirebaseStorageSource -) : DataSubmissionBaseViewModel(auth, database, storage) { + database: FirebaseDatabaseSource +) : DataSubmissionViewModel(auth, database) { override val databaseRef: DatabaseReference = database.getVehicleDetailsRef(uid) - override val storageRef: StorageReference? = null - override val objectName: String = "vehicle profile" - override fun getDataFromDatabase() = retrieveDataFromDatabase() + 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 { - doTryOperation("Failed to upload $objectName") { - postDataToDatabase(data) - } - } + + return true } diff --git a/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/VehicleProfileViewModel2.kt b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/VehicleProfileViewModel2.kt new file mode 100644 index 0000000..0527774 --- /dev/null +++ b/app/src/driver/java/h_mal/appttude/com/driver/viewmodels/VehicleProfileViewModel2.kt @@ -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(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 + } + +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/BaseFragment.kt b/app/src/main/java/h_mal/appttude/com/driver/base/BaseFragment.kt index afb8fbe..71da9b8 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/base/BaseFragment.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/base/BaseFragment.kt @@ -18,7 +18,7 @@ import org.kodein.di.generic.instance abstract class BaseFragment : Fragment(), KodeinAware { private var _binding: VB? = null - private val binding: VB + val binding: VB get() = _binding ?: error("Must only access binding while fragment is attached.") var mActivity: BaseActivity? = null diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionBaseFragment.kt b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionBaseFragment.kt index bb92dec..f5190df 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionBaseFragment.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionBaseFragment.kt @@ -1,7 +1,6 @@ package h_mal.appttude.com.driver.base import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.View import android.widget.EditText @@ -9,11 +8,9 @@ import androidx.core.widget.doAfterTextChanged import androidx.viewbinding.ViewBinding import com.google.firebase.storage.StorageReference 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.utils.GenericsHelper.getGenericClassAt import h_mal.appttude.com.driver.utils.TextValidationUtils.validateEditText -import h_mal.appttude.com.driver.utils.setGlideImage import kotlin.reflect.full.createInstance abstract class DataSubmissionBaseFragment, VB : ViewBinding, T : Model> : diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionBaseViewModel.kt b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionBaseViewModel.kt index ff59aa4..015e41d 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionBaseViewModel.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionBaseViewModel.kt @@ -53,8 +53,7 @@ abstract class DataSubmissionBaseViewModel( return localImageUri?.let { uri -> storageRef?.let { - val image = storage?.uploadImage(uri, it, imageString) - image.toString() + storage!!.uploadImageReturnRef(uri, it, imageString) } } } @@ -67,6 +66,14 @@ abstract class DataSubmissionBaseViewModel( 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?): List { if (localImageUris.isNullOrEmpty()) { throw IOException("No images is selected") diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel.kt b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel.kt index 1b5bbc4..f1bd511 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel.kt @@ -2,17 +2,22 @@ package h_mal.appttude.com.driver.base import androidx.lifecycle.LiveData 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.ValueEventListener import h_mal.appttude.com.driver.data.DataState import h_mal.appttude.com.driver.data.FirebaseAuthentication 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.utils.Coroutines.io +import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt import h_mal.appttude.com.driver.utils.toLiveData import java.io.IOException -abstract class DataSubmissionViewModel( +abstract class DataSubmissionViewModel( auth: FirebaseAuthentication, private val database: FirebaseDatabaseSource ) : BaseViewModel() { @@ -21,15 +26,33 @@ abstract class DataSubmissionViewModel( abstract val databaseRef: DatabaseReference - private val refLiveData: LiveData by lazy { databaseRef.toLiveData() } + private val refLiveData: LiveData by lazy { FirebaseDataLiveData(databaseRef, getGenericClassAt(0).java) } + @Suppress("UNCHECKED_CAST") private val observer = Observer { 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) } } - init { + private val valueListener = object : ValueEventListener { + override fun onDataChange(snapshot: DataSnapshot) { + snapshot.getValue(getGenericClassAt(0).javaObjectType) + } + + override fun onCancelled(error: DatabaseError) { + + } + } + + open fun setData(data: T) { + onSuccess(data) + } + + fun init() { +// databaseRef.addValueEventListener() refLiveData.observeForever(observer) } @@ -51,13 +74,19 @@ abstract class DataSubmissionViewModel( open fun postDataToDatabase(data: T) { io { - if (!validateData(data)) return@io - if (getDataFromDatabase() == data) return@io doTryOperation("Failed to submit document") { + if (!validateData(data)) return@doTryOperation + val postObject = database.postToDatabaseRed(databaseRef, data) onSuccess(postObject) } } } + fun String?.validateStringOrThrow(fieldName: String) { + if (isNullOrEmpty()) { + throw IOException("$fieldName cannot be empty") + } + } + } \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel2.kt b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel2.kt index d9c41da..1f617e9 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel2.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel2.kt @@ -1,32 +1,38 @@ package h_mal.appttude.com.driver.base import android.net.Uri +import com.google.android.gms.tasks.Tasks import com.google.firebase.storage.StorageReference 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.ImageDocumentFile +import h_mal.appttude.com.driver.data.ImageResults import h_mal.appttude.com.driver.data.Storage import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.DateUtils +import h_mal.appttude.com.driver.utils.isNotNull +import kotlinx.coroutines.tasks.await -abstract class DataSubmissionViewModel2( +abstract class DataSubmissionViewModel2( auth: FirebaseAuthentication, database: FirebaseDatabaseSource, - private val storage: FirebaseStorageSource + private val storage: FirebaseStorageSource, + private val storageType: Storage ) : DataSubmissionViewModel(auth, database) { abstract val storageRef: StorageReference // retrieve image and thumbnail as a pair - fun getImageAndThumbnail(filename: String): Pair { + fun getImageAndThumbnail(filename: String): ImageResults { val thumbnail = StringBuilder() .append("thumb_") .append(filename.split(".")[0]) .append(".png") .toString() - return Pair( + return ImageResults( storageRef.child(filename), storageRef.child(thumbnail) ) @@ -42,14 +48,44 @@ abstract class DataSubmissionViewModel2( return storage.uploadImageReturnName(localImageUri, storageRef, imageString) } - fun postDataToDatabase(localImageUri: Uri, data: T, storages: Storage) { + fun postDataToDatabase(localImageUri: Uri?, data: T) { io { - val fileName = uploadImage(localImageUri, storages) - val afterData = setDataAfterUpload(data, fileName) + val documentFileName = data.getImageFileName() + // 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) + } + + } } \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel3.kt b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel3.kt index 0afef81..be2fb12 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel3.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/base/DataSubmissionViewModel3.kt @@ -1,26 +1,30 @@ package h_mal.appttude.com.driver.base import android.net.Uri +import com.google.android.gms.tasks.Tasks import com.google.firebase.storage.StorageReference 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.ImageCollection import h_mal.appttude.com.driver.data.Storage import h_mal.appttude.com.driver.utils.Coroutines.io import h_mal.appttude.com.driver.utils.DateUtils +import h_mal.appttude.com.driver.utils.isNotNull import h_mal.appttude.com.driver.utils.mapIndexSuspend -abstract class DataSubmissionViewModel3( +abstract class DataSubmissionViewModel3( auth: FirebaseAuthentication, database: FirebaseDatabaseSource, - private val storage: FirebaseStorageSource + private val storage: FirebaseStorageSource, + private val storageType: Storage ) : DataSubmissionViewModel(auth, database) { abstract val storageRef: StorageReference - fun getImagesAndThumbnails(filenames: List): List> { - return filenames.map { + fun getImagesAndThumbnails(filenames: List): ImageCollection { + val listMap = filenames.map { val thumbnail = StringBuilder() .append("thumb_") .append(it.split(".")[0]) @@ -32,6 +36,7 @@ abstract class DataSubmissionViewModel3( storageRef.child(thumbnail) ) } + return ImageCollection(listMap) } suspend fun uploadImages(localImageUris: List, storages: Storage): List { @@ -47,15 +52,43 @@ abstract class DataSubmissionViewModel3( } } - fun postDataToDatabase(localImageUris: List, data: T, storages: Storage) { + fun postDataToDatabase(localImageUris: List, data: T) { io { - val files = uploadImages(localImageUris, storages) - val postData = setDataAfterUpload(data, files) + // Validate document fore upload + 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): 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) {} } \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/FormSubmissionFragment.kt b/app/src/main/java/h_mal/appttude/com/driver/base/FormSubmissionFragment.kt new file mode 100644 index 0000000..215a442 --- /dev/null +++ b/app/src/main/java/h_mal/appttude/com/driver/base/FormSubmissionFragment.kt @@ -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, VB : ViewBinding, T : Document> : + BaseFragment() { + + var model: T = getGenericClassAt(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) + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/ImageFormSubmissionFragment.kt b/app/src/main/java/h_mal/appttude/com/driver/base/ImageFormSubmissionFragment.kt new file mode 100644 index 0000000..0368878 --- /dev/null +++ b/app/src/main/java/h_mal/appttude/com/driver/base/ImageFormSubmissionFragment.kt @@ -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, VB : ViewBinding, T : ImageDocument> : + FormSubmissionFragment(), ImageSelectionHelper { + + private val selectedImage: AtomicReference = 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, + 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/ImageSelectionHelper.kt b/app/src/main/java/h_mal/appttude/com/driver/base/ImageSelectionHelper.kt new file mode 100644 index 0000000..1ce72cc --- /dev/null +++ b/app/src/main/java/h_mal/appttude/com/driver/base/ImageSelectionHelper.kt @@ -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 { + 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 = 0.rangeTo(itemCount).map { getItemAt(it).uri } + +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/ImageSelectorFragment.kt b/app/src/main/java/h_mal/appttude/com/driver/base/ImageSelectorFragment.kt index a5ee6b0..4776a8b 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/base/ImageSelectorFragment.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/base/ImageSelectorFragment.kt @@ -16,14 +16,11 @@ import permissions.dispatcher.PermissionRequest import permissions.dispatcher.RuntimePermissions @RuntimePermissions -open class ImageSelectorFragment : BaseFragment() { - private var multipleImage: Boolean = false +open class ImageSelectorFragment( + private val multipleImage: Boolean = false +) : BaseFragment() { var picUri: Uri? = null - fun setImageSelectionAsMultiple() { - multipleImage = true - } - fun openGalleryForImage() { permissionRequest.launch(multipleImage) } diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/Model.kt b/app/src/main/java/h_mal/appttude/com/driver/base/Model.kt new file mode 100644 index 0000000..3bc62a1 --- /dev/null +++ b/app/src/main/java/h_mal/appttude/com/driver/base/Model.kt @@ -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? + fun setImageFileNames(images: List) +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/MultiImageFormSubmissionFragment3.kt b/app/src/main/java/h_mal/appttude/com/driver/base/MultiImageFormSubmissionFragment3.kt new file mode 100644 index 0000000..59aa6e7 --- /dev/null +++ b/app/src/main/java/h_mal/appttude/com/driver/base/MultiImageFormSubmissionFragment3.kt @@ -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, VB : ViewBinding, T : MultiImageDocument> : + FormSubmissionFragment(), ImageSelectionHelper { + + private val selectedImages: MutableList = mutableListOf() + + private fun setSelectedImages(images: List) { + selectedImages.clear() + selectedImages.addAll(images) + } + + fun getSelectedImages(): List { + 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, + 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) { + setSelectedImages(imageUris) + } + + override fun submitDocument() { + viewModel.postDataToDatabase(getSelectedImages(), model) + } + +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseDataLiveData.kt b/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseDataLiveData.kt new file mode 100644 index 0000000..db983e4 --- /dev/null +++ b/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseDataLiveData.kt @@ -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( + private val reference: DatabaseReference, + private val cls: Class +) : LiveData() { + + 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)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseStorageSource.kt b/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseStorageSource.kt index b2fa171..6462ac7 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseStorageSource.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseStorageSource.kt @@ -2,6 +2,7 @@ package h_mal.appttude.com.driver.data import android.net.Uri import com.google.firebase.storage.FirebaseStorage +import com.google.firebase.storage.StorageMetadata import com.google.firebase.storage.StorageReference import kotlinx.coroutines.tasks.await @@ -27,7 +28,7 @@ class FirebaseStorageSource { suspend fun uploadImageReturnName(localFilePath: Uri, path: StorageReference, filename: String): String { val ref = path.child("$filename.jpeg") - return ref.putFile(localFilePath) + return ref.putFile(localFilePath, StorageMetadata.Builder().setContentType("image/jpeg").build()) .continueWith { ref.name } .await() } diff --git a/app/src/main/java/h_mal/appttude/com/driver/data/ImageDocument.kt b/app/src/main/java/h_mal/appttude/com/driver/data/ImageDocument.kt new file mode 100644 index 0000000..9b7e98d --- /dev/null +++ b/app/src/main/java/h_mal/appttude/com/driver/data/ImageDocument.kt @@ -0,0 +1,8 @@ +package h_mal.appttude.com.driver.data + +import h_mal.appttude.com.driver.base.ImageDocument + +data class ImageDocumentFile( + val document: T, + val image: ImageResults +) \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/data/ImageResults.kt b/app/src/main/java/h_mal/appttude/com/driver/data/ImageResults.kt new file mode 100644 index 0000000..7459df3 --- /dev/null +++ b/app/src/main/java/h_mal/appttude/com/driver/data/ImageResults.kt @@ -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> +) diff --git a/app/src/main/java/h_mal/appttude/com/driver/data/SnapshotResults.kt b/app/src/main/java/h_mal/appttude/com/driver/data/SnapshotResults.kt new file mode 100644 index 0000000..fd93f2a --- /dev/null +++ b/app/src/main/java/h_mal/appttude/com/driver/data/SnapshotResults.kt @@ -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( + private val snapshot: DataSnapshot +) { + private val cls = this.getGenericClassAt(0).getGenericClassAt(0).java + + fun getData(): T? { + return snapshot.getValue(cls) + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/model/DriverProfile.kt b/app/src/main/java/h_mal/appttude/com/driver/model/DriverProfile.kt index f6f9fc8..abc0a1c 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/model/DriverProfile.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/model/DriverProfile.kt @@ -1,5 +1,11 @@ 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( var driverPic: String? = null, var forenames: String? = null, @@ -8,4 +14,14 @@ data class DriverProfile( var dob: String? = null, var ni: String? = null, var dateFirst: String? = null -) : Model \ No newline at end of file +) : ImageDocument { + @Exclude + override fun getImageFileName(): String? { + return driverPic + } + + @Exclude + override fun setImageFileName(fileName: String) { + driverPic = fileName + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/model/DriversLicense.kt b/app/src/main/java/h_mal/appttude/com/driver/model/DriversLicense.kt index fd34f65..9970965 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/model/DriversLicense.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/model/DriversLicense.kt @@ -1,8 +1,22 @@ 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( var licenseImageString: String? = null, var licenseNumber: String? = null, var licenseExpiry: String? = null -) : Model \ No newline at end of file +) : ImageDocument { + @Exclude + override fun getImageFileName(): String? { + return licenseImageString + } + @Exclude + override fun setImageFileName(fileName: String) { + licenseImageString = fileName + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/model/Insurance.kt b/app/src/main/java/h_mal/appttude/com/driver/model/Insurance.kt index 712b5fe..2c01a62 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/model/Insurance.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/model/Insurance.kt @@ -1,7 +1,31 @@ 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( var photoStrings: MutableList? = null, var insurerName: String? = null, var expiryDate: String? = null -) : Model \ No newline at end of file +) : MultiImageDocument { + @Exclude + override fun getImageFileNames(): List? { + return photoStrings + } + + @Exclude + override fun setImageFileNames(images: List) { + // update existing array + photoStrings?.run{ + clear() + addAll(images) + return + } + // set new array of images + photoStrings = mutableListOf().apply { + addAll(images) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/model/Logbook.kt b/app/src/main/java/h_mal/appttude/com/driver/model/Logbook.kt index ea74f49..3b15c50 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/model/Logbook.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/model/Logbook.kt @@ -1,7 +1,20 @@ 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( var photoString: String? = null, var v5cnumber: String? = null -) : Model +) : ImageDocument{ + @Exclude + override fun getImageFileName(): String? { + return photoString + } + @Exclude + override fun setImageFileName(fileName: String) { + photoString = fileName + } +} diff --git a/app/src/main/java/h_mal/appttude/com/driver/model/Model.kt b/app/src/main/java/h_mal/appttude/com/driver/model/Model.kt deleted file mode 100644 index 004e1bc..0000000 --- a/app/src/main/java/h_mal/appttude/com/driver/model/Model.kt +++ /dev/null @@ -1,3 +0,0 @@ -package h_mal.appttude.com.driver.model - -interface Model \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/model/Mot.kt b/app/src/main/java/h_mal/appttude/com/driver/model/Mot.kt index 10fa777..7b2e756 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/model/Mot.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/model/Mot.kt @@ -1,7 +1,21 @@ 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( var motImageString: String? = null, var motExpiry: String? = null -) : Model \ No newline at end of file +) : ImageDocument { + @Exclude + override fun getImageFileName(): String? { + return motImageString + } + + @Exclude + override fun setImageFileName(fileName: String) { + motImageString = fileName + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/model/PrivateHireLicense.kt b/app/src/main/java/h_mal/appttude/com/driver/model/PrivateHireLicense.kt index 9148c92..26367b8 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/model/PrivateHireLicense.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/model/PrivateHireLicense.kt @@ -1,8 +1,23 @@ 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( var phImageString: String? = null, var phNumber: String? = null, var phExpiry: String? = null -) : Model \ No newline at end of file +) : ImageDocument { + @Exclude + override fun getImageFileName(): String? { + return phImageString + } + + @Exclude + override fun setImageFileName(fileName: String) { + phImageString = fileName + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/model/PrivateHireVehicle.kt b/app/src/main/java/h_mal/appttude/com/driver/model/PrivateHireVehicle.kt index 005a45a..34b6ccc 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/model/PrivateHireVehicle.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/model/PrivateHireVehicle.kt @@ -1,8 +1,23 @@ 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( var phCarImageString: String? = null, var phCarNumber: String? = null, var phCarExpiry: String? = null -) : Model \ No newline at end of file +) : ImageDocument { + + @Exclude + override fun getImageFileName(): String? { + return phCarImageString + } + + @Exclude + override fun setImageFileName(fileName: String) { + phCarImageString = fileName + } +} \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/model/VehicleProfile.kt b/app/src/main/java/h_mal/appttude/com/driver/model/VehicleProfile.kt index 106e68f..b9380a9 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/model/VehicleProfile.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/model/VehicleProfile.kt @@ -1,6 +1,9 @@ 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( var reg: String? = null, var make: String? = null, @@ -11,4 +14,4 @@ data class VehicleProfile( var keeperPostCode: String? = null, var startDate: String? = null, var isSeized: Boolean = false -) : Model \ No newline at end of file +) : Document \ No newline at end of file diff --git a/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseUtils.kt b/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseUtils.kt index 2fca974..1deb81e 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseUtils.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseUtils.kt @@ -76,7 +76,7 @@ suspend fun DatabaseReference.getDataFromDatabaseRef(clazz: Class): fun DatabaseReference.toLiveData(): LiveData { return object : LiveData() { - private val listener = addValueEventListener(object : ValueEventListener{ + private val listener = object : ValueEventListener{ override fun onDataChange(snapshot: DataSnapshot) { val data = snapshot.getValue(object : GenericTypeIndicator() {}) postValue(DataState.HasData(data ?: FirebaseCompletion.Default)) @@ -84,7 +84,7 @@ fun DatabaseReference.toLiveData(): LiveData { override fun onCancelled(error: DatabaseError) { postValue(DataState.HasError(error)) } - }) + } override fun onActive() { super.onActive() // add listener diff --git a/app/src/main/java/h_mal/appttude/com/driver/utils/GenericsHelper.kt b/app/src/main/java/h_mal/appttude/com/driver/utils/GenericsHelper.kt index 421815d..47351dc 100644 --- a/app/src/main/java/h_mal/appttude/com/driver/utils/GenericsHelper.kt +++ b/app/src/main/java/h_mal/appttude/com/driver/utils/GenericsHelper.kt @@ -12,7 +12,7 @@ object GenericsHelper { ((javaClass.genericSuperclass as? ParameterizedType) ?.actualTypeArguments?.getOrNull(position) as? Class) ?.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] diff --git a/firebase.json b/firebase.json index 272d4a0..9978b99 100644 --- a/firebase.json +++ b/firebase.json @@ -4,7 +4,7 @@ "port": 9099 }, "database": { - "port": 9000 + "port": 9001 }, "storage": { "port": 9199 @@ -12,12 +12,27 @@ "ui": { "enabled": true }, - "singleProjectMode": true + "singleProjectMode": true, + "functions": { + "port": 5001 + } }, "database": { "rules": "database.rules.json" }, "storage": { "rules": "storage.rules" - } + }, + "functions": [ + { + "source": "functions", + "codebase": "default", + "ignore": [ + "venv", + ".git", + "firebase-debug.log", + "firebase-debug.*.log" + ] + } + ] }