- android bumblebee gradle migration

- lint driverDebug
 - view binding migration

Took 12 hours 10 minutes
This commit is contained in:
2023-03-24 19:15:48 +00:00
parent 7f4060f1c2
commit 1b00d7d40d
125 changed files with 1163 additions and 1013 deletions

View File

@@ -1,16 +1,9 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.google.gms.google-services'
// kotlin kapt
apply plugin: 'kotlin-kapt'
// Android navigation
apply plugin: 'androidx.navigation.safeargs'
repositories {
mavenCentral()
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.gms.google-services'
id 'kotlin-kapt'
id 'androidx.navigation.safeargs'
}
def relStorePassword = System.getenv("RELEASE_STORE_PASSWORD")
@@ -59,10 +52,16 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding true
}
flavorDimensions "Default"
productFlavors {
@@ -139,7 +138,7 @@ dependencies {
/ * coroutines support for firebase operations */
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.1"
/ * Circle Image View */
implementation "com.mikhaellopez:circularimageview:4.2.0"
implementation "com.mikhaellopez:circularimageview:4.3.0"
/ * Kodein Dependency Injection */
def kodein_version = "6.2.1"
implementation "org.kodein.di:kodein-di-generic-jvm:$kodein_version"
@@ -151,4 +150,6 @@ dependencies {
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
/ * OKHttp */
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
/ * Kotlin Reflect */
implementation "org.jetbrains.kotlin:kotlin-reflect:1.8.10"
}

View File

@@ -1,7 +1,6 @@
package h_mal.appttude.com.objects
class ApprovalsObject {
var driver_details_approval: Int = 0
var driver_license_approval: Int = 0

View File

@@ -1,7 +1,6 @@
package h_mal.appttude.com.objects
class UserObject {
var profileName: String? = null
var profileEmail: String? = null

View File

@@ -29,7 +29,8 @@ class MappedObject : Parcelable {
companion object {
@JvmField
val CREATOR: Parcelable.Creator<MappedObject?> = object : Parcelable.Creator<MappedObject?> {
val CREATOR: Parcelable.Creator<MappedObject?> =
object : Parcelable.Creator<MappedObject?> {
override fun createFromParcel(`in`: Parcel): MappedObject? {
return MappedObject(`in`)
}

View File

@@ -125,7 +125,9 @@ class HomeSuperUserFragment : Fragment() {
}
return s1!!.compareTo((s2)!!)
}
else -> { throw IOException("dfdfs") }
else -> {
throw IOException("dfdfs")
}
// 2 -> return MainActivity.approvalsClass.getOverApprovalStatusCode(o1.wholeDriverObject) -
// MainActivity.approvalsClass.getOverApprovalStatusCode(o2.wholeDriverObject)
// else -> return MainActivity.approvalsClass.getOverApprovalStatusCode(

View File

@@ -19,6 +19,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/cardviewoutline" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

View File

@@ -21,6 +21,7 @@
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/choice_img_round" />
<ImageView
android:id="@+id/approval_iv"
android:layout_width="10dp"

View File

@@ -15,9 +15,13 @@ import org.hamcrest.CoreMatchers.anything
open class BaseTestRobot {
fun fillEditText(resId: Int, text: String?): ViewInteraction =
onView(withId(resId)).perform(ViewActions.replaceText(text), ViewActions.closeSoftKeyboard())
onView(withId(resId)).perform(
ViewActions.replaceText(text),
ViewActions.closeSoftKeyboard()
)
fun clickButton(resId: Int): ViewInteraction = onView((withId(resId))).perform(ViewActions.click())
fun clickButton(resId: Int): ViewInteraction =
onView((withId(resId))).perform(ViewActions.click())
fun textView(resId: Int): ViewInteraction = onView(withId(resId))
@@ -35,6 +39,7 @@ open class BaseTestRobot {
fun checkErrorOnTextEntry(resId: Int, errorMessage: String): ViewInteraction =
onView(withId(resId)).check(matches(checkErrorMessage(errorMessage)))
fun getStringFromResource(@StringRes resId: Int): String = Resources.getSystem().getString(resId)
fun getStringFromResource(@StringRes resId: Int): String =
Resources.getSystem().getString(resId)
}

View File

@@ -16,7 +16,7 @@ import org.junit.After
import org.junit.Before
open class BaseUiTest<T : BaseActivity<*>>(
open class BaseUiTest<T : BaseActivity<*,*>>(
private val activity: Class<T>
) {

View File

@@ -3,7 +3,8 @@ package h_mal.appttude.com
private const val apiKey = "test_key"
const val signUpFirebase = "http://identitytoolkit.googleapis.com/v1/accounts:signUp?key=$apiKey"
const val deleteAccountFirebase = "http://10.0.2.2:9099/identitytoolkit.googleapis.com/v1/accounts:delete?key=$apiKey"
const val deleteAccountFirebase =
"http://10.0.2.2:9099/identitytoolkit.googleapis.com/v1/accounts:delete?key=$apiKey"
const val USER_PASSWORD = "LetMeIn123!"

View File

@@ -23,6 +23,7 @@ fun checkErrorMessage(expectedErrorText: String): Matcher<View?>? {
val error = view.error ?: return false
return expectedErrorText == error.toString()
}
override fun describeTo(d: Description?) {}
}
}

View File

@@ -10,7 +10,7 @@ import kotlinx.coroutines.tasks.await
import org.junit.After
import org.junit.BeforeClass
open class FirebaseTest<T : BaseActivity<*>>(
open class FirebaseTest<T : BaseActivity<*,*>>(
activity: Class<T>,
private val registered: Boolean = false,
private val signedIn: Boolean = false
@@ -62,7 +62,10 @@ open class FirebaseTest<T : BaseActivity<*>>(
suspend fun removeUser() {
try {
getEmail()?.let {
if (firebaseAuthSource.getUser() == null) firebaseAuthSource.signIn(email = it, password = USER_PASSWORD).await()
if (firebaseAuthSource.getUser() == null) firebaseAuthSource.signIn(
email = it,
password = USER_PASSWORD
).await()
firebaseAuthSource.reauthenticate(it, USER_PASSWORD).await()
firebaseAuthSource.deleteProfile().await()
}

View File

@@ -8,7 +8,6 @@ import h_mal.appttude.com.R
import h_mal.appttude.com.USER_PASSWORD
import h_mal.appttude.com.robots.home
import h_mal.appttude.com.robots.login
import h_mal.appttude.com.robots.register
import h_mal.appttude.com.ui.user.LoginActivity
import org.junit.*
import org.junit.runner.RunWith
@@ -16,7 +15,8 @@ import org.junit.runner.RunWith
@LargeTest
@RunWith(AndroidJUnit4::class)
class RegisteredUserAuthenticationActivityTest : FirebaseTest<LoginActivity>(LoginActivity::class.java, registered = true, signedIn = false) {
class RegisteredUserAuthenticationActivityTest :
FirebaseTest<LoginActivity>(LoginActivity::class.java, registered = true, signedIn = false) {
@Test
fun verifyUserLogin_validUsernameAndPassword_loggedIn() {

View File

@@ -1,7 +1,9 @@
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application android:usesCleartextTraffic="true"
<application
android:usesCleartextTraffic="true"
tools:ignore="MissingApplicationIcon" />
</manifest>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain>10.0.2.2</domain>
<domain includeSubdomains="true">10.0.2.2</domain>
</domain-config>
</network-security-config>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:icon="@mipmap/ic_launcher"
<application
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round">
<activity
android:name="h_mal.appttude.com.ui.user.LoginActivity"
@@ -23,6 +24,7 @@
<activity
android:name="h_mal.appttude.com.ui.update.UpdateActivity"
android:theme="@style/AppTheme.NoActionBar.Update" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="h_mal.appttude.com.driver"

View File

@@ -1,25 +1,32 @@
package h_mal.appttude.com.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import h_mal.appttude.com.R
import h_mal.appttude.com.databinding.FragmentDriverOverallBinding
import h_mal.appttude.com.utils.navigateTo
import kotlinx.android.synthetic.main.fragment_driver_overall.*
class DriverOverallFragment : Fragment(R.layout.fragment_driver_overall) {
class DriverOverallFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
driver_prof.setOnClickListener {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return FragmentDriverOverallBinding.inflate(inflater, container, false).apply {
driverProf.setOnClickListener {
it.navigateTo(R.id.to_driverProfileFragment)
}
private_hire.setOnClickListener {
privateHire.setOnClickListener {
it.navigateTo(R.id.to_privateHireLicenseFragment2)
}
drivers_license.setOnClickListener {
driversLicense.setOnClickListener {
it.navigateTo(R.id.to_driverLicenseFragment)
}
}.root
}
}

View File

@@ -5,25 +5,22 @@ import android.view.View
import h_mal.appttude.com.R
import h_mal.appttude.com.base.DataSubmissionBaseFragment
import h_mal.appttude.com.data.DRIVER
import h_mal.appttude.com.databinding.FragmentHomeDriverBinding
import h_mal.appttude.com.utils.hide
import h_mal.appttude.com.utils.navigateTo
import h_mal.appttude.com.utils.show
import h_mal.appttude.com.viewmodels.RoleViewModel
import kotlinx.android.synthetic.main.driver_profile_request.*
import kotlinx.android.synthetic.main.fragment_home_driver.*
import kotlinx.android.synthetic.main.home_buttons_container.*
class HomeFragment : DataSubmissionBaseFragment<RoleViewModel, String>(R.layout.fragment_home_driver) {
class HomeFragment :
DataSubmissionBaseFragment<RoleViewModel, FragmentHomeDriverBinding, String>() {
private val viewmodel: RoleViewModel by getFragmentViewModel()
override fun getViewModel(): RoleViewModel = viewmodel
override var model = String()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewmodel.getDataFromDatabase()
viewModel.getDataFromDatabase()
}
override fun onSuccess(data: Any?) {
@@ -36,24 +33,29 @@ class HomeFragment : DataSubmissionBaseFragment<RoleViewModel, String>(R.layout.
}
private fun loadNonDriver() {
home_buttons_container.hide()
profile_request_container.show()
applyBinding {
homeButtonsContainer.root.hide()
profileRequestContainer.root.show()
request_driver_button.setOnClickListener {
viewmodel.setDataInDatabase(DRIVER)
profileRequestContainer.requestDriverButton.setOnClickListener {
viewModel.setDataInDatabase(DRIVER)
}
}
}
private fun loadDriver() {
home_buttons_container.show()
profile_request_container.hide()
applyBinding {
homeButtonsContainer.apply {
driver.setOnClickListener {
view?.navigateTo(R.id.to_driverOverallFragment)
}
car.setOnClickListener {
view?.navigateTo(R.id.to_vehicleOverallFragment)
}
root.show()
}
profileRequestContainer.root.hide()
}
}
}

View File

@@ -1,9 +1,7 @@
package h_mal.appttude.com.ui
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import androidx.core.view.GravityCompat
import androidx.navigation.NavController
import androidx.navigation.findNavController
@@ -15,36 +13,30 @@ import com.google.android.material.navigation.NavigationView
import com.google.firebase.auth.FirebaseUser
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseActivity
import h_mal.appttude.com.databinding.ActivityMainBinding
import h_mal.appttude.com.databinding.NavHeaderMainBinding
import h_mal.appttude.com.dialogs.ExitDialog.displayExitDialog
import h_mal.appttude.com.utils.isTrue
import h_mal.appttude.com.utils.setGlideImage
import h_mal.appttude.com.viewmodels.MainViewModel
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.nav_header_main.view.*
class MainActivity : BaseActivity<MainViewModel>(),
class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>(),
NavigationView.OnNavigationItemSelectedListener {
private val vm by createLazyViewModel<MainViewModel>()
override fun getViewModel(): MainViewModel = vm
override val layoutId: Int = R.layout.activity_main
lateinit var navController: NavController
lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setSupportActionBar(toolbar)
override fun setupView(binding: ActivityMainBinding) = binding.run {
setSupportActionBar(appBarLayout.toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false)
navController = findNavController(R.id.container)
appBarConfiguration = AppBarConfiguration(navController.graph, drawer_layout)
nav_view.setupWithNavController(navController)
appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
navView.setupWithNavController(navController)
setupActionBarWithNavController(navController, appBarConfiguration)
getViewModel().getUserDetails()
viewModel.getUserDetails()
setupLogoutInDrawer()
}
@@ -53,20 +45,25 @@ class MainActivity : BaseActivity<MainViewModel>(),
}
override fun setTitle(title: CharSequence) {
toolbar.title = title
applyBinding {
appBarLayout.toolbar.title = title
}
}
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START)
applyBinding {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START)
} else {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.container)
navHostFragment?.childFragmentManager?.backStackEntryCount?.takeIf { it >= 1 }?.let {
return super.onBackPressed()
navHostFragment?.childFragmentManager?.backStackEntryCount?.let { it >= 1 }?.isTrue {
super.onBackPressed()
}
displayExitDialog()
}
}
}
override fun onSuccess(data: Any?) {
super.onSuccess(data)
@@ -78,15 +75,20 @@ class MainActivity : BaseActivity<MainViewModel>(),
}
private fun setupDrawer(user: FirebaseUser) {
val header: View = nav_view.getHeaderView(0)
header.driver_email.text = user.email
header.driver_name.text = user.displayName
header.profileImage.setGlideImage(user.photoUrl)
applyBinding {
NavHeaderMainBinding.inflate(layoutInflater).apply {
driverEmail.text = user.email
driverName.text = user.displayName
profileImage.setGlideImage(user.photoUrl)
}
}
}
private fun setupLogoutInDrawer() {
applyBinding {
logout.setOnClickListener {
getViewModel().logOut()
viewModel.logOut()
}
}
}
@@ -95,7 +97,10 @@ class MainActivity : BaseActivity<MainViewModel>(),
when (item.itemId) {
R.id.nav_user_settings -> {}
}
drawer_layout.closeDrawer(GravityCompat.START)
applyBinding {
drawerLayout.closeDrawer(GravityCompat.START)
}
return true
}
}

View File

@@ -1,23 +1,30 @@
package h_mal.appttude.com.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import h_mal.appttude.com.R
import h_mal.appttude.com.databinding.FragmentVehicleOverallBinding
import h_mal.appttude.com.utils.navigateTo
import kotlinx.android.synthetic.main.fragment_vehicle_overall.*
class VehicleOverallFragment : Fragment(R.layout.fragment_vehicle_overall) {
class VehicleOverallFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
vehicle_prof.setOnClickListener { it.navigateTo(R.id.to_vehicleSetupFragment) }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return FragmentVehicleOverallBinding.inflate(inflater, container, false).apply {
vehicleProf.setOnClickListener {
it.navigateTo(R.id.to_vehicleSetupFragment)
}
insurance.setOnClickListener { it.navigateTo(R.id.to_insuranceFragment) }
mot.setOnClickListener { it.navigateTo(R.id.to_motFragment) }
logbook.setOnClickListener { it.navigateTo(R.id.to_logbookFragment) }
private_hire_vehicle_license.setOnClickListener { it.navigateTo(R.id.to_privateHireVehicleFragment) }
privateHireVehicleLicense.setOnClickListener { it.navigateTo(R.id.to_privateHireVehicleFragment) }
}.root
}
}

View File

@@ -1,20 +1,26 @@
package h_mal.appttude.com.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import h_mal.appttude.com.R
import h_mal.appttude.com.databinding.FragmentWelcomeBinding
import h_mal.appttude.com.utils.navigateTo
import kotlinx.android.synthetic.driver.fragment_welcome.*
class WelcomeFragment : Fragment(R.layout.fragment_welcome) {
class WelcomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
email_sign_in_button.setOnClickListener {
view.navigateTo(R.id.to_driverOverallFragment)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return FragmentWelcomeBinding.inflate(inflater, container, false).apply {
emailSignInButton.setOnClickListener {
it.navigateTo(R.id.to_driverOverallFragment)
}
}.root
}
}

View File

@@ -1,29 +1,22 @@
package h_mal.appttude.com.ui.driverprofile
import android.net.Uri
import android.os.Bundle
import android.view.View
import h_mal.appttude.com.R
import h_mal.appttude.com.base.DataSubmissionBaseFragment
import h_mal.appttude.com.databinding.FragmentDriverLicenseBinding
import h_mal.appttude.com.dialogs.DateDialog
import h_mal.appttude.com.model.DriversLicenseObject
import h_mal.appttude.com.model.DriversLicense
import h_mal.appttude.com.utils.isTrue
import h_mal.appttude.com.utils.setGlideImage
import h_mal.appttude.com.viewmodels.DriverLicenseViewModel
import kotlinx.android.synthetic.main.fragment_driver_license.*
class DriverLicenseFragment :
DataSubmissionBaseFragment<DriverLicenseViewModel, DriversLicenseObject>(R.layout.fragment_driver_license) {
DataSubmissionBaseFragment<DriverLicenseViewModel, FragmentDriverLicenseBinding, DriversLicense>() {
private val viewmodel: DriverLicenseViewModel by getFragmentViewModel()
override fun getViewModel(): DriverLicenseViewModel = viewmodel
override var model = DriversLicenseObject()
override var model = DriversLicense()
private var imageUri: Uri? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lic_expiry.apply {
override fun setupView(binding: FragmentDriverLicenseBinding) {
binding.apply {
licExpiry.apply {
setTextOnChange { model.licenseExpiry = it }
setOnClickListener {
DateDialog(this) { date ->
@@ -31,29 +24,31 @@ class DriverLicenseFragment :
}
}
}
lic_no.setTextOnChange { model.licenseNumber = it }
licNo.setTextOnChange { model.licenseNumber = it }
search_image.setOnClickListener { openGalleryWithPermissionRequest() }
submit.setOnClickListener { submit() }
searchImage.setOnClickListener { openGalleryWithPermissionRequest() }
submit.setOnClickListener {
validateEditTexts(licExpiry, licNo).isTrue {
viewModel.setDataInDatabase(model, picUri)
}
}
}
}
override fun submit() {
validateEditTexts(lic_expiry, lic_no).takeIf { !it }?.let { return }
viewmodel.setDataInDatabase(model, imageUri)
}
override fun setFields(data: DriversLicenseObject) {
override fun setFields(data: DriversLicense) {
super.setFields(data)
driversli_img.setGlideImage(data.licenseImageString)
lic_no.setText(data.licenseNumber)
lic_expiry.setText(data.licenseExpiry)
applyBinding {
driversliImg.setGlideImage(data.licenseImageString)
licNo.setText(data.licenseNumber)
licExpiry.setText(data.licenseExpiry)
}
}
override fun onImageGalleryResult(imageUri: Uri?) {
super.onImageGalleryResult(imageUri)
this.imageUri = imageUri
driversli_img.setGlideImage(imageUri)
applyBinding {
driversliImg.setGlideImage(imageUri)
}
}
}

View File

@@ -1,33 +1,25 @@
package h_mal.appttude.com.ui.driverprofile
import android.net.Uri
import android.os.Bundle
import android.view.View
import h_mal.appttude.com.R
import h_mal.appttude.com.base.DataSubmissionBaseFragment
import h_mal.appttude.com.databinding.FragmentDriverProfileBinding
import h_mal.appttude.com.dialogs.DateDialog
import h_mal.appttude.com.model.DriverProfileObject
import h_mal.appttude.com.model.DriverProfile
import h_mal.appttude.com.utils.isTrue
import h_mal.appttude.com.utils.setGlideImage
import h_mal.appttude.com.viewmodels.DriverProfileViewModel
import kotlinx.android.synthetic.main.fragment_driver_profile.*
class DriverProfileFragment: DataSubmissionBaseFragment
<DriverProfileViewModel, DriverProfileObject>(R.layout.fragment_driver_profile) {
class DriverProfileFragment :
DataSubmissionBaseFragment<DriverProfileViewModel, FragmentDriverProfileBinding, DriverProfile>() {
var localUri: Uri? = null
override var model = DriverProfile()
private val viewmodel by getFragmentViewModel<DriverProfileViewModel>()
override fun getViewModel(): DriverProfileViewModel = viewmodel
override var model = DriverProfileObject()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
names_input.setTextOnChange{ model.forenames = it }
address_input.setTextOnChange{ model.address = it }
postcode_input.setTextOnChange{ model.postcode = it }
dob_input.apply {
override fun setupView(binding: FragmentDriverProfileBinding) = binding.run {
namesInput.setTextOnChange { model.forenames = it }
addressInput.setTextOnChange { model.address = it }
postcodeInput.setTextOnChange { model.postcode = it }
dobInput.apply {
setTextOnChange { model.dob = it }
setOnClickListener {
DateDialog(this) { date ->
@@ -35,8 +27,8 @@ class DriverProfileFragment: DataSubmissionBaseFragment
}
}
}
ni_number.setTextOnChange{ model.ni = it }
date_first.apply {
niNumber.setTextOnChange { model.ni = it }
dateFirst.apply {
setTextOnChange { model.dateFirst = it }
setOnClickListener {
DateDialog(this) { date ->
@@ -44,34 +36,39 @@ class DriverProfileFragment: DataSubmissionBaseFragment
}
}
}
add_photo.setOnClickListener { openGalleryWithPermissionRequest() }
submit_driver.setOnClickListener{ submit() }
addPhoto.setOnClickListener { openGalleryWithPermissionRequest() }
submitDriver.setOnClickListener { submit() }
}
override fun submit() {
validateEditTexts(names_input, address_input, postcode_input,
dob_input, ni_number, date_first)
.takeIf { !it }
?.let { return }
viewmodel.setDataInDatabase(model, localUri)
applyBinding {
validateEditTexts(
namesInput, addressInput, postcodeInput,
dobInput, niNumber, dateFirst
).isTrue {
viewModel.setDataInDatabase(model, picUri)
}
}
}
override fun setFields(data: DriverProfileObject) {
override fun setFields(data: DriverProfile) {
super.setFields(data)
driver_pic.setGlideImage(data.driverPic)
names_input.setText(data.forenames)
address_input.setText(data.address)
postcode_input.setText(data.postcode)
dob_input.setText(data.dob)
ni_number.setText(data.ni)
date_first.setText(data.dateFirst)
applyBinding {
driverPic.setGlideImage(data.driverPic)
namesInput.setText(data.forenames)
addressInput.setText(data.address)
postcodeInput.setText(data.postcode)
dobInput.setText(data.dob)
niNumber.setText(data.ni)
dateFirst.setText(data.dateFirst)
}
}
override fun onImageGalleryResult(imageUri: Uri?) {
super.onImageGalleryResult(imageUri)
localUri = imageUri
driver_pic.setGlideImage(imageUri)
applyBinding {
driverPic.setGlideImage(imageUri)
}
}
}

View File

@@ -1,29 +1,23 @@
package h_mal.appttude.com.ui.driverprofile
import android.net.Uri
import android.os.Bundle
import android.view.View
import h_mal.appttude.com.R
import h_mal.appttude.com.base.DataSubmissionBaseFragment
import h_mal.appttude.com.databinding.FragmentPrivateHireLicenseBinding
import h_mal.appttude.com.dialogs.DateDialog
import h_mal.appttude.com.model.PrivateHireObject
import h_mal.appttude.com.model.PrivateHireLicense
import h_mal.appttude.com.utils.isTrue
import h_mal.appttude.com.utils.setGlideImage
import h_mal.appttude.com.viewmodels.PrivateHireLicenseViewModel
import kotlinx.android.synthetic.main.fragment_private_hire_license.*
class PrivateHireLicenseFragment : DataSubmissionBaseFragment
<PrivateHireLicenseViewModel, PrivateHireObject>(R.layout.fragment_private_hire_license) {
<PrivateHireLicenseViewModel, FragmentPrivateHireLicenseBinding, PrivateHireLicense>() {
val viewmodel by getFragmentViewModel<PrivateHireLicenseViewModel>()
override fun getViewModel(): PrivateHireLicenseViewModel = viewmodel
override var model = PrivateHireObject()
override var model = PrivateHireLicense()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ph_no.setTextOnChange{ model.phNumber = it }
ph_expiry.apply {
override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run {
phNo.setTextOnChange { model.phNumber = it }
phExpiry.apply {
setTextOnChange { model.phExpiry = it }
setOnClickListener {
DateDialog(this) { date ->
@@ -37,21 +31,27 @@ class PrivateHireLicenseFragment : DataSubmissionBaseFragment
}
override fun submit() {
validateEditTexts(ph_no,ph_expiry).takeIf { !it }?.let { return }
viewmodel.setDataInDatabase(model, picUri)
applyBinding {
validateEditTexts(phNo, phExpiry).isTrue {
viewModel.setDataInDatabase(model, picUri)
}
}
}
override fun setFields(data: PrivateHireObject) {
override fun setFields(data: PrivateHireLicense) {
super.setFields(data)
applyBinding {
imageView2.setGlideImage(data.phImageString)
ph_no.setText(data.phNumber)
ph_expiry.setText(data.phExpiry)
phNo.setText(data.phNumber)
phExpiry.setText(data.phExpiry)
}
}
override fun onImageGalleryResult(imageUri: Uri?) {
super.onImageGalleryResult(imageUri)
applyBinding {
imageView2.setGlideImage(imageUri)
}
}
}

View File

@@ -4,41 +4,42 @@ import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import h_mal.appttude.com.R
import h_mal.appttude.com.base.DataSubmissionBaseFragment
import h_mal.appttude.com.databinding.FragmentInsuranceBinding
import h_mal.appttude.com.dialogs.DateDialog
import h_mal.appttude.com.model.InsuranceObject
import h_mal.appttude.com.model.Insurance
import h_mal.appttude.com.utils.isTrue
import h_mal.appttude.com.utils.setGlideImage
import h_mal.appttude.com.viewmodels.InsuranceViewModel
import kotlinx.android.synthetic.main.fragment_insurance.*
class InsuranceFragment : DataSubmissionBaseFragment<InsuranceViewModel, InsuranceObject>(R.layout.fragment_insurance) {
class InsuranceFragment :
DataSubmissionBaseFragment<InsuranceViewModel, FragmentInsuranceBinding, Insurance>() {
private var selectedImages: List<Uri>? = listOf()
private val viewmodel: InsuranceViewModel by getFragmentViewModel()
override fun getViewModel(): InsuranceViewModel = viewmodel
override var model = InsuranceObject()
override var model = Insurance()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setImageSelectionAsMultiple()
applyBinding {
insurer.setTextOnChange { model.insurerName = it }
insurance_exp.apply {
insuranceExp.apply {
setOnClickListener {
DateDialog(this) { date ->
model.expiryDate = date
}
}
}
uploadInsurance.setOnClickListener { openGalleryWithPermissionRequest() }
submit_ins.setOnClickListener { submit() }
submitIns.setOnClickListener { submit() }
}
}
private fun updateImageCarousal(list: List<Any?>) {
applyBinding {
carouselView.setImageClickListener(null)
carouselView.setImageListener { i: Int, imageView: ImageView ->
when (list[i]) {
@@ -49,22 +50,26 @@ class InsuranceFragment : DataSubmissionBaseFragment<InsuranceViewModel, Insuran
}
}
carouselView.pageCount = list.size
}
}
override fun submit() {
super.submit()
validateEditTexts(insurer, insurance_exp).takeIf { !it }?.let { return }
viewmodel.setDataInDatabase(model, selectedImages)
applyBinding {
validateEditTexts(insurer, insuranceExp).isTrue {
viewModel.setDataInDatabase(model, selectedImages)
}
}
}
override fun setFields(data: InsuranceObject) {
override fun setFields(data: Insurance) {
super.setFields(data)
applyBinding {
insurer.setText(model.insurerName)
insurance_exp.setText(model.expiryDate)
insuranceExp.setText(model.expiryDate)
model.photoStrings?.let { updateImageCarousal(it) }
}
}
override fun onImageGalleryResult(imageUris: List<Uri>?) {
selectedImages = imageUris

View File

@@ -1,50 +1,47 @@
package h_mal.appttude.com.ui.vehicleprofile
import android.net.Uri
import android.os.Bundle
import android.view.View
import h_mal.appttude.com.R
import h_mal.appttude.com.base.DataSubmissionBaseFragment
import h_mal.appttude.com.model.LogbookObject
import h_mal.appttude.com.databinding.FragmentLogbookBinding
import h_mal.appttude.com.model.Logbook
import h_mal.appttude.com.utils.isTrue
import h_mal.appttude.com.utils.setGlideImage
import h_mal.appttude.com.viewmodels.LogbookViewModel
import kotlinx.android.synthetic.main.fragment_logbook.*
class LogbookFragment : DataSubmissionBaseFragment<LogbookViewModel, LogbookObject>(R.layout.fragment_logbook) {
class LogbookFragment :
DataSubmissionBaseFragment<LogbookViewModel, FragmentLogbookBinding, Logbook>() {
private val viewmodel by getFragmentViewModel<LogbookViewModel>()
override fun getViewModel(): LogbookViewModel = viewmodel
override var model = LogbookObject()
override var model = Logbook()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
v5c_no.setTextOnChange{ model.v5cnumber = it }
upload_lb.setOnClickListener { openGalleryWithPermissionRequest() }
submit_lb.setOnClickListener { submit() }
override fun setupView(binding: FragmentLogbookBinding) = binding.run {
v5cNo.setTextOnChange { model.v5cnumber = it }
uploadLb.setOnClickListener { openGalleryWithPermissionRequest() }
submitLb.setOnClickListener { submit() }
}
override fun submit() {
super.submit()
validateEditTexts(v5c_no)
.takeIf { !it }
?.let { return }
viewmodel.setDataInDatabase(model, picUri)
applyBinding {
validateEditTexts(v5cNo).isTrue {
viewModel.setDataInDatabase(model, picUri)
}
}
}
override fun setFields(data: LogbookObject) {
override fun setFields(data: Logbook) {
super.setFields(data)
applyBinding {
logBookImg.setGlideImage(data.photoString)
v5cNo.setText(data.v5cnumber)
}
log_book_img.setGlideImage(data.photoString)
v5c_no.setText(data.v5cnumber)
}
override fun onImageGalleryResult(imageUri: Uri?) {
super.onImageGalleryResult(imageUri)
picUri = imageUri
log_book_img.setGlideImage(picUri)
applyBinding {
logBookImg.setGlideImage(picUri)
}
}
}

View File

@@ -1,27 +1,21 @@
package h_mal.appttude.com.ui.vehicleprofile
import android.net.Uri
import android.os.Bundle
import android.view.View
import h_mal.appttude.com.R
import h_mal.appttude.com.base.DataSubmissionBaseFragment
import h_mal.appttude.com.databinding.FragmentMotBinding
import h_mal.appttude.com.dialogs.DateDialog
import h_mal.appttude.com.model.MotObject
import h_mal.appttude.com.model.Mot
import h_mal.appttude.com.utils.isTrue
import h_mal.appttude.com.utils.setGlideImage
import h_mal.appttude.com.viewmodels.MotViewModel
import kotlinx.android.synthetic.main.fragment_mot.*
class MotFragment: DataSubmissionBaseFragment<MotViewModel, MotObject>(R.layout.fragment_mot){
class MotFragment : DataSubmissionBaseFragment<MotViewModel, FragmentMotBinding, Mot>() {
private val viewmodel by getFragmentViewModel<MotViewModel>()
override fun getViewModel(): MotViewModel = viewmodel
override var model = MotObject()
override var model = Mot()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mot_expiry.apply {
override fun setupView(binding: FragmentMotBinding) = binding.run {
motExpiry.apply {
setOnClickListener {
DateDialog(this) { date ->
model.motExpiry = date
@@ -30,23 +24,25 @@ class MotFragment: DataSubmissionBaseFragment<MotViewModel, MotObject>(R.layout.
}
uploadmot.setOnClickListener { openGalleryWithPermissionRequest() }
submit_mot.setOnClickListener { submit() }
submitMot.setOnClickListener {
validateEditTexts(motExpiry).isTrue {
viewModel.setDataInDatabase(model, picUri)
}
}
}
override fun submit() {
super.submit()
validateEditTexts(mot_expiry).takeIf { !it }?.let { return }
viewmodel.setDataInDatabase(model, picUri)
}
override fun setFields(data: MotObject) {
override fun setFields(data: Mot) {
super.setFields(data)
mot_img.setGlideImage(data.motImageString)
mot_expiry.setText(data.motExpiry)
applyBinding {
motImg.setGlideImage(data.motImageString)
motExpiry.setText(data.motExpiry)
}
}
override fun onImageGalleryResult(imageUri: Uri?) {
super.onImageGalleryResult(imageUri)
mot_img.setGlideImage(imageUri)
applyBinding {
motImg.setGlideImage(imageUri)
}
}
}

View File

@@ -1,29 +1,23 @@
package h_mal.appttude.com.ui.vehicleprofile
import android.net.Uri
import android.os.Bundle
import android.view.View
import h_mal.appttude.com.R
import h_mal.appttude.com.base.DataSubmissionBaseFragment
import h_mal.appttude.com.databinding.FragmentPrivateHireLicenseBinding
import h_mal.appttude.com.dialogs.DateDialog
import h_mal.appttude.com.model.PrivateHireVehicleObject
import h_mal.appttude.com.model.PrivateHireVehicle
import h_mal.appttude.com.utils.isTrue
import h_mal.appttude.com.utils.setGlideImage
import h_mal.appttude.com.viewmodels.PrivateHireVehicleViewModel
import kotlinx.android.synthetic.main.fragment_private_hire_vehicle.*
class PrivateHireVehicleFragment: DataSubmissionBaseFragment
<PrivateHireVehicleViewModel, PrivateHireVehicleObject>(R.layout.fragment_private_hire_vehicle){
class PrivateHireVehicleFragment :
DataSubmissionBaseFragment<PrivateHireVehicleViewModel, FragmentPrivateHireLicenseBinding, PrivateHireVehicle>() {
private val viewmodel by getFragmentViewModel<PrivateHireVehicleViewModel>()
override fun getViewModel(): PrivateHireVehicleViewModel = viewmodel
override var model = PrivateHireVehicleObject()
override var model = PrivateHireVehicle()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ph_no.setTextOnChange{ model.phCarNumber = it }
ph_expiry.apply {
override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run {
phNo.setTextOnChange { model.phCarNumber = it }
phExpiry.apply {
setOnClickListener {
DateDialog(this) { date ->
model.phCarExpiry = date
@@ -32,24 +26,27 @@ class PrivateHireVehicleFragment: DataSubmissionBaseFragment
}
uploadphlic.setOnClickListener { openGalleryWithPermissionRequest() }
submit.setOnClickListener { submit() }
submit.setOnClickListener {
validateEditTexts(phNo, phExpiry).isTrue {
viewModel.setDataInDatabase(model, picUri)
}
}
}
override fun submit() {
super.submit()
validateEditTexts(ph_no, ph_expiry).takeIf { !it }?.let { return }
viewmodel.setDataInDatabase(model, picUri)
}
override fun setFields(data: PrivateHireVehicleObject) {
override fun setFields(data: PrivateHireVehicle) {
super.setFields(data)
applyBinding {
imageView2.setGlideImage(data.phCarImageString)
ph_no.setText(data.phCarNumber)
ph_expiry.setText(data.phCarExpiry)
phNo.setText(data.phCarNumber)
phExpiry.setText(data.phCarExpiry)
}
}
override fun onImageGalleryResult(imageUri: Uri?) {
super.onImageGalleryResult(imageUri)
applyBinding {
imageView2.setGlideImage(imageUri)
}
}
}

View File

@@ -1,62 +1,63 @@
package h_mal.appttude.com.ui.vehicleprofile
import android.os.Bundle
import android.view.View
import h_mal.appttude.com.R
import h_mal.appttude.com.base.DataSubmissionBaseFragment
import h_mal.appttude.com.databinding.FragmentVehicleSetupBinding
import h_mal.appttude.com.dialogs.DateDialog
import h_mal.appttude.com.model.VehicleProfileObject
import h_mal.appttude.com.model.VehicleProfile
import h_mal.appttude.com.utils.isTrue
import h_mal.appttude.com.viewmodels.VehicleProfileViewModel
import kotlinx.android.synthetic.main.fragment_vehicle_setup.*
class VehicleProfileFragment : DataSubmissionBaseFragment
<VehicleProfileViewModel, VehicleProfileObject>(R.layout.fragment_vehicle_setup){
<VehicleProfileViewModel, FragmentVehicleSetupBinding, VehicleProfile>() {
private val viewmodel by getFragmentViewModel<VehicleProfileViewModel>()
override fun getViewModel(): VehicleProfileViewModel = viewmodel
override var model = VehicleProfileObject()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
override var model = VehicleProfile()
override fun setupView(binding: FragmentVehicleSetupBinding) = binding.run {
reg.setTextOnChange { model.reg = it }
make.setTextOnChange { model.make = it }
car_model.setTextOnChange { model.model = it }
carModel.setTextOnChange { model.model = it }
colour.setTextOnChange { model.colour = it }
keeper_name.setTextOnChange { model.keeperName = it }
keeperName.setTextOnChange { model.keeperName = it }
address.setTextOnChange { model.keeperAddress = it }
postcode.setTextOnChange { model.keeperPostCode = it }
start_date.apply {
startDate.apply {
setOnClickListener {
DateDialog(this) { date ->
model.startDate = date
}
}
}
seized_checkbox.setOnCheckedChangeListener { _, res -> model.isSeized = res}
seizedCheckbox.setOnCheckedChangeListener { _, res -> model.isSeized = res }
submit_vehicle.setOnClickListener { submit() }
submitVehicle.setOnClickListener {
validateEditTexts(
reg,
make,
carModel,
colour,
keeperName,
address,
postcode,
startDate
).isTrue {
viewModel.setDataInDatabase(model)
}
}
}
override fun submit() {
validateEditTexts(reg, make, car_model, colour, keeper_name, address, postcode, start_date)
.takeIf { !it }
?.let { return }
viewmodel.setDataInDatabase(model)
}
override fun setFields(data: VehicleProfileObject) {
override fun setFields(data: VehicleProfile) {
super.setFields(data)
applyBinding {
reg.setText(data.reg)
make.setText(data.make)
car_model.setText(data.model)
carModel.setText(data.model)
colour.setText(data.colour)
keeper_name.setText(data.keeperName)
keeperName.setText(data.keeperName)
address.setText(data.keeperAddress)
postcode.setText(data.keeperPostCode)
start_date.setText(data.startDate)
seized_checkbox.isChecked = data.isSeized
startDate.setText(data.startDate)
seizedCheckbox.isChecked = data.isSeized
}
}
}

View File

@@ -14,27 +14,6 @@ class ArchiveFragment : Fragment() {
private var reference: DatabaseReference? = null
private var listView: ListView? = null
var archiveString: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// reference =
// MainActivity.mDatabase!!.child(FirebaseClass.USER_FIREBASE).child(
// requireArguments().getString("user_id")
// )
// .child(FirebaseClass.ARCHIVE_FIREBASE)
// archiveString = requireArguments().getString("archive")
// var s: String = ""
// when (archiveString) {
// FirebaseClass.PRIVATE_HIRE_FIREBASE -> s = "Private Hire"
// FirebaseClass.DRIVERS_LICENSE_FIREBASE -> s = "License"
// FirebaseClass.VEHICLE_DETAILS_FIREBASE -> s = "Vehicle"
// FirebaseClass.MOT_FIREBASE -> s = "M.O.T"
// FirebaseClass.INSURANCE_FIREBASE -> s = "Insurance"
// FirebaseClass.LOG_BOOK_FIREBASE -> s = "Logbook"
// FirebaseClass.PRIVATE_HIRE_VEHICLE_LICENSE -> s = "Private Hire Vehicle"
// }
// requireActivity().title = s + " Archive"
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,

View File

@@ -174,7 +174,8 @@ class ArchiveObjectListAdapter(
private fun dateString(position: Int) {
var success: Boolean = true
try {
dateArchivedText!!.text = mKeys[position].convertDateStringDatePattern("yyyyMMdd_HHmmss", "dd/MM/yyyy")
dateArchivedText!!.text =
mKeys[position].convertDateStringDatePattern("yyyyMMdd_HHmmss", "dd/MM/yyyy")
} catch (e: ParseException) {
e.printStackTrace()
success = false

View File

@@ -19,16 +19,51 @@ class ApplicationViewModelFactory(
return when {
isAssignableFrom(UserViewModel::class.java) -> UserViewModel(auth)
isAssignableFrom(MainViewModel::class.java) -> MainViewModel(auth, database)
isAssignableFrom(UpdateUserViewModel::class.java) -> UpdateUserViewModel(auth, storage)
isAssignableFrom(DriverLicenseViewModel::class.java) -> DriverLicenseViewModel(auth, database, storage)
isAssignableFrom(DriverProfileViewModel::class.java) -> DriverProfileViewModel(auth, database, storage)
isAssignableFrom(PrivateHireLicenseViewModel::class.java) -> PrivateHireLicenseViewModel(auth, database, storage)
isAssignableFrom(VehicleProfileViewModel::class.java) -> VehicleProfileViewModel(auth, database, storage)
isAssignableFrom(InsuranceViewModel::class.java) -> InsuranceViewModel(auth, database, storage)
isAssignableFrom(UpdateUserViewModel::class.java) -> UpdateUserViewModel(
auth,
storage
)
isAssignableFrom(DriverLicenseViewModel::class.java) -> DriverLicenseViewModel(
auth,
database,
storage
)
isAssignableFrom(DriverProfileViewModel::class.java) -> DriverProfileViewModel(
auth,
database,
storage
)
isAssignableFrom(PrivateHireLicenseViewModel::class.java) -> PrivateHireLicenseViewModel(
auth,
database,
storage
)
isAssignableFrom(VehicleProfileViewModel::class.java) -> VehicleProfileViewModel(
auth,
database,
storage
)
isAssignableFrom(InsuranceViewModel::class.java) -> InsuranceViewModel(
auth,
database,
storage
)
isAssignableFrom(MotViewModel::class.java) -> MotViewModel(auth, database, storage)
isAssignableFrom(LogbookViewModel::class.java) -> LogbookViewModel(auth, database, storage)
isAssignableFrom(PrivateHireVehicleViewModel::class.java) -> PrivateHireVehicleViewModel(auth, database, storage)
isAssignableFrom(RoleViewModel::class.java) -> RoleViewModel(auth, database, storage)
isAssignableFrom(LogbookViewModel::class.java) -> LogbookViewModel(
auth,
database,
storage
)
isAssignableFrom(PrivateHireVehicleViewModel::class.java) -> PrivateHireVehicleViewModel(
auth,
database,
storage
)
isAssignableFrom(RoleViewModel::class.java) -> RoleViewModel(
auth,
database,
storage
)
else -> throw IllegalArgumentException("Unknown ViewModel class")
} as T
}

View File

@@ -2,15 +2,16 @@ package h_mal.appttude.com.base
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import androidx.activity.viewModels
import android.view.ViewGroup.inflate
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelLazy
import androidx.test.espresso.IdlingResource
import androidx.viewbinding.ViewBinding
import h_mal.appttude.com.R
import h_mal.appttude.com.application.ApplicationViewModelFactory
import h_mal.appttude.com.data.ViewState
@@ -18,30 +19,65 @@ import h_mal.appttude.com.utils.*
import org.kodein.di.KodeinAware
import org.kodein.di.android.kodein
import org.kodein.di.generic.instance
import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
abstract class BaseActivity<V : BaseViewModel> : AppCompatActivity(), KodeinAware {
abstract class BaseActivity<V : BaseViewModel, VB : ViewBinding> : AppCompatActivity(), KodeinAware {
// The Idling Resource which will be null in production.
private var mIdlingResource: BasicIdlingResource? = null
private lateinit var loadingView: View
abstract fun getViewModel(): V?
abstract val layoutId: Int
private var _binding: VB? = null
private val binding: VB
get() = _binding ?: error("Must only access binding while fragment is attached.")
val viewModel: V by createLazyViewModel()
override val kodein by kodein()
val factory by instance<ApplicationViewModelFactory>()
inline fun <reified VM : ViewModel> createLazyViewModel(): Lazy<VM> = viewModels { factory }
inline fun <reified VM : ViewModel> createViewModel(): VM =
ViewModelProvider(viewModelStore, factory).get(VM::class.java)
fun createLazyViewModel(): Lazy<V> = ViewModelLazy(
getGenericClassAt(0),
{ viewModelStore },
{ factory },
{ defaultViewModelCreationExtras }
)
@Suppress("UNCHECKED_CAST")
fun <CLASS : Any> Any.getGenericClassAt(position: Int): KClass<CLASS> =
((javaClass.genericSuperclass as? ParameterizedType)
?.actualTypeArguments?.getOrNull(position) as? Class<CLASS>)
?.kotlin
?: throw IllegalStateException("Can not find class from generic argument")
fun inflateBindingByType(
genericClassAt: KClass<VB>
): VB = try {
@Suppress("UNCHECKED_CAST")
genericClassAt.java.methods.first { viewBinding ->
viewBinding.parameterTypes.size == 1
&& viewBinding.parameterTypes.getOrNull(0) == LayoutInflater::class.java
}.invoke(null, layoutInflater) as VB
} catch (exception: Exception) {
throw IllegalStateException("Can not inflate binding from generic")
}
private var loading: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
configureObserver()
setContentView(layoutId)
_binding = inflateBindingByType(getGenericClassAt(1))
setContentView(requireNotNull(_binding).root)
setupView(binding)
}
open fun setupView(binding: VB) {}
fun applyBinding(block: VB.() -> Unit) {
block(binding)
}
/**
@@ -51,8 +87,7 @@ abstract class BaseActivity<V : BaseViewModel> : AppCompatActivity(), KodeinAwar
* loading
*/
private fun instantiateLoadingView() {
// loadingView = View.inflate(this, R.layout.progress_layout, null)
loadingView = layoutInflater.inflate(R.layout.progress_layout, null)
loadingView = inflate(this, R.layout.progress_layout, null)
loadingView.setOnClickListener(null)
addContentView(loadingView, LayoutParams(MATCH_PARENT, MATCH_PARENT))
loadingView.hide()
@@ -97,7 +132,7 @@ abstract class BaseActivity<V : BaseViewModel> : AppCompatActivity(), KodeinAwar
}
private fun configureObserver() {
getViewModel()?.uiState?.observe(this) {
viewModel.uiState.observe(this) {
when (it) {
is ViewState.HasStarted -> onStarted()
is ViewState.HasData<*> -> onSuccess(it.data.getContentIfNotHandled())

View File

@@ -5,24 +5,33 @@ import android.content.ClipData
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.annotation.LayoutRes
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModel
import androidx.fragment.app.createViewModelLazy
import androidx.viewbinding.ViewBinding
import h_mal.appttude.com.application.ApplicationViewModelFactory
import h_mal.appttude.com.data.ViewState
import h_mal.appttude.com.utils.PermissionsUtils
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
const val IMAGE_SELECT_REQUEST_CODE = 401
abstract class BaseFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int) :
Fragment(contentLayoutId), KodeinAware {
abstract class BaseFragment<V : BaseViewModel, VB : ViewBinding>(
) : Fragment(), KodeinAware {
var mActivity: BaseActivity<V>? = null
abstract fun getViewModel(): V
private var _binding: VB? = null
private val binding: VB
get() = _binding ?: error("Must only access binding while fragment is attached.")
var mActivity: BaseActivity<V, *>? = null
val viewModel: V by getFragmentViewModel()
private var multipleImage: Boolean = false
@@ -33,13 +42,57 @@ abstract class BaseFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int)
override val kodein by kodein()
val factory by instance<ApplicationViewModelFactory>()
inline fun <reified VM : ViewModel> getFragmentViewModel(): Lazy<VM> = viewModels { factory }
fun getFragmentViewModel(): Lazy<V> =
createViewModelLazy(getGenericClassAt(0), { viewModelStore }, factoryProducer = { factory })
fun LayoutInflater.inflateBindingByType(
container: ViewGroup?,
genericClassAt: KClass<VB>
): VB = try {
@Suppress("UNCHECKED_CAST")
genericClassAt.java.methods.first { inflateFun ->
inflateFun.parameterTypes.size == 3
&& inflateFun.parameterTypes.getOrNull(0) == LayoutInflater::class.java
&& inflateFun.parameterTypes.getOrNull(1) == ViewGroup::class.java
&& inflateFun.parameterTypes.getOrNull(2) == Boolean::class.java
}.invoke(null, this, container, false) as VB
} catch (exception: Exception) {
throw IllegalStateException("Can not inflate binding from generic")
}
@Suppress("UNCHECKED_CAST")
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mActivity = activity as BaseActivity<V>
fun <CLASS : Any> Any.getGenericClassAt(position: Int): KClass<CLASS> =
((javaClass.genericSuperclass as? ParameterizedType)
?.actualTypeArguments?.getOrNull(position) as? Class<CLASS>)
?.kotlin
?: throw IllegalStateException("Can not find class from generic argument")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = inflater.inflateBindingByType(container, getGenericClassAt(1))
return requireNotNull(_binding).root
}
@Suppress("UNCHECKED_CAST")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mActivity = activity as BaseActivity<V, *>
configureObserver()
setupView(binding)
}
open fun setupView(binding: VB) {}
fun applyBinding(block: VB.() -> Unit) {
block(binding)
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
/**
@@ -64,7 +117,7 @@ abstract class BaseFragment<V : BaseViewModel>(@LayoutRes contentLayoutId: Int)
}
private fun configureObserver() {
getViewModel().uiState.observe(viewLifecycleOwner) {
viewModel.uiState.observe(viewLifecycleOwner) {
when (it) {
is ViewState.HasStarted -> onStarted()
is ViewState.HasData<*> -> onSuccess(it.data.getContentIfNotHandled())

View File

@@ -6,25 +6,25 @@ import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.EditText
import androidx.annotation.LayoutRes
import androidx.core.widget.doAfterTextChanged
import androidx.viewbinding.ViewBinding
import h_mal.appttude.com.data.UserAuthState
import h_mal.appttude.com.ui.user.LoginActivity
import h_mal.appttude.com.utils.PermissionsUtils.askForPermissions
import h_mal.appttude.com.utils.TextValidationUtils.validateEditText
private const val IMAGE_PERMISSION_RESULT = 402
abstract class DataSubmissionBaseFragment<V : DataSubmissionBaseViewModel<T>, T: Any>
(@LayoutRes contentLayoutId: Int) : BaseFragment<BaseViewModel>(contentLayoutId){
abstract class DataSubmissionBaseFragment<V : DataSubmissionBaseViewModel<T>, VB : ViewBinding, T : Any> :
BaseFragment<V, VB>() {
var picUri: Uri? = null
abstract override fun getViewModel(): V
abstract var model: T
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
getViewModel().stateLiveData.observe(viewLifecycleOwner) {
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)
@@ -32,7 +32,7 @@ abstract class DataSubmissionBaseFragment<V : DataSubmissionBaseViewModel<T>, T:
requireActivity().finish()
}
}
getViewModel().getDataFromDatabase()
viewModel.getDataFromDatabase()
}
@Suppress("UNCHECKED_CAST")

View File

@@ -86,5 +86,14 @@ abstract class DataSubmissionBaseViewModel<T : Any>(
suspend fun <T, R> Iterable<T>.mapIndexSuspend(transform: suspend (index: Int, T) -> R) =
coroutineScope { mapIndexed { index: Int, t: T -> async { transform(index, t) } }.map { it.await() } }
coroutineScope {
mapIndexed { index: Int, t: T ->
async {
transform(
index,
t
)
}
}.map { it.await() }
}
}

View File

@@ -1,7 +0,0 @@
package h_mal.appttude.com.data
sealed class DataFieldState {
object DefaultState : DataFieldState()
object NonUserStateUpdated: DataFieldState()
object UserUpdateState: DataFieldState()
}

View File

@@ -42,12 +42,14 @@ class FirebaseAuthSource: FirebaseAuthentication{
}
override fun updateEmail(email: String): Task<Void> = getCurrentUser().updateEmail(email)
override fun updatePassword(password: String): Task<Void> = getCurrentUser().updatePassword(password)
override fun updatePassword(password: String): Task<Void> =
getCurrentUser().updatePassword(password)
override fun deleteProfile(): Task<Void> = getCurrentUser().delete()
override fun userStateListener() : FirebaseLiveData {
return FirebaseLiveData(auth)
override fun userStateListener(): FirebaseAuthStateLiveData {
return FirebaseAuthStateLiveData(auth)
}
private fun getCurrentUser(): FirebaseUser {

View File

@@ -3,7 +3,10 @@ package h_mal.appttude.com.data
import androidx.lifecycle.LiveData
import com.google.firebase.auth.FirebaseAuth
class FirebaseLiveData(
/**
* Creates #LiveDate out of {UserAuthState} for firebase user state
*/
class FirebaseAuthStateLiveData(
private val firebaseAuth: FirebaseAuth
) : LiveData<UserAuthState>() {

View File

@@ -16,12 +16,14 @@ interface FirebaseAuthentication{
name: String?,
profilePic: Uri?
): Task<Void>
fun reauthenticate(
email: String,
password: String
): Task<Void>
fun updateEmail(email: String): Task<Void>
fun updatePassword(password: String): Task<Void>
fun deleteProfile(): Task<Void>
fun userStateListener() : FirebaseLiveData
fun userStateListener(): FirebaseAuthStateLiveData
}

View File

@@ -20,9 +20,16 @@ const val MOT = "mot_details"
const val PRIVATE_HIRE_VEHICLE = "private_hire_vehicle"
const val VEHICLE_DETAILS = "vehicle_details"
const val ARCHIVE = "archive"
class FirebaseDatabaseSource {
private val database = FirebaseDatabase.getInstance()
/**
* Post object to the databse on reference
*
* @param ref - Database reference
* @return T returns data posted
*/
suspend fun <T : Any> postToDatabaseRed(ref: DatabaseReference, data: T): T {
ref.setValue(data).await()
return data
@@ -52,6 +59,8 @@ class FirebaseDatabaseSource {
fun getArchiveLogbookRef(uid: String) = getArchiveRef(uid).child(LOG_BOOK)
fun getArchiveMotDetailsRef(uid: String) = getArchiveRef(uid).child(MOT)
fun getArchivePrivateHireLicenseRef(uid: String) = getArchiveRef(uid).child(PRIVATE_HIRE)
fun getArchivePrivateHireVehicleRef(uid: String) = getArchiveRef(uid).child(PRIVATE_HIRE_VEHICLE)
fun getArchivePrivateHireVehicleRef(uid: String) =
getArchiveRef(uid).child(PRIVATE_HIRE_VEHICLE)
fun getArchiveVehicleDetailsRef(uid: String) = getArchiveRef(uid).child(VEHICLE_DETAILS)
}

View File

@@ -13,6 +13,7 @@ const val LOG_BOOK_SREF = "log_book"
const val MOT_SREF = "mot_Details"
const val PRIVATE_HIRE_SREF = "private_hire"
const val PRIVATE_HIRE_VEHICLE_SREF = "private_hire_vehicle"
class FirebaseStorageSource {
private val storage = FirebaseStorage.getInstance()
private val storageRef: StorageReference by lazy { storage.reference }
@@ -26,10 +27,13 @@ class FirebaseStorageSource {
private fun usersImagesStorageRef(uid: String) = storageRef.child(IMAGE_CONST).child(uid)
fun profileImageStorageRef(uid: String) = usersImagesStorageRef(uid).child(PROFILE_SREF)
fun driversLicenseStorageRef(uid: String) = usersImagesStorageRef(uid).child(DRIVERS_LICENSE_SREF)
fun driversLicenseStorageRef(uid: String) =
usersImagesStorageRef(uid).child(DRIVERS_LICENSE_SREF)
fun insuranceStorageRef(uid: String) = usersImagesStorageRef(uid).child(INSURANCE_SREF)
fun logBookStorageRef(uid: String) = usersImagesStorageRef(uid).child(LOG_BOOK_SREF)
fun motStorageRef(uid: String) = usersImagesStorageRef(uid).child(MOT_SREF)
fun privateHireStorageRef(uid: String) = usersImagesStorageRef(uid).child(PRIVATE_HIRE_SREF)
fun privateHireVehicleStorageRef(uid: String) = usersImagesStorageRef(uid).child(PRIVATE_HIRE_VEHICLE_SREF)
fun privateHireVehicleStorageRef(uid: String) =
usersImagesStorageRef(uid).child(PRIVATE_HIRE_VEHICLE_SREF)
}

View File

@@ -1,7 +1,6 @@
package h_mal.appttude.com.dialogs
import android.app.AlertDialog
import android.app.DatePickerDialog
import android.app.DatePickerDialog.OnDateSetListener
import android.icu.util.Calendar
@@ -11,11 +10,12 @@ import h_mal.appttude.com.utils.DateUtils
private const val DATE_FORMAT = "dd/MM/yyyy"
@Suppress("DEPRECATION")
class DateDialog(
private val editText: EditText,
dateSelected: (String?) -> Unit
) : DatePickerDialog(editText.context, AlertDialog.THEME_HOLO_LIGHT) {
) : DatePickerDialog(editText.context) {
private val dateSetListener: OnDateSetListener =
OnDateSetListener { _, year, month, dayOfMonth ->
@@ -29,6 +29,10 @@ class DateDialog(
}
init {
datePicker.apply {
spinnersShown = true
calendarViewShown = false
}
val dateString = editText.text?.toString()
val date = if (dateString.isNullOrBlank()) {
// Set time to now
@@ -52,5 +56,4 @@ class DateDialog(
updateDate(mYear, mMonth, mDay)
}
}

View File

@@ -2,14 +2,14 @@ package h_mal.appttude.com.dialogs
import android.app.Activity
import android.app.AlertDialog
import androidx.fragment.app.Fragment
import h_mal.appttude.com.R
import kotlin.system.exitProcess
object ExitDialog {
fun Activity.displayExitDialog() = AlertDialog.Builder(this)
.setTitle("Leave?")
.setMessage("Are you sure you want to exit?")
.setTitle(getString(R.string.leave_header))
.setMessage(getString(R.string.leave_message))
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(
android.R.string.ok
@@ -20,5 +20,4 @@ object ExitDialog{
.create()
.show()
fun Fragment.displayExitDialog() = requireActivity().displayExitDialog()
}

View File

@@ -1,8 +1,6 @@
package h_mal.appttude.com.model
data class DriverProfileObject(
data class DriverProfile(
var driverPic: String? = null,
var forenames: String? = null,
var address: String? = null,

View File

@@ -1,7 +1,7 @@
package h_mal.appttude.com.model
data class DriversLicenseObject(
data class DriversLicense(
var licenseImageString: String? = null,
var licenseNumber: String? = null,
var licenseExpiry: String? = null

View File

@@ -1,8 +1,6 @@
package h_mal.appttude.com.model
data class InsuranceObject (
data class Insurance(
var photoStrings: MutableList<String?>? = null,
var insurerName: String? = null,
var expiryDate: String? = null

View File

@@ -1,8 +1,7 @@
package h_mal.appttude.com.model
data class LogbookObject(
data class Logbook(
var photoString: String? = null,
var v5cnumber: String? = null
)

View File

@@ -1,8 +1,7 @@
package h_mal.appttude.com.model
data class MotObject(
data class Mot(
var motImageString: String? = null,
var motExpiry: String? = null
)

View File

@@ -1,8 +1,7 @@
package h_mal.appttude.com.model
data class PrivateHireObject (
data class PrivateHireLicense(
var phImageString: String? = null,
var phNumber: String? = null,
var phExpiry: String? = null

View File

@@ -1,8 +1,7 @@
package h_mal.appttude.com.model
class PrivateHireVehicleObject(
class PrivateHireVehicle(
var phCarImageString: String? = null,
var phCarNumber: String? = null,
var phCarExpiry: String? = null

View File

@@ -1,8 +1,7 @@
package h_mal.appttude.com.model
data class VehicleProfileObject(
data class VehicleProfile(
var reg: String? = null,
var make: String? = null,
var model: String? = null,

View File

@@ -1,33 +1,25 @@
package h_mal.appttude.com.ui.update
import android.os.Bundle
import android.view.View
import androidx.fragment.app.activityViewModels
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseFragment
import h_mal.appttude.com.databinding.FragmentDeleteProfileBinding
import h_mal.appttude.com.utils.TextValidationUtils.validatePasswordEditText
import h_mal.appttude.com.utils.setEnterPressedListener
import h_mal.appttude.com.viewmodels.UpdateUserViewModel
import kotlinx.android.synthetic.main.fragment_delete_profile.*
class DeleteProfileFragment : BaseFragment<UpdateUserViewModel>(R.layout.fragment_delete_profile) {
class DeleteProfileFragment :
BaseFragment<UpdateUserViewModel, FragmentDeleteProfileBinding>() {
private val viewmodel: UpdateUserViewModel by activityViewModels()
override fun getViewModel(): UpdateUserViewModel = viewmodel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
password_top.setEnterPressedListener { deleteUser() }
submission_button_label.setOnClickListener { deleteUser() }
override fun setupView(binding: FragmentDeleteProfileBinding) = binding.run {
passwordTop.setEnterPressedListener { deleteUser() }
submissionButtonLabel.setOnClickListener { deleteUser() }
}
private fun deleteUser(){
val emailString = email_update.validatePasswordEditText() ?: return
val passwordText = password_top.validatePasswordEditText() ?: return
private fun deleteUser() = applyBinding {
val emailString = emailUpdate.validatePasswordEditText() ?: return@applyBinding
val passwordText = passwordTop.validatePasswordEditText() ?: return@applyBinding
getViewModel().deleteProfile(emailString, passwordText)
viewModel.deleteProfile(emailString, passwordText)
}
}

View File

@@ -1,21 +1,12 @@
package h_mal.appttude.com.ui.update
import android.os.Bundle
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseActivity
import h_mal.appttude.com.data.FirebaseCompletion
import h_mal.appttude.com.databinding.UpdateActivityBinding
import h_mal.appttude.com.utils.displayToast
import h_mal.appttude.com.viewmodels.UpdateUserViewModel
class UpdateActivity : BaseActivity<UpdateUserViewModel>() {
override val layoutId: Int = R.layout.update_activity
override fun getViewModel(): UpdateUserViewModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
createViewModel<UpdateUserViewModel>()
}
class UpdateActivity : BaseActivity<UpdateUserViewModel, UpdateActivityBinding>() {
override fun onSuccess(data: Any?) {
super.onSuccess(data)

View File

@@ -1,36 +1,28 @@
package h_mal.appttude.com.ui.update
import android.os.Bundle
import android.view.View
import androidx.fragment.app.activityViewModels
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseFragment
import h_mal.appttude.com.databinding.FragmentUpdateEmailBinding
import h_mal.appttude.com.utils.TextValidationUtils.validateEmailEditText
import h_mal.appttude.com.utils.TextValidationUtils.validatePasswordEditText
import h_mal.appttude.com.utils.setEnterPressedListener
import h_mal.appttude.com.viewmodels.UpdateUserViewModel
import kotlinx.android.synthetic.main.fragment_update_email.*
class UpdateEmailFragment : BaseFragment<UpdateUserViewModel>(R.layout.fragment_update_email) {
class UpdateEmailFragment : BaseFragment<UpdateUserViewModel, FragmentUpdateEmailBinding>() {
private val viewmodel: UpdateUserViewModel by activityViewModels()
override fun getViewModel(): UpdateUserViewModel = viewmodel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
new_email.setEnterPressedListener { registerUser() }
submission_button_label.setOnClickListener { registerUser() }
override fun setupView(binding: FragmentUpdateEmailBinding) = binding.run {
newEmail.setEnterPressedListener { registerUser() }
submissionButtonLabel.setOnClickListener { registerUser() }
}
private fun registerUser() {
val emailString = email_update.validatePasswordEditText() ?: return
val passwordText = password_top.validatePasswordEditText() ?: return
val newEmail = new_email.validateEmailEditText() ?: return
applyBinding {
val emailString = emailUpdate.validatePasswordEditText() ?: return@applyBinding
val passwordText = passwordTop.validatePasswordEditText() ?: return@applyBinding
val newEmail = newEmail.validateEmailEditText() ?: return@applyBinding
getViewModel().updateEmail(emailString, passwordText, newEmail)
viewModel.updateEmail(emailString, passwordText, newEmail)
}
}
}

View File

@@ -1,28 +1,23 @@
package h_mal.appttude.com.ui.update
import android.os.Bundle
import android.view.View
import androidx.fragment.app.activityViewModels
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseFragment
import h_mal.appttude.com.databinding.UpdateOverviewFragmentBinding
import h_mal.appttude.com.utils.navigateTo
import h_mal.appttude.com.viewmodels.UpdateUserViewModel
import kotlinx.android.synthetic.main.update_overview_fragment.*
class UpdateOverviewFragment : BaseFragment<UpdateUserViewModel>(R.layout.update_overview_fragment), View.OnClickListener {
class UpdateOverviewFragment : BaseFragment<UpdateUserViewModel, UpdateOverviewFragmentBinding>(),
View.OnClickListener {
private val vm by activityViewModels<UpdateUserViewModel>()
override fun getViewModel(): UpdateUserViewModel = vm
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
update_email_button.setOnClickListener(this)
update_password_button.setOnClickListener(this)
update_profile_button.setOnClickListener(this)
delete_profile.setOnClickListener(this)
override fun setupView(binding: UpdateOverviewFragmentBinding) = binding.run {
updateEmailButton.setOnClickListener(this@UpdateOverviewFragment)
updatePasswordButton.setOnClickListener(this@UpdateOverviewFragment)
updateProfileButton.setOnClickListener(this@UpdateOverviewFragment)
deleteProfile.setOnClickListener(this@UpdateOverviewFragment)
}
private fun View.submit() {
when (id) {
R.id.update_email_button -> navigateTo(R.id.to_updateEmailFragment)
@@ -36,5 +31,4 @@ class UpdateOverviewFragment : BaseFragment<UpdateUserViewModel>(R.layout.update
v?.submit()
}
}

View File

@@ -1,36 +1,30 @@
package h_mal.appttude.com.ui.update
import android.os.Bundle
import android.view.View
import androidx.fragment.app.activityViewModels
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseFragment
import h_mal.appttude.com.databinding.FragmentUpdatePasswordBinding
import h_mal.appttude.com.utils.TextValidationUtils.validateEmailEditText
import h_mal.appttude.com.utils.TextValidationUtils.validatePasswordEditText
import h_mal.appttude.com.utils.setEnterPressedListener
import h_mal.appttude.com.viewmodels.UpdateUserViewModel
import kotlinx.android.synthetic.main.fragment_update_password.*
class UpdatePasswordFragment :
BaseFragment<UpdateUserViewModel>(R.layout.fragment_update_password) {
class UpdatePasswordFragment : BaseFragment<UpdateUserViewModel, FragmentUpdatePasswordBinding>() {
private val viewmodel: UpdateUserViewModel by activityViewModels()
override fun getViewModel(): UpdateUserViewModel = viewmodel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
email_update.setEnterPressedListener { registerUser() }
email_sign_up.setOnClickListener { registerUser() }
override fun setupView(binding: FragmentUpdatePasswordBinding) {
applyBinding {
emailUpdate.setEnterPressedListener { registerUser() }
emailSignUp.setOnClickListener { registerUser() }
}
}
private fun registerUser() {
val emailString = email_update.validatePasswordEditText() ?: return
val passwordText = password_top.validatePasswordEditText() ?: return
val newPassword = password_bottom.validateEmailEditText() ?: return
applyBinding {
val emailString = emailUpdate.validatePasswordEditText() ?: return@applyBinding
val passwordText = passwordTop.validatePasswordEditText() ?: return@applyBinding
val newPassword = passwordBottom.validateEmailEditText() ?: return@applyBinding
getViewModel().updatePassword(emailString, passwordText, newPassword)
viewModel.updatePassword(emailString, passwordText, newPassword)
}
}
}

View File

@@ -2,39 +2,29 @@ package h_mal.appttude.com.ui.update
import android.Manifest.permission.READ_EXTERNAL_STORAGE
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.activityViewModels
import com.google.firebase.auth.FirebaseUser
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseFragment
import h_mal.appttude.com.databinding.FragmentUpdateProfileBinding
import h_mal.appttude.com.utils.PermissionsUtils.askForPermissions
import h_mal.appttude.com.utils.setEnterPressedListener
import h_mal.appttude.com.utils.setGlideImage
import h_mal.appttude.com.viewmodels.UpdateUserViewModel
import kotlinx.android.synthetic.main.fragment_update_profile.*
const val TAG_CONST = "non-user"
private const val IMAGE_PERMISSION_RESULT = 402
class UpdateProfileFragment : BaseFragment<UpdateUserViewModel>(R.layout.fragment_update_profile) {
private val viewmodel: UpdateUserViewModel by activityViewModels()
override fun getViewModel(): UpdateUserViewModel = viewmodel
class UpdateProfileFragment : BaseFragment<UpdateUserViewModel, FragmentUpdateProfileBinding>() {
private var imageChangeListener: Boolean = false
private var nameChangeListener: Boolean = false
private var imageUri: Uri? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
override fun setupView(binding: FragmentUpdateProfileBinding) = binding.run {
viewModel.getUser()
viewmodel.getUser()
update_name.apply {
updateName.apply {
doAfterTextChanged {
if (tag == TAG_CONST) {
tag = null
@@ -45,20 +35,22 @@ class UpdateProfileFragment : BaseFragment<UpdateUserViewModel>(R.layout.fragmen
setEnterPressedListener { submitProfileUpdate() }
}
profile_img.setOnClickListener {
profileImg.setOnClickListener {
if (askForPermissions(READ_EXTERNAL_STORAGE, IMAGE_PERMISSION_RESULT)) {
openGalleryForImage()
}
}
submit_update_profile.setOnClickListener { submitProfileUpdate() }
submitUpdateProfile.setOnClickListener { submitProfileUpdate() }
}
private fun submitProfileUpdate() {
val name: String? = takeIf { nameChangeListener }?.update_name?.text?.toString()
applyBinding {
val name: String? = takeIf { nameChangeListener }?.updateName?.text?.toString()
val imgUri = takeIf { imageChangeListener }?.let { imageUri }
viewmodel.updateProfile(name, imgUri)
viewModel.updateProfile(name, imgUri)
}
}
override fun onRequestPermissionsResult(
@@ -76,18 +68,24 @@ class UpdateProfileFragment : BaseFragment<UpdateUserViewModel>(R.layout.fragmen
}
private fun setFields(firebaseUser: FirebaseUser) {
profile_img.setGlideImage(firebaseUser.photoUrl)
update_name.apply {
applyBinding {
profileImg.setGlideImage(firebaseUser.photoUrl)
updateName.apply {
setText(firebaseUser.displayName)
tag = TAG_CONST
}
}
}
override fun onImageGalleryResult(imageUri: Uri?) {
super.onImageGalleryResult(imageUri)
this.imageUri = imageUri
profile_img.setGlideImage(imageUri)
applyBinding {
profileImg.setGlideImage(imageUri)
imageChangeListener = true
}
}
}

View File

@@ -1,26 +1,17 @@
package h_mal.appttude.com.ui.user
import android.os.Bundle
import android.view.View
import androidx.fragment.app.activityViewModels
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseFragment
import h_mal.appttude.com.databinding.FragmentForgotPasswordBinding
import h_mal.appttude.com.utils.TextValidationUtils.validateEmailEditText
import h_mal.appttude.com.viewmodels.UserViewModel
import kotlinx.android.synthetic.main.fragment_forgot_password.*
class ForgotPasswordFragment : BaseFragment<UserViewModel>(R.layout.fragment_forgot_password) {
class ForgotPasswordFragment : BaseFragment<UserViewModel, FragmentForgotPasswordBinding>() {
private val userViewModel: UserViewModel by activityViewModels()
override fun getViewModel(): UserViewModel = userViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
submission_button.setOnClickListener {
val emailString = submission_et.validateEmailEditText() ?: return@setOnClickListener
userViewModel.forgotPassword(emailString)
override fun setupView(binding: FragmentForgotPasswordBinding) = binding.run {
submissionButton.setOnClickListener {
val emailString = submissionEt.validateEmailEditText() ?: return@setOnClickListener
viewModel.forgotPassword(emailString)
}
}

View File

@@ -2,11 +2,10 @@ package h_mal.appttude.com.ui.user
import android.content.Intent
import android.os.Bundle
import com.google.firebase.auth.AuthResult
import com.google.firebase.auth.FirebaseUser
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseActivity
import h_mal.appttude.com.databinding.ActivityLoginBinding
import h_mal.appttude.com.ui.MainActivity
import h_mal.appttude.com.viewmodels.UserViewModel
@@ -14,15 +13,7 @@ import h_mal.appttude.com.viewmodels.UserViewModel
/**
* A login screen that offers login via email/password.
*/
class LoginActivity : BaseActivity<UserViewModel>() {
override fun getViewModel(): UserViewModel? = null
override val layoutId: Int = R.layout.activity_login
override fun onCreate(savedInstanceState: Bundle?) {
createViewModel<UserViewModel>()
super.onCreate(savedInstanceState)
}
class LoginActivity : BaseActivity<UserViewModel, ActivityLoginBinding>() {
override fun onSuccess(data: Any?) {
super.onSuccess(data)

View File

@@ -1,39 +1,32 @@
package h_mal.appttude.com.ui.user
import android.os.Bundle
import android.view.View
import androidx.fragment.app.activityViewModels
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseFragment
import h_mal.appttude.com.databinding.FragmentLoginBinding
import h_mal.appttude.com.utils.TextValidationUtils.validateEmailEditText
import h_mal.appttude.com.utils.TextValidationUtils.validatePasswordEditText
import h_mal.appttude.com.utils.navigateTo
import h_mal.appttude.com.utils.setEnterPressedListener
import h_mal.appttude.com.viewmodels.UserViewModel
import kotlinx.android.synthetic.main.fragment_login.*
class LoginFragment : BaseFragment<UserViewModel>(R.layout.fragment_login) {
private val userViewModel: UserViewModel by activityViewModels()
override fun getViewModel(): UserViewModel = userViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
class LoginFragment : BaseFragment<UserViewModel, FragmentLoginBinding>() {
override fun setupView(binding: FragmentLoginBinding) = binding.run {
password.setEnterPressedListener { attemptLogin() }
email_sign_in_button.setOnClickListener { attemptLogin() }
register_button.setOnClickListener { it.navigateTo(R.id.to_register) }
emailSignInButton.setOnClickListener { attemptLogin() }
registerButton.setOnClickListener { it.navigateTo(R.id.to_register) }
forgot.setOnClickListener { it.navigateTo(R.id.to_forgotPassword) }
}
private fun attemptLogin() {
applyBinding {
// Store values at the time of the login attempt.
val emailString = email.validateEmailEditText() ?: return
val passwordString = password.validatePasswordEditText() ?: return
val emailString = email.validateEmailEditText() ?: return@applyBinding
val passwordString = password.validatePasswordEditText() ?: return@applyBinding
// attempt to login user
getViewModel().signInUser(emailString, passwordString)
viewModel.signInUser(emailString, passwordString)
}
}
}

View File

@@ -1,42 +1,37 @@
package h_mal.appttude.com.ui.user
import android.os.Bundle
import android.view.View
import androidx.fragment.app.activityViewModels
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseFragment
import h_mal.appttude.com.databinding.FragmentRegisterBinding
import h_mal.appttude.com.utils.TextValidationUtils.validateEmailEditText
import h_mal.appttude.com.utils.TextValidationUtils.validatePasswordEditText
import h_mal.appttude.com.utils.setEnterPressedListener
import h_mal.appttude.com.viewmodels.UserViewModel
import kotlinx.android.synthetic.main.fragment_register.*
class RegisterFragment : BaseFragment<UserViewModel>(R.layout.fragment_register) {
class RegisterFragment :
BaseFragment<UserViewModel, FragmentRegisterBinding>() {
override fun getViewModel(): UserViewModel {
val userViewModel: UserViewModel by activityViewModels()
return userViewModel
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
password_bottom.setEnterPressedListener { registerUser() }
email_sign_up.setOnClickListener { registerUser() }
override fun setupView(binding: FragmentRegisterBinding) = binding.run {
passwordBottom.setEnterPressedListener { registerUser() }
emailSignUp.setOnClickListener { registerUser() }
}
private fun registerUser() {
val nameString = name_register.validatePasswordEditText() ?: return
val emailText = email_register.validateEmailEditText() ?: return
val passwordText = password_top.validatePasswordEditText() ?: return
val passwordTextBottom = password_bottom.validatePasswordEditText() ?: return
applyBinding {
val nameString = nameRegister.validatePasswordEditText() ?: return@applyBinding
val emailText = emailRegister.validateEmailEditText() ?: return@applyBinding
val passwordText = passwordTop.validatePasswordEditText() ?: return@applyBinding
val passwordTextBottom =
passwordBottom.validatePasswordEditText() ?: return@applyBinding
if ((passwordText != passwordTextBottom)) {
password_bottom.error = getString(R.string.no_match_password)
password_bottom.requestFocus()
return
passwordBottom.error = getString(R.string.no_match_password)
passwordBottom.requestFocus()
return@applyBinding
}
viewModel.registerUser(nameString, emailText, passwordText)
}
getViewModel().registerUser(nameString, emailText, passwordText)
}
}

View File

@@ -2,23 +2,20 @@ package h_mal.appttude.com.ui.user
import android.os.Bundle
import android.view.View
import androidx.fragment.app.activityViewModels
import h_mal.appttude.com.R
import h_mal.appttude.com.base.BaseFragment
import h_mal.appttude.com.data.FirebaseCompletion
import h_mal.appttude.com.databinding.SplashScreenBinding
import h_mal.appttude.com.utils.navigateTo
import h_mal.appttude.com.viewmodels.UserViewModel
class SplashScreenFragment : BaseFragment<UserViewModel>(R.layout.fragment_splash_screen) {
private val userViewModel by activityViewModels<UserViewModel>()
override fun getViewModel(): UserViewModel = userViewModel
class SplashScreenFragment : BaseFragment<UserViewModel, SplashScreenBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
userViewModel.splashscreenCheckUserIsLoggedIn()
viewModel.splashscreenCheckUserIsLoggedIn()
}
override fun onSuccess(data: Any?) {

View File

@@ -0,0 +1,16 @@
package h_mal.appttude.com.utils
inline fun Boolean.isTrue(block: () -> Unit){
if (this) {
block()
}
}
inline fun <T, R> T.isNotNull(block: (T) -> R): R?{
return if (this != null) {
block(this)
} else {
null
}
}

View File

@@ -8,8 +8,12 @@ import h_mal.appttude.com.data.EventResponse
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
/**
* Read database reference once {@link #DatabaseReference.addListenerForSingleValueEvent}
*
*
* @return EventResponse
*/
suspend fun DatabaseReference.singleValueEvent(): EventResponse = suspendCoroutine { continuation ->
val valueEventListener = object : ValueEventListener {
override fun onCancelled(error: DatabaseError) {
@@ -23,6 +27,12 @@ suspend fun DatabaseReference.singleValueEvent(): EventResponse = suspendCorouti
addListenerForSingleValueEvent(valueEventListener)
}
/**
* Read database reference once {@link #DatabaseReference.addListenerForSingleValueEvent}
*
*
* @return T
*/
suspend inline fun <reified T : Any> DatabaseReference.getDataFromDatabaseRef(): T? {
return when (val response: EventResponse = singleValueEvent()) {
is EventResponse.Changed -> {

View File

@@ -103,6 +103,7 @@ fun ImageView.setPicassoImage(
context?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
}
override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {}
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {}
})

View File

@@ -7,22 +7,22 @@ import h_mal.appttude.com.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.data.FirebaseAuthentication
import h_mal.appttude.com.data.FirebaseDatabaseSource
import h_mal.appttude.com.data.FirebaseStorageSource
import h_mal.appttude.com.model.DriversLicenseObject
import h_mal.appttude.com.model.DriversLicense
import h_mal.appttude.com.utils.Coroutines.io
class DriverLicenseViewModel(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource,
storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<DriversLicenseObject>(auth, database, storage) {
) : DataSubmissionBaseViewModel<DriversLicense>(auth, database, storage) {
override val databaseRef: DatabaseReference = database.getDriverLicenseRef(uid)
override val storageRef: StorageReference = storage.driversLicenseStorageRef(uid)
override val objectName: String = "drivers license"
override fun getDataFromDatabase() = getDataClass<DriversLicenseObject>()
override fun getDataFromDatabase() = getDataClass<DriversLicense>()
override fun setDataInDatabase(data: DriversLicenseObject, localImageUri: Uri?) = io {
override fun setDataInDatabase(data: DriversLicense, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") {
val imageUrl = getImageUrl(localImageUri, data.licenseImageString)
data.licenseImageString = imageUrl

View File

@@ -7,22 +7,22 @@ import h_mal.appttude.com.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.data.FirebaseAuthentication
import h_mal.appttude.com.data.FirebaseDatabaseSource
import h_mal.appttude.com.data.FirebaseStorageSource
import h_mal.appttude.com.model.DriverProfileObject
import h_mal.appttude.com.model.DriverProfile
import h_mal.appttude.com.utils.Coroutines.io
class DriverProfileViewModel(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource,
storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<DriverProfileObject>(auth, database, storage) {
) : DataSubmissionBaseViewModel<DriverProfile>(auth, database, storage) {
override val databaseRef: DatabaseReference = database.getDriverDetailsRef(uid)
override val storageRef: StorageReference = storage.profileImageStorageRef(uid)
override val objectName: String = "drivers profile"
override fun getDataFromDatabase() = getDataClass<DriverProfileObject>()
override fun getDataFromDatabase() = getDataClass<DriverProfile>()
override fun setDataInDatabase(data: DriverProfileObject, localImageUri: Uri?) = io {
override fun setDataInDatabase(data: DriverProfile, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") {
val imageUrl = getImageUrl(localImageUri, data.driverPic)

View File

@@ -7,22 +7,22 @@ import h_mal.appttude.com.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.data.FirebaseAuthentication
import h_mal.appttude.com.data.FirebaseDatabaseSource
import h_mal.appttude.com.data.FirebaseStorageSource
import h_mal.appttude.com.model.InsuranceObject
import h_mal.appttude.com.model.Insurance
import h_mal.appttude.com.utils.Coroutines.io
class InsuranceViewModel(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource,
storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<InsuranceObject>(auth, database, storage) {
) : DataSubmissionBaseViewModel<Insurance>(auth, database, storage) {
override val databaseRef: DatabaseReference = database.getInsuranceDetailsRef(uid)
override val storageRef: StorageReference = storage.insuranceStorageRef(uid)
override val objectName: String = "insurance"
override fun getDataFromDatabase() = getDataClass<InsuranceObject>()
override fun getDataFromDatabase() = getDataClass<Insurance>()
override fun setDataInDatabase(data: InsuranceObject, localImageUris: List<Uri?>?) = io {
override fun setDataInDatabase(data: Insurance, localImageUris: List<Uri?>?) = io {
doTryOperation("Failed to upload $objectName") {
val imageUrls = if (!localImageUris.isNullOrEmpty()) {
getImageUrls(localImageUris).toMutableList()

View File

@@ -7,22 +7,22 @@ import h_mal.appttude.com.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.data.FirebaseAuthentication
import h_mal.appttude.com.data.FirebaseDatabaseSource
import h_mal.appttude.com.data.FirebaseStorageSource
import h_mal.appttude.com.model.LogbookObject
import h_mal.appttude.com.model.Logbook
import h_mal.appttude.com.utils.Coroutines.io
class LogbookViewModel(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource,
storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<LogbookObject>(auth, database, storage) {
) : DataSubmissionBaseViewModel<Logbook>(auth, database, storage) {
override val databaseRef: DatabaseReference = database.getLogbookRef(uid)
override val storageRef: StorageReference = storage.logBookStorageRef(uid)
override val objectName: String = "Log book"
override fun getDataFromDatabase() = getDataClass<LogbookObject>()
override fun getDataFromDatabase() = getDataClass<Logbook>()
override fun setDataInDatabase(data: LogbookObject, localImageUri: Uri?) = io {
override fun setDataInDatabase(data: Logbook, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") {
val imageUrl = getImageUrl(localImageUri, data.photoString)
data.photoString = imageUrl

View File

@@ -7,22 +7,22 @@ import h_mal.appttude.com.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.data.FirebaseAuthentication
import h_mal.appttude.com.data.FirebaseDatabaseSource
import h_mal.appttude.com.data.FirebaseStorageSource
import h_mal.appttude.com.model.MotObject
import h_mal.appttude.com.model.Mot
import h_mal.appttude.com.utils.Coroutines.io
class MotViewModel(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource,
storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<MotObject>(auth, database, storage) {
) : DataSubmissionBaseViewModel<Mot>(auth, database, storage) {
override val databaseRef: DatabaseReference = database.getMotDetailsRef(uid)
override val storageRef: StorageReference? = storage.motStorageRef(uid)
override val objectName: String = "vehicle profile"
override fun getDataFromDatabase() = getDataClass<MotObject>()
override fun getDataFromDatabase() = getDataClass<Mot>()
override fun setDataInDatabase(data: MotObject, localImageUri: Uri?) = io {
override fun setDataInDatabase(data: Mot, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") {
val imageUrl = getImageUrl(localImageUri, data.motImageString)
data.motImageString = imageUrl

View File

@@ -7,25 +7,25 @@ import h_mal.appttude.com.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.data.FirebaseAuthentication
import h_mal.appttude.com.data.FirebaseDatabaseSource
import h_mal.appttude.com.data.FirebaseStorageSource
import h_mal.appttude.com.model.PrivateHireObject
import h_mal.appttude.com.model.PrivateHireLicense
import h_mal.appttude.com.utils.Coroutines.io
class PrivateHireLicenseViewModel(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource,
storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<PrivateHireObject>(auth, database, storage) {
) : DataSubmissionBaseViewModel<PrivateHireLicense>(auth, database, storage) {
override val databaseRef: DatabaseReference = database.getPrivateHireRef(uid)
override val storageRef: StorageReference = storage.privateHireStorageRef(uid)
override val objectName: String = "private hire license"
override fun getDataFromDatabase() = getDataClass<PrivateHireObject>()
override fun getDataFromDatabase() = getDataClass<PrivateHireLicense>()
override fun setDataInDatabase(data: PrivateHireObject, localImageUri: Uri?) = io {
override fun setDataInDatabase(data: PrivateHireLicense, localImageUri: Uri?) = io {
doTryOperation("Failed to upload private hire license") {
val imageUrl = getImageUrl(localImageUri, data.phImageString)
val driverLicense = PrivateHireObject(
val driverLicense = PrivateHireLicense(
phExpiry = data.phExpiry,
phNumber = data.phNumber,
phImageString = imageUrl

View File

@@ -7,22 +7,22 @@ import h_mal.appttude.com.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.data.FirebaseAuthentication
import h_mal.appttude.com.data.FirebaseDatabaseSource
import h_mal.appttude.com.data.FirebaseStorageSource
import h_mal.appttude.com.model.PrivateHireVehicleObject
import h_mal.appttude.com.model.PrivateHireVehicle
import h_mal.appttude.com.utils.Coroutines.io
class PrivateHireVehicleViewModel(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource,
storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<PrivateHireVehicleObject>(auth, database, storage) {
) : DataSubmissionBaseViewModel<PrivateHireVehicle>(auth, database, storage) {
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() = getDataClass<PrivateHireVehicleObject>()
override fun getDataFromDatabase() = getDataClass<PrivateHireVehicle>()
override fun setDataInDatabase(data: PrivateHireVehicleObject, localImageUri: Uri?) = io {
override fun setDataInDatabase(data: PrivateHireVehicle, localImageUri: Uri?) = io {
doTryOperation("Failed to upload $objectName") {
val imageUrl = getImageUrl(localImageUri, data.phCarImageString)
data.phCarImageString = imageUrl

View File

@@ -6,22 +6,22 @@ import h_mal.appttude.com.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.data.FirebaseAuthentication
import h_mal.appttude.com.data.FirebaseDatabaseSource
import h_mal.appttude.com.data.FirebaseStorageSource
import h_mal.appttude.com.model.VehicleProfileObject
import h_mal.appttude.com.model.VehicleProfile
import h_mal.appttude.com.utils.Coroutines.io
class VehicleProfileViewModel(
auth: FirebaseAuthentication,
database: FirebaseDatabaseSource,
storage: FirebaseStorageSource
) : DataSubmissionBaseViewModel<VehicleProfileObject>(auth, database, storage) {
) : DataSubmissionBaseViewModel<VehicleProfile>(auth, database, storage) {
override val databaseRef: DatabaseReference = database.getVehicleDetailsRef(uid)
override val storageRef: StorageReference? = null
override val objectName: String = "vehicle profile"
override fun getDataFromDatabase() = getDataClass<VehicleProfileObject>()
override fun getDataFromDatabase() = getDataClass<VehicleProfile>()
override fun setDataInDatabase(data: VehicleProfileObject) {
override fun setDataInDatabase(data: VehicleProfile) {
io {
doTryOperation("Failed to upload $objectName") {
postDataToDatabase(data)

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent"/>
<stroke android:width="2dip" android:color="#03a9f4" />
<corners android:radius="22dip"/>
<padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
</shape>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent" />
<stroke
android:width="2dip"
android:color="#03a9f4" />
<corners android:radius="22dip" />
<padding
android:left="0dip"
android:top="0dip"
android:right="0dip"
android:bottom="0dip" />
</shape>

View File

@@ -1,5 +1,10 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
<vector android:height="24dp"
android:tint="#FFFFFF"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z" />
</vector>

View File

@@ -1,5 +1,10 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM12,7c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM18,19L6,19v-1.4c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1L18,19z"/>
<vector android:height="24dp"
android:tint="#FFFFFF"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM12,7c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM18,19L6,19v-1.4c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1L18,19z" />
</vector>

View File

@@ -1,5 +1,10 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z"/>
<vector android:height="24dp"
android:tint="#FFFFFF"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z" />
</vector>

View File

@@ -1,9 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"
tools:ignore="VectorPath" />
</vector>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" android:padding="10dp">
android:shape="rectangle"
android:padding="10dp">
<solid android:color="#03a9f4" />
<corners
android:bottomRightRadius="22dp"

View File

@@ -1,14 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/cars"
android:background="@drawable/background_with_curve"
tools:context="h_mal.appttude.com.ui.user.LoginActivity">
<fragment
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
@@ -21,5 +20,4 @@
app:navGraph="@navigation/auth_navigation"
tools:context=".ui.auth.AuthActivity" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
@@ -10,6 +9,7 @@
tools:openDrawer="start">
<include
android:id="@+id/app_bar_layout"
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.appcompat.widget.LinearLayoutCompat
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_margin="12dp"
@@ -20,12 +21,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="1dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/or"
android:layout_toStartOf="@id/or"
android:background="#616161"
android:gravity="center" />
@@ -35,14 +37,14 @@
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="4dp"
android:text="OR" />
android:text="@string/or" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="1dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/or"
android:layout_toEndOf="@id/or"
android:background="#616161"
android:gravity="center" />
@@ -60,4 +62,4 @@
android:textColor="@android:color/black"
android:textStyle="bold" />
</LinearLayout>
</androidx.appcompat.widget.LinearLayoutCompat>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/driver_profile_request_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
@@ -17,7 +16,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:text="You are not a driver, request a driver profile to use this app" />
android:text="@string/not_a_driver_message" />
<androidx.cardview.widget.CardView
android:id="@+id/request_driver_button"
@@ -32,7 +31,7 @@
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:gravity="center"
android:text="Request driver profile"
android:text="@string/request_driver_profile"
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold" />

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/parent_constraint_layout"

View File

@@ -12,17 +12,17 @@
android:background="#000000"
tools:src="@drawable/cars" />
<android.support.design.widget.FloatingActionButton
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/download_pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:src="@drawable/ic_file_download_black_24dp" />
android:src="@drawable/ic_file_download_black_24dp"
android:contentDescription="@string/floating_action_button" />
</RelativeLayout>

View File

@@ -3,7 +3,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="@drawable/cars"
tools:context=".ui.user.SplashScreenFragment">
</FrameLayout>

View File

@@ -12,8 +12,7 @@
android:layout_margin="12dp"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
style="@style/text_input_layout">
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout">
<EditText
android:id="@+id/reg"
@@ -25,8 +24,7 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/text_input_layout">
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout">
<EditText
android:id="@+id/make"
@@ -39,8 +37,7 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/text_input_layout">
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout">
<EditText
android:id="@+id/car_model"
@@ -53,8 +50,7 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/text_input_layout">
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout">
<EditText
android:id="@+id/colour"
@@ -67,8 +63,7 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/text_input_layout">
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout">
<EditText
android:id="@+id/keeper_name"
@@ -84,8 +79,7 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/text_input_layout">
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout">
<EditText
android:id="@+id/address"
@@ -104,8 +98,7 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/text_input_layout">
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout">
<EditText
android:id="@+id/postcode"
@@ -122,8 +115,7 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/text_input_layout">
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout">
<EditText
android:id="@+id/start_date"

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/driver_profile_request_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -15,6 +14,7 @@
style="@style/TextButton.WithIcon"
android:text="@string/driver_profile"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/car"
style="@style/TextButton.WithIcon"

View File

@@ -17,6 +17,7 @@
android:layout_width="200dp"
android:layout_height="200dp"
android:adjustViewBounds="true">
<androidx.viewpager.widget.ViewPager
android:layout_width="wrap_content"
android:layout_height="wrap_content">

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background_with_curve">
<ImageView
android:id="@+id/background_img"
android:src="@drawable/welcome_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
style="@style/imageBackground"
android:contentDescription="@string/image_description" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"

View File

@@ -89,4 +89,10 @@
<string name="insurance_expiry">Insurance expiry</string>
<string name="logout">Logout</string>
<string name="forgot_password">Forgot password?</string>
<string name="leave_header">Leave?</string>
<string name="leave_message">Are you sure you want to exit?</string>
<string name="not_a_driver_message">You are not a driver, request a driver profile to use this app</string>
<string name="request_driver_profile">Request driver profile</string>
<string name="or">OR</string>
<string name="floating_action_button">Floating action button</string>
</resources>

View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="my_images" path="Android/data/h_mal.appttude.com.driver/files/Pictures" />
<external-path
name="my_images"
path="Android/data/h_mal.appttude.com.driver/files/Pictures" />
</paths>

View File

@@ -1,32 +1,19 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.7.10"
repositories {
google()
jcenter()
maven { url "https://www.jitpack.io" }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath 'com.google.gms:google-services:4.3.15'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10"
def nav_version = "2.4.1"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.4.1")
}
}
allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io" }
}
plugins {
id 'com.android.application' version '7.2.2' apply false
id 'com.android.library' version '7.2.2' apply false
id 'com.google.gms.google-services' version '4.3.15' apply false
id 'androidx.navigation.safeargs.kotlin' version '2.4.0' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
}
task clean(type: Delete) {

View File

@@ -1 +1,19 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
jcenter()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url "https://www.jitpack.io" }
jcenter()
}
}
rootProject.name = "Driver"
include ':app'