diff --git a/.circleci/config.yml b/.circleci/config.yml
index c9fe671..bd9f168 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -27,8 +27,15 @@ jobs:
# Add steps to the job
# See: https://circleci.com/docs/2.0/configuration-reference/#steps
steps:
- # Checkout the code as the first step.
+ # Checkout the code and its submodule as the first step.
- checkout
+ # config git user
+ - run:
+ name: Setup subtree for test data
+ command: |
+ git config --global user.email "$GIT_EMAIL"
+ git config --global user.name "$GIT_EMAIL"
+ git subtree add --prefix=driver_app_data https://github.com/hmalik144/driver_app_data main
# Setup files for build.
- run:
name: Setup variables for build
@@ -50,13 +57,14 @@ jobs:
- run:
name: Start firebase emulator
command: |
- firebase emulators:start
+ firebase emulators:start --import=driver_app_data/export_directory
background: true
# Then start the emulator and run the Instrumentation tests!
- android/start-emulator-and-run-tests:
post-emulator-launch-assemble-command: ./gradlew assemble<< parameters.flavour >>DebugAndroidTest
test-command: ./gradlew connected<< parameters.flavour >>DebugAndroidTest
system-image: system-images;android-25;google_apis;x86
+ max-tries: 1
# store test reports
- store_artifacts:
path: app/build/reports/androidTests/connected
diff --git a/.gitignore b/.gitignore
index c9415c3..4468893 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
/.idea/navEditor.xml
/.idea/misc.xml
/.idea/assetWizardSettings.xml
+/.idea/shelf/
.DS_Store
/build
/captures
@@ -19,3 +20,6 @@ local
/.circleci/run_local.bash
/Gemfile.lock
/playstore.json
+
+database-debug.log
+firebase-debug.log
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index b589d56..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/dictionaries/h_mal.xml b/.idea/dictionaries/h_mal.xml
deleted file mode 100644
index a2afb5c..0000000
--- a/.idea/dictionaries/h_mal.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- viewmodel
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index 66ff961..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 6626cd0..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 4a43c3f..387eda3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -150,7 +150,7 @@ dependencies {
implementation "org.kodein.di:kodein-di-generic-jvm:$kodein_version"
implementation "org.kodein.di:kodein-di-framework-android-x:$kodein_version"
/ * Image Carousal */
- implementation 'com.synnapps:carouselview:0.1.5'
+ implementation 'com.synnapps:carouselview:0.1.6'
/ * Glide */
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt b/app/src/admin/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt
index 34fb329..6bb156a 100644
--- a/app/src/admin/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt
+++ b/app/src/admin/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt
@@ -1,5 +1,6 @@
package h_mal.appttude.com.driver.application
+import android.content.res.Resources
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import h_mal.appttude.com.driver.data.FirebaseAuthSource
@@ -12,7 +13,8 @@ class ApplicationViewModelFactory(
private val auth: FirebaseAuthSource,
private val database: FirebaseDatabaseSource,
private val storage: FirebaseStorageSource,
- private val preferences: PreferenceProvider
+ private val preferences: PreferenceProvider,
+ private val resources: Resources
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@@ -57,6 +59,7 @@ class ApplicationViewModelFactory(
database,
preferences
)
+ isAssignableFrom(ApproverViewModel::class.java) -> ApproverViewModel(resources , database)
else -> throw IllegalArgumentException("Unknown ViewModel class")
} as T
}
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/application/DriverApplication.kt b/app/src/admin/java/h_mal/appttude/com/driver/application/DriverApplication.kt
new file mode 100644
index 0000000..6f4a8b6
--- /dev/null
+++ b/app/src/admin/java/h_mal/appttude/com/driver/application/DriverApplication.kt
@@ -0,0 +1,23 @@
+package h_mal.appttude.com.driver.application
+
+import h_mal.appttude.com.driver.data.prefs.PreferenceProvider
+import org.kodein.di.generic.bind
+import org.kodein.di.generic.instance
+import org.kodein.di.generic.provider
+import org.kodein.di.generic.singleton
+
+class DriverApplication : BaseApplication() {
+
+ override val flavourModule = super.flavourModule.copy {
+ bind() from singleton { PreferenceProvider(this@DriverApplication) }
+ bind() from provider {
+ ApplicationViewModelFactory(
+ instance(),
+ instance(),
+ instance(),
+ instance(),
+ instance()
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/model/ApprovalStatus.kt b/app/src/admin/java/h_mal/appttude/com/driver/model/ApprovalStatus.kt
new file mode 100644
index 0000000..75fc57b
--- /dev/null
+++ b/app/src/admin/java/h_mal/appttude/com/driver/model/ApprovalStatus.kt
@@ -0,0 +1,19 @@
+package h_mal.appttude.com.driver.model
+
+import h_mal.appttude.com.driver.R
+
+enum class ApprovalStatus(val stringId: Int, val drawableId: Int, val score: Int) {
+ NOT_SUBMITTED(R.string.not_submitted, R.drawable.denied, 0),
+ DENIED(R.string.denied, R.drawable.denied, 1),
+ PENDING_APPROVAL(R.string.pending, R.drawable.pending, 2),
+ APPROVED(R.string.approved, R.drawable.approved, 3);
+
+ companion object {
+ infix fun getByScore(value: Int): ApprovalStatus? =
+ ApprovalStatus.values().firstOrNull { it.score == value }
+ infix fun getByStringId(value: Int): ApprovalStatus? =
+ ApprovalStatus.values().firstOrNull { it.stringId == value }
+ infix fun getByDrawableId(value: Int): ApprovalStatus? =
+ ApprovalStatus.values().firstOrNull { it.drawableId == value }
+ }
+}
\ No newline at end of file
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/model/DatabaseStatus.kt b/app/src/admin/java/h_mal/appttude/com/driver/model/DatabaseStatus.kt
new file mode 100644
index 0000000..3fba046
--- /dev/null
+++ b/app/src/admin/java/h_mal/appttude/com/driver/model/DatabaseStatus.kt
@@ -0,0 +1,27 @@
+package h_mal.appttude.com.driver.model
+
+import h_mal.appttude.com.driver.R
+
+enum class DatabaseStatus(val drawable: Int, val header: Int, val subtext: Int) {
+ NO_CONNECTION(R.drawable.baseline_inbox_24, R.string.no_connection, R.string.no_connection_subtext),
+ NO_PERMISSION(
+ R.drawable.baseline_inbox_24,
+ R.string.no_permission,
+ R.string.no_permission_subtext
+ ),
+ CANNOT_RETRIEVE(
+ R.drawable.baseline_inbox_24,
+ R.string.cannot_retrieve,
+ R.string.cannot_retrieve_subtext
+ ),
+ NO_AUTHORIZATION(
+ R.drawable.baseline_inbox_24,
+ R.string.no_authorization,
+ R.string.no_authorization_subtext
+ ),
+ EMPTY_RESULTS(
+ R.drawable.baseline_inbox_24,
+ R.string.no_drivers_to_show,
+ R.string.no_drivers_subtext
+ )
+}
\ No newline at end of file
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/objects/ArchiveObject.kt b/app/src/admin/java/h_mal/appttude/com/driver/objects/ArchiveObject.kt
index c79088f..2700e93 100644
--- a/app/src/admin/java/h_mal/appttude/com/driver/objects/ArchiveObject.kt
+++ b/app/src/admin/java/h_mal/appttude/com/driver/objects/ArchiveObject.kt
@@ -1,6 +1,12 @@
package h_mal.appttude.com.driver.objects
-import h_mal.appttude.com.driver.model.*
+import h_mal.appttude.com.driver.model.DriversLicense
+import h_mal.appttude.com.driver.model.Insurance
+import h_mal.appttude.com.driver.model.Logbook
+import h_mal.appttude.com.driver.model.Mot
+import h_mal.appttude.com.driver.model.PrivateHireLicense
+import h_mal.appttude.com.driver.model.PrivateHireVehicle
+import h_mal.appttude.com.driver.model.VehicleProfile
data class ArchiveObject(
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/objects/wholeObject/VehicleProfile.kt b/app/src/admin/java/h_mal/appttude/com/driver/objects/wholeObject/VehicleProfile.kt
index 1cfec4d..1828463 100644
--- a/app/src/admin/java/h_mal/appttude/com/driver/objects/wholeObject/VehicleProfile.kt
+++ b/app/src/admin/java/h_mal/appttude/com/driver/objects/wholeObject/VehicleProfile.kt
@@ -1,6 +1,9 @@
package h_mal.appttude.com.driver.objects.wholeObject
-import h_mal.appttude.com.driver.model.*
+import h_mal.appttude.com.driver.model.Insurance
+import h_mal.appttude.com.driver.model.Logbook
+import h_mal.appttude.com.driver.model.Mot
+import h_mal.appttude.com.driver.model.PrivateHireVehicle
import h_mal.appttude.com.driver.model.VehicleProfile
data class VehicleProfile (
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/ui/ApprovalListAdapter.kt b/app/src/admin/java/h_mal/appttude/com/driver/ui/ApprovalListAdapter.kt
index d08f1a2..297f572 100644
--- a/app/src/admin/java/h_mal/appttude/com/driver/ui/ApprovalListAdapter.kt
+++ b/app/src/admin/java/h_mal/appttude/com/driver/ui/ApprovalListAdapter.kt
@@ -1,77 +1,57 @@
package h_mal.appttude.com.driver.ui
+import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.BaseAdapter
-import androidx.annotation.DrawableRes
-import androidx.annotation.StringRes
-import h_mal.appttude.com.driver.R
+import android.widget.ArrayAdapter
import h_mal.appttude.com.driver.databinding.ApprovalListItemBinding
+import h_mal.appttude.com.driver.model.ApprovalStatus
import h_mal.appttude.com.driver.utils.hide
+import java.io.IOException
class ApprovalListAdapter(
- private val layoutInflater: LayoutInflater,
- private var approvals: Map,
+ private val context: Context,
+ private val data: List>,
private val callback: (String) -> Unit
-) : BaseAdapter() {
- override fun getCount(): Int = approvals.size
- override fun getItem(position: Int): Map.Entry = approvals.entries.elementAt(position)
- override fun getItemId(position: Int): Long = position.toLong()
+) : ArrayAdapter>(context, 0, data) {
+
+ override fun getCount(): Int = data.size
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var listItemView: View? = convertView
val binding: ApprovalListItemBinding
if (listItemView == null) {
- binding = ApprovalListItemBinding.inflate(layoutInflater, parent, false)
+ // Inflate view binding into listview cell
+ binding = ApprovalListItemBinding.inflate(LayoutInflater.from(context), parent, false)
listItemView = binding.root
listItemView.setTag(listItemView.id, binding)
} else {
+ // cell exists so recycling view
binding = listItemView.getTag(listItemView.id) as ApprovalListItemBinding
}
- val key = getItem(position).key
- val itemValue = getItem(position).value
+ val key: String = getItem(position)?.first ?: throw IOException("No document name provided")
+ val approvalStatus: ApprovalStatus? = getItem(position)?.second
binding.approvalText.text = key
- if (itemValue != 0) {
- binding.root.setOnClickListener { callback.invoke(key) }
- }
- binding.approvalIv.setImageResource(getImageResourceBasedOnApproval(itemValue))
- binding.approvalStatus.text = listItemView.context.getString(getStringResourceBasedOnApproval(itemValue))
-
- if (position == 0) {
- binding.divider.hide()
+ approvalStatus?.let { item ->
+ item.score.takeIf { it != 0 }?.let {
+ binding.root.setOnClickListener { callback.invoke(key) }
+ }
+ binding.approvalIv.setImageResource(item.drawableId)
+ binding.approvalStatus.text = context.getString(item.stringId)
}
+ // hide divider for first cell
+ if (position == 0) binding.divider.hide()
return (listItemView)
}
- @DrawableRes
- private fun getImageResourceBasedOnApproval(value: Int?): Int {
- return when(value) {
- 0 -> R.drawable.denied
- 1 -> R.drawable.denied
- 2 -> R.drawable.pending
- 3 -> R.drawable.approved
- else -> R.drawable.pending
- }
- }
- @StringRes
- private fun getStringResourceBasedOnApproval(value: Int?): Int {
- return when(value) {
- 0 -> R.string.not_submitted
- 1 -> R.string.denied
- 2 -> R.string.pending
- 3 -> R.string.approved
- else -> R.string.pending
- }
- }
-
- fun updateAdapter(data: Map) {
- approvals = data
- notifyDataSetChanged()
+ fun updateAdapter(date: List>) {
+ clear()
+ addAll(date)
}
}
\ No newline at end of file
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/ui/ApproverFragment.kt b/app/src/admin/java/h_mal/appttude/com/driver/ui/ApproverFragment.kt
new file mode 100644
index 0000000..16df2d8
--- /dev/null
+++ b/app/src/admin/java/h_mal/appttude/com/driver/ui/ApproverFragment.kt
@@ -0,0 +1,39 @@
+package h_mal.appttude.com.driver.ui
+
+import h_mal.appttude.com.driver.base.BaseFragment
+import h_mal.appttude.com.driver.databinding.FragmentApproverBinding
+import h_mal.appttude.com.driver.model.ApprovalStatus
+import h_mal.appttude.com.driver.viewmodels.ApproverViewModel
+
+
+class ApproverFragment : BaseFragment() {
+
+ override fun setupView(binding: FragmentApproverBinding) = binding.run {
+ super.setupView(binding)
+
+ val args = requireArguments()
+ viewModel.init(args)
+
+ // Retrieve fragment name argument saved from previous fragment
+ val fragmentClass = viewModel.getFragmentClass()
+
+ childFragmentManager.beginTransaction()
+ .replace(container.id, fragmentClass, args, null)
+ .commitNow()
+
+ approve.setOnClickListener { viewModel.approveDocument() }
+ decline.setOnClickListener { viewModel.declineDocument() }
+ }
+
+ override fun onSuccess(data: Any?) {
+ super.onSuccess(data)
+ when (data) {
+ ApprovalStatus.APPROVED -> displaySnackBar("approved")
+ ApprovalStatus.DENIED -> displaySnackBar("declined")
+ }
+ }
+
+ private fun displaySnackBar(status: String) {
+ showSnackBar("Document has been $status")
+ }
+}
\ No newline at end of file
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/ui/DriverOverviewFragment.kt b/app/src/admin/java/h_mal/appttude/com/driver/ui/DriverOverviewFragment.kt
index d3e85d6..d8df9ae 100644
--- a/app/src/admin/java/h_mal/appttude/com/driver/ui/DriverOverviewFragment.kt
+++ b/app/src/admin/java/h_mal/appttude/com/driver/ui/DriverOverviewFragment.kt
@@ -1,11 +1,12 @@
package h_mal.appttude.com.driver.ui
-import android.view.View
import android.widget.ListView
import h_mal.appttude.com.driver.R
import h_mal.appttude.com.driver.base.BaseFragment
import h_mal.appttude.com.driver.data.USER_CONST
import h_mal.appttude.com.driver.databinding.FragmentUserMainBinding
+import h_mal.appttude.com.driver.model.ApprovalStatus
+import h_mal.appttude.com.driver.utils.FRAGMENT
import h_mal.appttude.com.driver.utils.navigateTo
import h_mal.appttude.com.driver.utils.toBundle
import h_mal.appttude.com.driver.viewmodels.DriverOverviewViewModel
@@ -19,40 +20,30 @@ class DriverOverviewFragment : BaseFragment) {
+ if (data is List<*>) {
+ val listData = data as List>
if (listView.adapter == null) {
- listView.adapter = ApprovalListAdapter(layoutInflater, data as Map) {
- this.view?.applyNavigation(it)
+ listView.adapter = ApprovalListAdapter(requireContext(), listData) {
+ this.view?.navigateTo(
+ R.id.to_approverFragment,
+ driverId.toBundle(USER_CONST).apply { putString(FRAGMENT, it) })
}
listView.isScrollContainer = false
} else {
- (listView.adapter as ApprovalListAdapter).updateAdapter(data as Map)
+ (listView.adapter as ApprovalListAdapter).updateAdapter(listData)
}
}
}
-
- private fun View.applyNavigation(key: String) {
- val navId = when (key) {
- context.getString(R.string.driver_profile) -> R.id.to_driverProfileFragment
- context.getString(R.string.drivers_license) -> R.id.to_driverLicenseFragment
- context.getString(R.string.private_hire_license) -> R.id.to_privateHireLicenseFragment
- context.getString(R.string.vehicle_profile) -> R.id.to_vehicleProfileFragment
- context.getString(R.string.insurance) -> R.id.to_insuranceFragment
- context.getString(R.string.m_o_t) -> R.id.to_motFragment
- context.getString(R.string.log_book) -> R.id.to_logbookFragment
- context.getString(R.string.private_hire_vehicle_license) -> R.id.to_privateHireVehicleFragment
- else -> {
- throw StringIndexOutOfBoundsException("No resource for $key")
- }
- }
- navigateTo(navId, driverId.toBundle(USER_CONST))
- }
}
\ No newline at end of file
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/ui/HomeSuperUserFragment.kt b/app/src/admin/java/h_mal/appttude/com/driver/ui/HomeSuperUserFragment.kt
index f75e34d..7b0e28f 100644
--- a/app/src/admin/java/h_mal/appttude/com/driver/ui/HomeSuperUserFragment.kt
+++ b/app/src/admin/java/h_mal/appttude/com/driver/ui/HomeSuperUserFragment.kt
@@ -19,6 +19,8 @@ import h_mal.appttude.com.driver.base.CustomViewHolder
import h_mal.appttude.com.driver.data.USER_CONST
import h_mal.appttude.com.driver.databinding.FragmentHomeSuperUserBinding
import h_mal.appttude.com.driver.databinding.ListItemLayoutBinding
+import h_mal.appttude.com.driver.model.DatabaseStatus
+import h_mal.appttude.com.driver.model.DatabaseStatus.*
import h_mal.appttude.com.driver.model.SortOption
import h_mal.appttude.com.driver.objects.UserObject
import h_mal.appttude.com.driver.objects.WholeDriverObject
@@ -27,7 +29,8 @@ import h_mal.appttude.com.driver.viewmodels.SuperUserViewModel
import java.util.*
-class HomeSuperUserFragment : BaseFragment(), MenuProvider {
+class HomeSuperUserFragment : BaseFragment(),
+ MenuProvider {
private lateinit var adapter: FirebaseRecyclerAdapter>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -42,6 +45,17 @@ class HomeSuperUserFragment : BaseFragment -> setAdapterToRecyclerView(data)
}
}
+ private fun setNonView(status: DatabaseStatus) {
+ applyBinding {
+ emptyView.run {
+ root.setOnClickListener(null)
+ root.visibility = View.VISIBLE
+ icon.setImageResource(status.drawable)
+ header.setText(status.header)
+ subtext.setText(status.subtext)
+ }
+ }
+ }
@Suppress("UNCHECKED_CAST")
private fun setAdapterToRecyclerView(options: FirebaseRecyclerOptions<*>) {
@@ -74,8 +88,8 @@ class HomeSuperUserFragment : BaseFragment): BaseFirebaseAdapter {
- return object : BaseFirebaseAdapter(options, layoutInflater) {
-
+ return object :
+ BaseFirebaseAdapter(options, layoutInflater) {
override fun onBindViewHolder(
holder: CustomViewHolder,
position: Int,
@@ -87,7 +101,8 @@ class HomeSuperUserFragment : BaseFragment if (count > 6) context.displayToast("Identifier cannot be larger than 6") }
+ doOnTextChanged { _, _, count, _ -> if (count > 6) showToast("Identifier cannot be larger than 6") }
}
val layout = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/ui/vehicleprofile/VehicleProfileFragment.kt b/app/src/admin/java/h_mal/appttude/com/driver/ui/vehicleprofile/VehicleProfileFragment.kt
index e5d3218..309cab7 100644
--- a/app/src/admin/java/h_mal/appttude/com/driver/ui/vehicleprofile/VehicleProfileFragment.kt
+++ b/app/src/admin/java/h_mal/appttude/com/driver/ui/vehicleprofile/VehicleProfileFragment.kt
@@ -12,6 +12,7 @@ class VehicleProfileFragment :
override fun setupView(binding: FragmentVehicleSetupBinding) {
super.setupView(binding)
viewsToHide(binding.submit)
+ binding.seizedCheckbox.isEnabled = false
}
override fun setFields(data: VehicleProfile) {
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/utils/Constants.kt b/app/src/admin/java/h_mal/appttude/com/driver/utils/Constants.kt
new file mode 100644
index 0000000..09d4953
--- /dev/null
+++ b/app/src/admin/java/h_mal/appttude/com/driver/utils/Constants.kt
@@ -0,0 +1,3 @@
+package h_mal.appttude.com.driver.utils
+
+const val FRAGMENT = "fragment"
\ No newline at end of file
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/ApproverViewModel.kt b/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/ApproverViewModel.kt
new file mode 100644
index 0000000..d31570b
--- /dev/null
+++ b/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/ApproverViewModel.kt
@@ -0,0 +1,104 @@
+package h_mal.appttude.com.driver.viewmodels
+
+import android.content.res.Resources
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import com.google.firebase.database.DatabaseReference
+import h_mal.appttude.com.driver.R
+import h_mal.appttude.com.driver.base.BaseViewModel
+import h_mal.appttude.com.driver.data.FirebaseCompletion
+import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
+import h_mal.appttude.com.driver.data.USER_CONST
+import h_mal.appttude.com.driver.model.ApprovalStatus
+import h_mal.appttude.com.driver.objects.ApprovalsObject
+import h_mal.appttude.com.driver.ui.driverprofile.DriverLicenseFragment
+import h_mal.appttude.com.driver.ui.driverprofile.DriverProfileFragment
+import h_mal.appttude.com.driver.ui.driverprofile.PrivateHireLicenseFragment
+import h_mal.appttude.com.driver.ui.vehicleprofile.InsuranceFragment
+import h_mal.appttude.com.driver.ui.vehicleprofile.LogbookFragment
+import h_mal.appttude.com.driver.ui.vehicleprofile.MotFragment
+import h_mal.appttude.com.driver.ui.vehicleprofile.PrivateHireVehicleFragment
+import h_mal.appttude.com.driver.ui.vehicleprofile.VehicleProfileFragment
+import h_mal.appttude.com.driver.utils.Coroutines.io
+import h_mal.appttude.com.driver.utils.FRAGMENT
+import h_mal.appttude.com.driver.utils.getDataFromDatabaseRef
+
+class ApproverViewModel(
+ private val resources: Resources,
+ private val database: FirebaseDatabaseSource
+) : BaseViewModel() {
+
+ private lateinit var name: String
+ private lateinit var docRef: DatabaseReference
+
+ private var score: ApprovalStatus? = null
+
+ fun init(args: Bundle) {
+ // Retried uid & fragment class name from args
+ val uid = args.getString(USER_CONST) ?: throw NullPointerException("No user Id was passed")
+ name = args.getString(FRAGMENT)
+ ?: throw NullPointerException("No fragment name argument passed")
+ // Define a document name based on fragment class name
+ val documentName = when (name) {
+ resources.getString(R.string.driver_profile) -> ApprovalsObject::driver_details_approval.name
+ resources.getString(R.string.drivers_license) -> ApprovalsObject::driver_license_approval.name
+ resources.getString(R.string.private_hire_license) -> ApprovalsObject::private_hire_approval.name
+ resources.getString(R.string.vehicle_profile) -> ApprovalsObject::vehicle_details_approval.name
+ resources.getString(R.string.insurance) -> ApprovalsObject::insurance_details_approval.name
+ resources.getString(R.string.m_o_t) -> ApprovalsObject::mot_details_approval.name
+ resources.getString(R.string.log_book) -> ApprovalsObject::log_book_approval.name
+ resources.getString(R.string.private_hire_vehicle_license) -> ApprovalsObject::ph_car_approval.name
+ else -> {
+ throw StringIndexOutOfBoundsException("No resource for $name")
+ }
+ }
+
+ docRef = database.getDocumentApprovalRef(uid, documentName)
+ io {
+ doTryOperation("") {
+ val data = docRef.getDataFromDatabaseRef()
+ score = data?.let { ApprovalStatus.getByScore(it) } ?: ApprovalStatus.NOT_SUBMITTED
+ onSuccess(FirebaseCompletion.Default)
+ }
+ }
+ }
+
+ fun getFragmentClass(): Class {
+ return when (name) {
+ resources.getString(R.string.driver_profile) -> DriverProfileFragment::class.java
+ resources.getString(R.string.drivers_license) -> DriverLicenseFragment::class.java
+ resources.getString(R.string.private_hire_license) -> PrivateHireLicenseFragment::class.java
+ resources.getString(R.string.vehicle_profile) -> VehicleProfileFragment::class.java
+ resources.getString(R.string.insurance) -> InsuranceFragment::class.java
+ resources.getString(R.string.m_o_t) -> MotFragment::class.java
+ resources.getString(R.string.log_book) -> LogbookFragment::class.java
+ resources.getString(R.string.private_hire_vehicle_license) -> PrivateHireVehicleFragment::class.java
+ else -> {
+ throw StringIndexOutOfBoundsException("No resource for $name")
+ }
+ }
+ }
+
+ fun approveDocument() {
+ updateDocument(ApprovalStatus.APPROVED)
+ }
+
+ fun declineDocument() {
+ updateDocument(ApprovalStatus.DENIED)
+ }
+
+ private fun updateDocument(approval: ApprovalStatus) {
+ if (approval == score) {
+ val result = if (approval == ApprovalStatus.APPROVED) "approved" else "declined"
+ onError("Document already $result")
+ return
+ }
+
+ io {
+ doTryOperation("Failed to decline document") {
+ database.postToDatabaseRed(docRef, approval.score)
+ onSuccess(approval)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/DriverOverviewViewModel.kt b/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/DriverOverviewViewModel.kt
index 9a21521..fbda4ad 100644
--- a/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/DriverOverviewViewModel.kt
+++ b/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/DriverOverviewViewModel.kt
@@ -5,10 +5,12 @@ import com.google.firebase.storage.StorageReference
import h_mal.appttude.com.driver.base.DataSubmissionBaseViewModel
import h_mal.appttude.com.driver.data.FirebaseAuthentication
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
+import h_mal.appttude.com.driver.model.ApprovalStatus
import h_mal.appttude.com.driver.objects.ApprovalsObject
import h_mal.appttude.com.driver.utils.Coroutines.io
import h_mal.appttude.com.driver.utils.getDataFromDatabaseRef
import kotlinx.coroutines.Job
+import java.io.IOException
class DriverOverviewViewModel(
auth: FirebaseAuthentication,
@@ -37,17 +39,23 @@ class DriverOverviewViewModel(
}
}
- private fun mapApprovalsForView(data: ApprovalsObject): Map {
- return mutableMapOf().apply {
- put("Driver Profile", data.driver_details_approval)
- put("Drivers License", data.driver_license_approval)
- put("Private Hire License", data.private_hire_approval)
- put("Vehicle Profile", data.vehicle_details_approval)
- put("Insurance", data.insurance_details_approval)
- put("M.O.T", data.mot_details_approval)
- put("Log book", data.log_book_approval)
- put("Private Hire Vehicle License", data.ph_car_approval)
- }.toMap()
+ private fun mapApprovalsForView(data: ApprovalsObject): List> {
+ val list = mutableListOf>()
+ return list.apply {
+ add(0, Pair("Driver Profile", getApprovalStatusByScore(data.driver_details_approval)))
+ add(1, Pair("Drivers License", getApprovalStatusByScore(data.driver_license_approval)))
+ add(2, Pair("Private Hire License", getApprovalStatusByScore(data.private_hire_approval)))
+ add(3, Pair("Vehicle Profile", getApprovalStatusByScore(data.vehicle_details_approval)))
+ add(4, Pair("Insurance", getApprovalStatusByScore(data.insurance_details_approval)))
+ add(5, Pair("M.O.T", getApprovalStatusByScore(data.mot_details_approval)))
+ add(6, Pair("Log book", getApprovalStatusByScore(data.log_book_approval)))
+ add(7, Pair("Private Hire Vehicle License", getApprovalStatusByScore(data.ph_car_approval)))
+ }
+ }
+
+ private fun getApprovalStatusByScore(score: Int): ApprovalStatus {
+ if (score == 0) return ApprovalStatus.NOT_SUBMITTED
+ return ApprovalStatus.getByScore(score) ?: throw IOException("No approval for score $score")
}
}
diff --git a/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/SuperUserViewModel.kt b/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/SuperUserViewModel.kt
index 33fa782..9fdda57 100644
--- a/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/SuperUserViewModel.kt
+++ b/app/src/admin/java/h_mal/appttude/com/driver/viewmodels/SuperUserViewModel.kt
@@ -22,7 +22,7 @@ class SuperUserViewModel(
}
fun createFirebaseOptions(sort: SortOption? = null) {
- val ref = firebaseDatabaseSource.getUsersRef()
+ val ref = firebaseDatabaseSource.getUsersRef().orderByChild("role").startAt("driver").endAt("driver")
sort?.isNotNull { preferenceProvider.setSortOption(it.label) }
@@ -33,7 +33,7 @@ class SuperUserViewModel(
// }
val options = FirebaseRecyclerOptions.Builder()
- .setQuery(ref.orderByKey(), WholeDriverObject::class.java)
+ .setQuery(ref, WholeDriverObject::class.java)
.build()
onSuccess(options)
@@ -47,7 +47,7 @@ class SuperUserViewModel(
onError("No driver identifier provided")
return@doTryOperation
}
- val text = if (input.length > 6) input.substring(0,7) else input
+ val text = if (input.length > 6) input.substring(0, 7) else input
firebaseDatabaseSource.run {
postToDatabaseRed(getDriverNumberRef(uid), text)
diff --git a/app/src/admin/res/layout/empty_users_view.xml b/app/src/admin/res/layout/empty_users_view.xml
new file mode 100644
index 0000000..68d156d
--- /dev/null
+++ b/app/src/admin/res/layout/empty_users_view.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/admin/res/layout/fragment_approver.xml b/app/src/admin/res/layout/fragment_approver.xml
new file mode 100644
index 0000000..02eda08
--- /dev/null
+++ b/app/src/admin/res/layout/fragment_approver.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/admin/res/layout/fragment_home_super_user.xml b/app/src/admin/res/layout/fragment_home_super_user.xml
index 3831a54..9a8e8a7 100644
--- a/app/src/admin/res/layout/fragment_home_super_user.xml
+++ b/app/src/admin/res/layout/fragment_home_super_user.xml
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:id="@+id/container"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.HomeSuperUserFragment">
@@ -28,9 +29,9 @@
-
\ No newline at end of file
diff --git a/app/src/admin/res/navigation/main_navigation.xml b/app/src/admin/res/navigation/main_navigation.xml
index 085ebfa..fe1c29a 100644
--- a/app/src/admin/res/navigation/main_navigation.xml
+++ b/app/src/admin/res/navigation/main_navigation.xml
@@ -23,68 +23,16 @@
android:id="@+id/userMainFragment"
android:name="h_mal.appttude.com.driver.ui.DriverOverviewFragment"
android:label="fragment_user_main"
- tools:layout="@layout/fragment_user_main" >
+ tools:layout="@layout/fragment_user_main">
+
-
-
-
-
-
-
-
+ android:id="@+id/to_approverFragment"
+ app:destination="@id/approverFragment" />
-
-
-
-
-
-
-
+ android:id="@+id/approverFragment"
+ android:name="h_mal.appttude.com.driver.ui.ApproverFragment"
+ android:label="fragment_approver"
+ tools:layout="@layout/fragment_approver" />
\ No newline at end of file
diff --git a/app/src/admin/res/values/strings.xml b/app/src/admin/res/values/strings.xml
index b2f051c..dbfce83 100644
--- a/app/src/admin/res/values/strings.xml
+++ b/app/src/admin/res/values/strings.xml
@@ -1,3 +1,5 @@
Driver Admin
+
+ Hello blank fragment
\ No newline at end of file
diff --git a/app/src/androidTest/java/h_mal/appttude/com/driver/BaseTestRobot.kt b/app/src/androidTest/java/h_mal/appttude/com/driver/BaseTestRobot.kt
index ed6dcc2..7acdd09 100644
--- a/app/src/androidTest/java/h_mal/appttude/com/driver/BaseTestRobot.kt
+++ b/app/src/androidTest/java/h_mal/appttude/com/driver/BaseTestRobot.kt
@@ -24,7 +24,11 @@ import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.Intents.intending
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
-import androidx.test.espresso.matcher.ViewMatchers.*
+import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
+import androidx.test.espresso.matcher.ViewMatchers.isRoot
+import androidx.test.espresso.matcher.ViewMatchers.withClassName
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
import h_mal.appttude.com.driver.helpers.DataHelper
import h_mal.appttude.com.driver.helpers.EspressoHelper.waitForView
import org.hamcrest.CoreMatchers.allOf
@@ -33,6 +37,7 @@ import org.hamcrest.Matcher
import org.hamcrest.Matchers
import java.io.File
+@SuppressWarnings("unused")
open class BaseTestRobot {
fun fillEditText(resId: Int, text: String?): ViewInteraction =
@@ -51,6 +56,9 @@ open class BaseTestRobot {
fun matchText(viewInteraction: ViewInteraction, text: String): ViewInteraction = viewInteraction
.check(matches(withText(text)))
+ fun matchText(viewId: Int, textId: Int): ViewInteraction = onView(withId(viewId))
+ .check(matches(withText(textId)))
+
fun matchText(resId: Int, text: String): ViewInteraction = matchText(matchView(resId), text)
fun clickListItem(listRes: Int, position: Int) {
@@ -60,7 +68,7 @@ open class BaseTestRobot {
}
fun scrollToRecyclerItem(recyclerId: Int, text: String): ViewInteraction? {
- return onView(withId(recyclerId))
+ return matchView(recyclerId)
.perform(
// scrollTo will fail the test if no item matches.
RecyclerViewActions.scrollTo(
@@ -69,8 +77,38 @@ open class BaseTestRobot {
)
}
+ fun scrollToRecyclerItem(recyclerId: Int, resIdForString: Int): ViewInteraction? {
+ return matchView(recyclerId)
+ .perform(
+ // scrollTo will fail the test if no item matches.
+ RecyclerViewActions.scrollTo(
+ hasDescendant(withText(resIdForString))
+ )
+ )
+ }
+
+ fun scrollToRecyclerItemByPosition(recyclerId: Int, position: Int): ViewInteraction? {
+ return matchView(recyclerId)
+ .perform(
+ // scrollTo will fail the test if no item matches.
+ RecyclerViewActions.scrollToPosition(position)
+ )
+ }
+
fun clickViewInRecycler(recyclerId: Int, text: String) {
- scrollToRecyclerItem(recyclerId, text)?.perform(click())
+ matchView(recyclerId)
+ .perform(
+ // scrollTo will fail the test if no item matches.
+ RecyclerViewActions.actionOnItem(hasDescendant(withText(text)), click())
+ )
+ }
+
+ fun clickViewInRecycler(recyclerId: Int, resIdForString: Int) {
+ matchView(recyclerId)
+ .perform(
+ // scrollTo will fail the test if no item matches.
+ RecyclerViewActions.actionOnItem(hasDescendant(withText(resIdForString)), click())
+ )
}
fun clickSubViewInRecycler(recyclerId: Int, text: String, subView: Int) {
diff --git a/app/src/androidTest/java/h_mal/appttude/com/driver/BaseUiTest.kt b/app/src/androidTest/java/h_mal/appttude/com/driver/BaseUiTest.kt
index 109c0ab..e33e9cc 100644
--- a/app/src/androidTest/java/h_mal/appttude/com/driver/BaseUiTest.kt
+++ b/app/src/androidTest/java/h_mal/appttude/com/driver/BaseUiTest.kt
@@ -1,17 +1,32 @@
package h_mal.appttude.com.driver
+import android.R
+import android.app.Activity
+import android.content.Context
+import android.os.Build
import android.view.View
+import android.view.WindowManager
import androidx.annotation.StringRes
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.IdlingResource
+import androidx.test.espresso.Root
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
-import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import h_mal.appttude.com.driver.base.BaseActivity
+import h_mal.appttude.com.driver.helpers.BaseViewAction
+import org.hamcrest.CoreMatchers
+import org.hamcrest.Description
import org.hamcrest.Matcher
+import org.hamcrest.TypeSafeMatcher
+import org.hamcrest.core.AllOf
import org.junit.After
import org.junit.Before
@@ -23,6 +38,8 @@ open class BaseUiTest>(
private lateinit var mActivityScenarioRule: ActivityScenario
private var mIdlingResource: IdlingResource? = null
+ private lateinit var currentActivity: Activity
+
@Before
fun setup() {
beforeLaunch()
@@ -30,7 +47,7 @@ open class BaseUiTest>(
mActivityScenarioRule.onActivity {
mIdlingResource = it.getIdlingResource()!!
IdlingRegistry.getInstance().register(mIdlingResource)
- afterLaunch()
+ afterLaunch(it)
}
}
@@ -42,7 +59,7 @@ open class BaseUiTest>(
}
fun getResourceString(@StringRes stringRes: Int): String {
- return InstrumentationRegistry.getInstrumentation().targetContext.resources.getString(
+ return getInstrumentation().targetContext.resources.getString(
stringRes
)
}
@@ -50,7 +67,7 @@ open class BaseUiTest>(
fun waitFor(delay: Long) {
onView(isRoot()).perform(object : ViewAction {
override fun getConstraints(): Matcher = isRoot()
- override fun getDescription(): String? = "wait for $delay milliseconds"
+ override fun getDescription(): String = "wait for $delay milliseconds"
override fun perform(uiController: UiController, v: View?) {
uiController.loopMainThreadForAtLeast(delay)
}
@@ -58,5 +75,55 @@ open class BaseUiTest>(
}
open fun beforeLaunch() {}
- open fun afterLaunch() {}
+ open fun afterLaunch(context: Context) {}
+
+
+ @Suppress("DEPRECATION")
+ fun checkToastMessage(message: String) {
+ onView(withText(message)).inRoot(object : TypeSafeMatcher() {
+ override fun describeTo(description: Description?) {
+ description?.appendText("is toast")
+ }
+
+ override fun matchesSafely(root: Root): Boolean {
+ root.run {
+ if (windowLayoutParams.get().type == WindowManager.LayoutParams.TYPE_TOAST) {
+ decorView.run {
+ if (windowToken === applicationWindowToken) {
+ // windowToken == appToken means this window isn't contained by any other windows.
+ // if it was a window for an activity, it would have TYPE_BASE_APPLICATION.
+ return true
+ }
+ }
+ }
+ }
+ return false
+ }
+ }
+ ).check(matches(isDisplayed()))
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ waitFor(3500)
+ }
+ }
+
+ fun checkSnackBarDisplayedByMessage(message: String) {
+ onView(
+ CoreMatchers.allOf(
+ withId(com.google.android.material.R.id.snackbar_text),
+ withText(message)
+ )
+ ).check(matches(isDisplayed()))
+ }
+
+ private fun getCurrentActivity(): Activity {
+ onView(AllOf.allOf(withId(R.id.content), isDisplayed()))
+ .perform(object : BaseViewAction() {
+ override fun setPerform(uiController: UiController?, view: View?) {
+ if (view?.context is Activity) {
+ currentActivity = view.context as Activity
+ }
+ }
+ })
+ return currentActivity
+ }
}
\ No newline at end of file
diff --git a/app/src/androidTest/java/h_mal/appttude/com/driver/FirebaseTest.kt b/app/src/androidTest/java/h_mal/appttude/com/driver/FirebaseTest.kt
index 18550f4..6c93287 100644
--- a/app/src/androidTest/java/h_mal/appttude/com/driver/FirebaseTest.kt
+++ b/app/src/androidTest/java/h_mal/appttude/com/driver/FirebaseTest.kt
@@ -5,6 +5,8 @@ import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.storage.FirebaseStorage
import h_mal.appttude.com.driver.base.BaseActivity
import h_mal.appttude.com.driver.data.FirebaseAuthSource
+import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
+import h_mal.appttude.com.driver.data.FirebaseStorageSource
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.tasks.await
import org.junit.After
@@ -16,7 +18,11 @@ open class FirebaseTest>(
private val signedIn: Boolean = false,
private val signOutAfterTest: Boolean = true
) : BaseUiTest(activity) {
+
private val firebaseAuthSource by lazy { FirebaseAuthSource() }
+ private val firebaseDatabaseSource by lazy { FirebaseDatabaseSource() }
+ private val firebaseStorageSource by lazy { FirebaseStorageSource() }
+
private var email: String? = null
companion object {
@@ -58,6 +64,14 @@ open class FirebaseTest>(
firebaseAuthSource.registerUser(signInEmail, password).await().user
}
+ suspend fun login(
+ signInEmail: String,
+ password: String
+ ) {
+ email = signInEmail
+ firebaseAuthSource.signIn(signInEmail, password).await()
+ }
+
// remove the user we created for testing
suspend fun removeUser() {
try {
diff --git a/app/src/androidTest/java/h_mal/appttude/com/driver/helpers/BaseViewAction.kt b/app/src/androidTest/java/h_mal/appttude/com/driver/helpers/BaseViewAction.kt
new file mode 100644
index 0000000..28be005
--- /dev/null
+++ b/app/src/androidTest/java/h_mal/appttude/com/driver/helpers/BaseViewAction.kt
@@ -0,0 +1,27 @@
+package h_mal.appttude.com.driver.helpers
+
+import android.view.View
+import androidx.test.espresso.UiController
+import androidx.test.espresso.ViewAction
+import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
+import org.hamcrest.Matcher
+
+open class BaseViewAction: ViewAction {
+ override fun getDescription(): String? = setDescription()
+
+ override fun getConstraints(): Matcher = setConstraints()
+
+ override fun perform(uiController: UiController?, view: View?) {
+ setPerform(uiController, view)
+ }
+
+ open fun setDescription(): String? {
+ return null
+ }
+
+ open fun setConstraints(): Matcher {
+ return isAssignableFrom(View::class.java)
+ }
+
+ open fun setPerform(uiController: UiController?, view: View?) { }
+}
\ No newline at end of file
diff --git a/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/ApproverRobot.kt b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/ApproverRobot.kt
new file mode 100644
index 0000000..22a74a0
--- /dev/null
+++ b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/ApproverRobot.kt
@@ -0,0 +1,13 @@
+package h_mal.appttude.com.driver.robots
+
+import h_mal.appttude.com.driver.BaseTestRobot
+import h_mal.appttude.com.driver.R
+
+
+fun approver(func: ApproverRobot.() -> Unit) = ApproverRobot().apply { func() }
+class ApproverRobot : BaseTestRobot() {
+
+ fun clickApprove() = clickButton(R.id.approve)
+ fun clickDecline() = clickButton(R.id.decline)
+
+}
\ No newline at end of file
diff --git a/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/DriverOverviewRobot.kt b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/DriverOverviewRobot.kt
new file mode 100644
index 0000000..b2e3916
--- /dev/null
+++ b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/DriverOverviewRobot.kt
@@ -0,0 +1,29 @@
+package h_mal.appttude.com.driver.robots
+
+import androidx.test.espresso.Espresso.onData
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import h_mal.appttude.com.driver.BaseTestRobot
+import h_mal.appttude.com.driver.R
+import org.hamcrest.CoreMatchers.anything
+
+
+fun driverOverview(func: DriverOverviewRobot.() -> Unit) = DriverOverviewRobot().apply { func() }
+class DriverOverviewRobot : BaseTestRobot() {
+
+ fun clickOnItemAtPosition(index: Int) =
+ onData(anything())
+ .inAdapterView(withId(R.id.approvals_list))
+ .atPosition(index)
+ .perform(click())
+
+ fun matchView(position: Int, status: String) =
+ onData(anything())
+ .inAdapterView(withId(R.id.approvals_list))
+ .atPosition(position)
+ .onChildView(withText(status))
+ .check(matches(isDisplayed()))
+}
\ No newline at end of file
diff --git a/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/HomeAdminRobot.kt b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/HomeAdminRobot.kt
index aa749ba..2cd8bfb 100644
--- a/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/HomeAdminRobot.kt
+++ b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/robots/HomeAdminRobot.kt
@@ -8,10 +8,15 @@ import androidx.test.espresso.matcher.ViewMatchers.withTagKey
import h_mal.appttude.com.driver.BaseTestRobot
import h_mal.appttude.com.driver.R
import h_mal.appttude.com.driver.base.CustomViewHolder
+import h_mal.appttude.com.driver.model.DatabaseStatus
fun homeAdmin(func: HomeAdminRobot.() -> Unit) = HomeAdminRobot().apply { func() }
class HomeAdminRobot : BaseTestRobot() {
+ fun waitUntilDisplayed() {
+ matchViewWaitFor(R.id.recycler_view)
+ }
+
fun openDrawer() {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
}
@@ -39,4 +44,10 @@ class HomeAdminRobot : BaseTestRobot() {
// Click OK
onView(withId(android.R.id.button1)).perform(ViewActions.click())
}
+
+ fun showNoPermissionsDisplay() {
+ matchViewWaitFor(R.id.header)
+ matchText(R.id.header, DatabaseStatus.NO_PERMISSION.header)
+ matchText(R.id.subtext, DatabaseStatus.NO_PERMISSION.subtext)
+ }
}
\ No newline at end of file
diff --git a/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/AdminBaseTest.kt b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/AdminBaseTest.kt
new file mode 100644
index 0000000..60a6772
--- /dev/null
+++ b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/AdminBaseTest.kt
@@ -0,0 +1,15 @@
+package h_mal.appttude.com.driver.tests
+
+import h_mal.appttude.com.driver.ADMIN_EMAIL
+import h_mal.appttude.com.driver.FirebaseTest
+import h_mal.appttude.com.driver.PASSWORD
+import h_mal.appttude.com.driver.ui.MainActivity
+import kotlinx.coroutines.runBlocking
+
+open class AdminBaseTest: FirebaseTest(MainActivity::class.java) {
+ override fun beforeLaunch() {
+ runBlocking {
+ login(ADMIN_EMAIL, PASSWORD)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/DocumentApproverTest.kt b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/DocumentApproverTest.kt
new file mode 100644
index 0000000..89bfba0
--- /dev/null
+++ b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/DocumentApproverTest.kt
@@ -0,0 +1,72 @@
+package h_mal.appttude.com.driver.tests
+
+import androidx.test.espresso.Espresso
+import h_mal.appttude.com.driver.R
+import h_mal.appttude.com.driver.robots.approver
+import h_mal.appttude.com.driver.robots.driverOverview
+import h_mal.appttude.com.driver.robots.homeAdmin
+import org.junit.Test
+
+class DocumentApproverTest : AdminBaseTest() {
+
+ @Test
+ fun loginAsAdmin_approveDocumentForDriver_documentApproved() {
+ homeAdmin {
+ waitUntilDisplayed()
+ clickOnItem("kabirmhkhan@gmail.com")
+ }
+ // Approve check
+ driverOverview {
+ clickOnItemAtPosition(0)
+ }
+ approver {
+ clickApprove()
+ checkToastMessage("Document already approved")
+ Espresso.pressBack()
+ }
+ driverOverview {
+ clickOnItemAtPosition(2)
+ }
+ approver {
+ clickApprove()
+ Espresso.pressBack()
+ }
+ driverOverview {
+ matchView(2, getResourceString(R.string.approved))
+ }
+
+ // Decline check
+ driverOverview {
+ clickOnItemAtPosition(3)
+ }
+ approver {
+ clickDecline()
+ checkToastMessage("Document already declined")
+ Espresso.pressBack()
+ }
+ driverOverview {
+ clickOnItemAtPosition(1)
+ }
+ approver {
+ clickDecline()
+ Espresso.pressBack()
+ }
+ driverOverview {
+ matchView(1, getResourceString(R.string.denied))
+ }
+ }
+
+ @Test
+ fun loginAsAdmin_verifyNoDocumentForNewDriver() {
+ homeAdmin {
+ waitUntilDisplayed()
+ clickOnItem("fanasid@gmail.com")
+ }
+ driverOverview {
+ matchView(0, getResourceString(R.string.not_submitted))
+ clickOnItemAtPosition(0)
+ matchView(0, getResourceString(R.string.not_submitted))
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/UserListTest.kt b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/UserListTest.kt
index 4b5006f..6606a34 100644
--- a/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/UserListTest.kt
+++ b/app/src/androidTestAdmin/java/h_mal/appttude/com/driver/tests/UserListTest.kt
@@ -1,6 +1,7 @@
package h_mal.appttude.com.driver.tests
import h_mal.appttude.com.driver.ADMIN_EMAIL
+import h_mal.appttude.com.driver.DRIVER_EMAIL
import h_mal.appttude.com.driver.FirebaseTest
import h_mal.appttude.com.driver.robots.homeAdmin
import h_mal.appttude.com.driver.robots.login
@@ -18,7 +19,17 @@ class UserListTest : FirebaseTest(LoginActivity::class.java) {
homeAdmin {
clickOnDriverIdentifier("rsaif660@gmail.com")
submitDialog("ID45")
- waitFor(5000)
+ }
+ }
+
+ @Test
+ fun loginAsUser_unableToSeeDrivers_loggedIn() {
+ login {
+ waitFor(1100)
+ attemptLogin(DRIVER_EMAIL)
+ }
+ homeAdmin {
+ showNoPermissionsDisplay()
}
}
diff --git a/app/src/driver/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt b/app/src/driver/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt
index c4cada5..0f81ab1 100644
--- a/app/src/driver/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt
+++ b/app/src/driver/java/h_mal/appttude/com/driver/application/ApplicationViewModelFactory.kt
@@ -11,8 +11,7 @@ import h_mal.appttude.com.driver.viewmodels.*
class ApplicationViewModelFactory(
private val auth: FirebaseAuthSource,
private val database: FirebaseDatabaseSource,
- private val storage: FirebaseStorageSource,
- private val preferences: PreferenceProvider
+ private val storage: FirebaseStorageSource
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
diff --git a/app/src/driver/java/h_mal/appttude/com/driver/application/DriverApplication.kt b/app/src/driver/java/h_mal/appttude/com/driver/application/DriverApplication.kt
new file mode 100644
index 0000000..855d97a
--- /dev/null
+++ b/app/src/driver/java/h_mal/appttude/com/driver/application/DriverApplication.kt
@@ -0,0 +1,28 @@
+package h_mal.appttude.com.driver.application
+
+import android.app.Application
+import android.content.res.Resources
+import h_mal.appttude.com.driver.data.FirebaseAuthSource
+import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
+import h_mal.appttude.com.driver.data.FirebaseStorageSource
+import h_mal.appttude.com.driver.data.prefs.PreferenceProvider
+import org.kodein.di.Kodein
+import org.kodein.di.KodeinAware
+import org.kodein.di.android.x.androidXModule
+import org.kodein.di.generic.bind
+import org.kodein.di.generic.instance
+import org.kodein.di.generic.provider
+import org.kodein.di.generic.singleton
+
+class DriverApplication : BaseApplication() {
+
+ override val flavourModule = super.flavourModule.copy {
+ bind() from provider {
+ ApplicationViewModelFactory(
+ instance(),
+ instance(),
+ instance()
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/h_mal/appttude/com/driver/application/DriverApplication.kt b/app/src/main/java/h_mal/appttude/com/driver/application/BaseApplication.kt
similarity index 62%
rename from app/src/main/java/h_mal/appttude/com/driver/application/DriverApplication.kt
rename to app/src/main/java/h_mal/appttude/com/driver/application/BaseApplication.kt
index 9800149..78a4740 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/application/DriverApplication.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/application/BaseApplication.kt
@@ -4,24 +4,28 @@ import android.app.Application
import h_mal.appttude.com.driver.data.FirebaseAuthSource
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
import h_mal.appttude.com.driver.data.FirebaseStorageSource
-import h_mal.appttude.com.driver.data.prefs.PreferenceProvider
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.androidXModule
import org.kodein.di.generic.bind
-import org.kodein.di.generic.instance
-import org.kodein.di.generic.provider
import org.kodein.di.generic.singleton
-class DriverApplication : Application(), KodeinAware {
+open class BaseApplication : Application(), KodeinAware {
// Kodein aware to initialise the classes used for DI
override val kodein = Kodein.lazy {
- import(androidXModule(this@DriverApplication))
+ import(parentModule)
+ import(flavourModule)
+ }
+
+ val parentModule = Kodein.Module("Parent Module") {
+ import(androidXModule(this@BaseApplication))
bind() from singleton { FirebaseAuthSource() }
bind() from singleton { FirebaseDatabaseSource() }
bind() from singleton { FirebaseStorageSource() }
- bind() from singleton { PreferenceProvider(this@DriverApplication) }
- bind() from provider { ApplicationViewModelFactory(instance(), instance(), instance(), instance()) }
+ }
+
+ open val flavourModule = Kodein.Module("Flavour") {
+ import(parentModule)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/BaseActivity.kt b/app/src/main/java/h_mal/appttude/com/driver/base/BaseActivity.kt
index 8a5c0ab..c20577e 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/base/BaseActivity.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/base/BaseActivity.kt
@@ -1,27 +1,36 @@
package h_mal.appttude.com.driver.base
import android.content.Intent
+import android.os.Build
import android.os.Bundle
import android.view.View
+import android.view.View.OnAttachStateChangeListener
import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.inflate
+import android.widget.Toast
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelLazy
import androidx.test.espresso.IdlingResource
import androidx.viewbinding.ViewBinding
+import com.google.android.material.snackbar.BaseTransientBottomBar.BaseCallback
+import com.google.android.material.snackbar.Snackbar
import h_mal.appttude.com.driver.R
import h_mal.appttude.com.driver.application.ApplicationViewModelFactory
import h_mal.appttude.com.driver.data.ViewState
-import h_mal.appttude.com.driver.utils.*
+import h_mal.appttude.com.driver.utils.BasicIdlingResource
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
import h_mal.appttude.com.driver.utils.GenericsHelper.inflateBindingByType
+import h_mal.appttude.com.driver.utils.hide
+import h_mal.appttude.com.driver.utils.show
+import h_mal.appttude.com.driver.utils.triggerAnimation
import org.kodein.di.KodeinAware
import org.kodein.di.android.kodein
import org.kodein.di.generic.instance
-abstract class BaseActivity : AppCompatActivity(), KodeinAware {
+abstract class BaseActivity : AppCompatActivity(),
+ KodeinAware {
// The Idling Resource which will be null in production.
private var mIdlingResource: BasicIdlingResource? = null
private lateinit var loadingView: View
@@ -108,7 +117,7 @@ abstract class BaseActivity : AppCompatActi
* Called in case of failure or some error emitted from the liveData in viewModel
*/
open fun onFailure(error: String?) {
- error?.let { displayToast(it) }
+ error?.let { showToast(it) }
loadingView.fadeOut()
mIdlingResource?.setIdleState(true)
}
@@ -139,6 +148,44 @@ abstract class BaseActivity : AppCompatActi
super.onBackPressed()
}
+ fun showToast(message: String) {
+ val toast = Toast.makeText(this, message, Toast.LENGTH_LONG)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ toast.addCallback(object : Toast.Callback() {
+ override fun onToastHidden() {
+ super.onToastHidden()
+ mIdlingResource?.setIdleState(true)
+ }
+ override fun onToastShown() {
+ super.onToastShown()
+ mIdlingResource?.setIdleState(false)
+ }
+ })
+ } else {
+
+ }
+ toast.show()
+ }
+
+ fun showSnackBar(message: String) {
+ val snackbar = Snackbar.make(
+ window.decorView.findViewById(android.R.id.content),
+ message,
+ Snackbar.LENGTH_LONG
+ )
+ snackbar.addCallback(object : BaseCallback() {
+ override fun onShown(transientBottomBar: Snackbar?) {
+ super.onShown(transientBottomBar)
+ mIdlingResource?.setIdleState(false)
+ }
+ override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
+ super.onDismissed(transientBottomBar, event)
+ mIdlingResource?.setIdleState(true)
+ }
+ })
+ snackbar.show()
+ }
+
/**
* Only called from test, creates and returns a new [BasicIdlingResource].
*/
diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/BaseFirebaseAdapter.kt b/app/src/main/java/h_mal/appttude/com/driver/base/BaseFirebaseAdapter.kt
index a102837..62b13c5 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/base/BaseFirebaseAdapter.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/base/BaseFirebaseAdapter.kt
@@ -8,15 +8,20 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
import androidx.viewbinding.ViewBinding
import com.firebase.ui.database.FirebaseRecyclerAdapter
import com.firebase.ui.database.FirebaseRecyclerOptions
+import com.google.firebase.database.DatabaseError
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
import h_mal.appttude.com.driver.utils.GenericsHelper.inflateBindingByType
import java.nio.ByteBuffer
-open class BaseFirebaseAdapter(options: FirebaseRecyclerOptions, private val layoutInflater: LayoutInflater):
+open class BaseFirebaseAdapter(
+ options: FirebaseRecyclerOptions,
+ private val layoutInflater: LayoutInflater
+) :
FirebaseRecyclerAdapter>(options) {
- private val connectivityManager = layoutInflater.context.getSystemService(ConnectivityManager::class.java) as ConnectivityManager
+ private val connectivityManager =
+ layoutInflater.context.getSystemService(ConnectivityManager::class.java) as ConnectivityManager
private var _binding: VB? = null
val binding: VB
@@ -32,7 +37,7 @@ open class BaseFirebaseAdapter(options: FirebaseRecycl
return CustomViewHolder(requireNotNull(_binding))
}
- override fun onBindViewHolder(holder: CustomViewHolder, position: Int, model: T) { }
+ override fun onBindViewHolder(holder: CustomViewHolder, position: Int, model: T) {}
override fun getItemId(position: Int): Long {
return snapshots.getSnapshot(position).key?.toByteArray()
@@ -50,6 +55,26 @@ open class BaseFirebaseAdapter(options: FirebaseRecycl
}
open fun connectionLost() {}
+ override fun onDataChanged() {
+ super.onDataChanged()
+ if (itemCount == 0) emptyList()
+ }
+ override fun onError(error: DatabaseError) {
+ super.onError(error)
+ when (error.code) {
+ DatabaseError.PERMISSION_DENIED -> permissionsDenied()
+ DatabaseError.DISCONNECTED, DatabaseError.UNAVAILABLE, DatabaseError.NETWORK_ERROR -> noConnection()
+ DatabaseError.EXPIRED_TOKEN, DatabaseError.OPERATION_FAILED, DatabaseError.INVALID_TOKEN, DatabaseError.MAX_RETRIES -> authorizationError()
+ else -> cannotRetrieve()
+ }
+
+ }
+
+ open fun permissionsDenied() {}
+ open fun noConnection() {}
+ open fun cannotRetrieve() {}
+ open fun authorizationError() {}
+ open fun emptyList() {}
}
-class CustomViewHolder(val viewBinding: VB): ViewHolder(viewBinding.root)
\ No newline at end of file
+class CustomViewHolder(val viewBinding: VB) : ViewHolder(viewBinding.root)
\ No newline at end of file
diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/BaseFragment.kt b/app/src/main/java/h_mal/appttude/com/driver/base/BaseFragment.kt
index ba1d5b6..e80b26e 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/base/BaseFragment.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/base/BaseFragment.kt
@@ -20,7 +20,6 @@ import h_mal.appttude.com.driver.utils.PermissionsUtils
import org.kodein.di.KodeinAware
import org.kodein.di.android.x.kodein
import org.kodein.di.generic.instance
-import java.io.File
abstract class BaseFragment : Fragment(), KodeinAware {
@@ -43,7 +42,6 @@ abstract class BaseFragment : Fragment(), K
multipleImage = true
}
-
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -169,4 +167,7 @@ abstract class BaseFragment : Fragment(), K
}
}
}
+
+ fun showToast(message: String) = (activity as BaseActivity<*, *>).showToast(message)
+ fun showSnackBar(message: String) = (activity as BaseActivity<*, *>).showSnackBar(message)
}
\ No newline at end of file
diff --git a/app/src/main/java/h_mal/appttude/com/driver/base/BaseViewModel.kt b/app/src/main/java/h_mal/appttude/com/driver/base/BaseViewModel.kt
index 7580984..f32a029 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/base/BaseViewModel.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/base/BaseViewModel.kt
@@ -20,6 +20,9 @@ abstract class BaseViewModel : ViewModel() {
uiState.postValue(ViewState.HasError(Event(error)))
}
+ /*
+ * All in one function for trying an operation and handling its start and failure
+ */
suspend fun doTryOperation(
defaultErrorMessage: String?,
operation: suspend () -> Unit
diff --git a/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseAuthSource.kt b/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseAuthSource.kt
index 8820572..dec0995 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseAuthSource.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseAuthSource.kt
@@ -2,7 +2,11 @@ package h_mal.appttude.com.driver.data
import android.net.Uri
import com.google.android.gms.tasks.Task
-import com.google.firebase.auth.*
+import com.google.firebase.auth.AuthResult
+import com.google.firebase.auth.EmailAuthProvider
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.auth.FirebaseUser
+import com.google.firebase.auth.UserProfileChangeRequest
import java.io.IOException
class FirebaseAuthSource : FirebaseAuthentication {
diff --git a/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseDatabaseSource.kt b/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseDatabaseSource.kt
index 5d38537..adbeee4 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseDatabaseSource.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/data/FirebaseDatabaseSource.kt
@@ -36,7 +36,7 @@ class FirebaseDatabaseSource {
return data
}
- fun getDatabaseReferenceFromLink(link: String) = database.getReferenceFromUrl(link)
+ fun getDatabaseRefFromPath(path: String) = database.getReference(path)
val users = database.getReference(USER_CONST)
@@ -46,6 +46,7 @@ class FirebaseDatabaseSource {
fun getVehicleRef(uid: String) = getUserRef(uid).child(VEHICLE_PROFILE)
fun getDriverRef(uid: String) = getUserRef(uid).child(DRIVER_PROFILE)
fun getApprovalsRef(uid: String) = getUserRef(uid).child(APPROVALS)
+ fun getDocumentApprovalRef(uid: String, document: String) = getApprovalsRef(uid).child(document)
fun getArchiveRef(uid: String) = getUserRef(uid).child(ARCHIVE)
fun getUserRoleRef(uid: String) = getUserRef(uid).child(PROFILE_ROLE)
fun getDriverNumberRef(uid: String) = getUserRef(uid).child(DRIVER_NUMBER)
diff --git a/app/src/main/java/h_mal/appttude/com/driver/ui/update/UpdateActivity.kt b/app/src/main/java/h_mal/appttude/com/driver/ui/update/UpdateActivity.kt
index 71d6691..ea00689 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/ui/update/UpdateActivity.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/ui/update/UpdateActivity.kt
@@ -3,7 +3,6 @@ package h_mal.appttude.com.driver.ui.update
import h_mal.appttude.com.driver.base.BaseActivity
import h_mal.appttude.com.driver.data.FirebaseCompletion
import h_mal.appttude.com.driver.databinding.UpdateActivityBinding
-import h_mal.appttude.com.driver.utils.displayToast
import h_mal.appttude.com.driver.viewmodels.UpdateUserViewModel
class UpdateActivity : BaseActivity() {
@@ -11,7 +10,7 @@ class UpdateActivity : BaseActivity(
override fun onSuccess(data: Any?) {
super.onSuccess(data)
when (data) {
- is FirebaseCompletion.Changed -> displayToast(data.message)
+ is FirebaseCompletion.Changed -> showToast(data.message)
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseException.kt b/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseException.kt
new file mode 100644
index 0000000..5949f2a
--- /dev/null
+++ b/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseException.kt
@@ -0,0 +1,55 @@
+package h_mal.appttude.com.driver.utils
+
+import com.google.firebase.database.DatabaseError
+
+class FirebaseException(
+ private val databaseError: DatabaseError
+) : RuntimeException(databaseError.message, databaseError.toException()) {
+
+ fun getCode() = databaseError.code
+ fun getDetails() = databaseError.details
+
+ fun getErrorStatus(): Status {
+ return Status.getByScore(getCode()) ?: Status.UNKNOWN_ERROR
+ }
+
+ enum class Status(private val code: Int) {
+ DATA_STALE(-1),
+ /** The server indicated that this operation failed */
+ OPERATION_FAILED(-2),
+ /** This client does not have permission to perform this operation */
+ PERMISSION_DENIED(-3),
+ /** The operation had to be aborted due to a network disconnect */
+ DISCONNECTED(-4),
+ /** The supplied auth token has expired */
+ EXPIRED_TOKEN (-6),
+ /**
+ * The specified authentication token is invalid. This can occur when the token is malformed,
+ * expired, or the secret that was used to generate it has been revoked.
+ */
+ INVALID_TOKEN(-7),
+ /** The transaction had too many retries */
+ MAX_RETRIES(-8),
+ /** The transaction was overridden by a subsequent set */
+ OVERRIDDEN_BY_SET(-9),
+ /** The service is unavailable */
+ UNAVAILABLE(-10),
+ /** An exception occurred in user code */
+ USER_CODE_EXCEPTION(-11),
+ /** The operation could not be performed due to a network error. */
+ NETWORK_ERROR(-24),
+ /** The write was canceled locally */
+ WRITE_CANCELED(-25),
+ /**
+ * An unknown error occurred. Please refer to the error message and error details for more
+ * information.
+ */
+ UNKNOWN_ERROR(-999);
+
+ companion object {
+ infix fun getByScore(value: Int): Status? =
+ Status.values().firstOrNull { it.code == value }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseUtils.kt b/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseUtils.kt
index c2270d0..00b35bf 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseUtils.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/utils/FirebaseUtils.kt
@@ -30,7 +30,6 @@ suspend fun DatabaseReference.singleValueEvent(): EventResponse = suspendCorouti
/**
* Read database reference once {@link #DatabaseReference.addListenerForSingleValueEvent}
*
- *
* @return T
*/
suspend inline fun DatabaseReference.getDataFromDatabaseRef(): T? {
@@ -39,7 +38,23 @@ suspend inline fun DatabaseReference.getDataFromDatabaseRef():
response.snapshot.getValue(T::class.java)
}
is EventResponse.Cancelled -> {
- throw response.error.toException()
+ throw FirebaseException(response.error)
+ }
+ }
+}
+
+/**
+ * Read database reference once {@link #DatabaseReference.addListenerForSingleValueEvent}
+ *
+ * @return T
+ */
+suspend inline fun DatabaseReference.getListDataFromDatabaseRef(): List {
+ return when (val response: EventResponse = singleValueEvent()) {
+ is EventResponse.Changed -> {
+ response.snapshot.children.map { it.getValue(T::class.java) }
+ }
+ is EventResponse.Cancelled -> {
+ throw FirebaseException(response.error)
}
}
}
@@ -50,7 +65,7 @@ suspend fun DatabaseReference.getDataFromDatabaseRef(clazz : Class):
response.snapshot.getValue(clazz)
}
is EventResponse.Cancelled -> {
- throw response.error.toException()
+ throw FirebaseException(response.error)
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/h_mal/appttude/com/driver/utils/ViewUtils.kt b/app/src/main/java/h_mal/appttude/com/driver/utils/ViewUtils.kt
index febf32d..ed50c3d 100644
--- a/app/src/main/java/h_mal/appttude/com/driver/utils/ViewUtils.kt
+++ b/app/src/main/java/h_mal/appttude/com/driver/utils/ViewUtils.kt
@@ -2,7 +2,6 @@ package h_mal.appttude.com.driver.utils
import android.annotation.SuppressLint
import android.app.Activity
-import android.content.Context
import android.content.Intent
import android.content.res.Resources
import android.graphics.Bitmap
@@ -16,7 +15,6 @@ import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
-import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment
@@ -33,14 +31,6 @@ fun View.hide() {
this.visibility = View.GONE
}
-fun Context.displayToast(message: String) {
- Toast.makeText(this, message, Toast.LENGTH_LONG).show()
-}
-
-fun Fragment.displayToast(message: String) {
- requireContext().displayToast(message)
-}
-
fun EditText.setEnterPressedListener(action: () -> Unit) {
setOnEditorActionListener(TextView.OnEditorActionListener { _, id, _ ->
if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) {
diff --git a/app/src/main/res/drawable-hdpi/splash_screen.png b/app/src/main/res/drawable-hdpi/splash_screen.png
new file mode 100644
index 0000000..f707bb5
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/splash_screen.png differ
diff --git a/app/src/main/res/drawable-mdpi/splash_screen.png b/app/src/main/res/drawable-mdpi/splash_screen.png
new file mode 100644
index 0000000..01c44b4
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/splash_screen.png differ
diff --git a/app/src/main/res/drawable-xhdpi/splash_screen.png b/app/src/main/res/drawable-xhdpi/splash_screen.png
new file mode 100644
index 0000000..4abd07a
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/splash_screen.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/splash_screen.png b/app/src/main/res/drawable-xxhdpi/splash_screen.png
new file mode 100644
index 0000000..a80cb6e
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/splash_screen.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/splash_screen.png b/app/src/main/res/drawable-xxxhdpi/splash_screen.png
new file mode 100644
index 0000000..fe8defc
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/splash_screen.png differ
diff --git a/app/src/main/res/drawable/baseline_check_24.xml b/app/src/main/res/drawable/baseline_check_24.xml
new file mode 100644
index 0000000..2501e9f
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_check_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/baseline_clear_24.xml b/app/src/main/res/drawable/baseline_clear_24.xml
new file mode 100644
index 0000000..70db409
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_clear_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/baseline_inbox_24.xml b/app/src/main/res/drawable/baseline_inbox_24.xml
new file mode 100644
index 0000000..5857b5d
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_inbox_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/mipmap-hdpi/drawable-hdpi/welcome_background.png b/app/src/main/res/mipmap-hdpi/drawable-hdpi/welcome_background.png
deleted file mode 100644
index 847c1e3..0000000
Binary files a/app/src/main/res/mipmap-hdpi/drawable-hdpi/welcome_background.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-hdpi/drawable-ldpi/welcome_background.png b/app/src/main/res/mipmap-hdpi/drawable-ldpi/welcome_background.png
deleted file mode 100644
index 20445c6..0000000
Binary files a/app/src/main/res/mipmap-hdpi/drawable-ldpi/welcome_background.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-hdpi/drawable-mdpi/welcome_background.png b/app/src/main/res/mipmap-hdpi/drawable-mdpi/welcome_background.png
deleted file mode 100644
index 910c85f..0000000
Binary files a/app/src/main/res/mipmap-hdpi/drawable-mdpi/welcome_background.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-hdpi/drawable-xhdpi/welcome_background.png b/app/src/main/res/mipmap-hdpi/drawable-xhdpi/welcome_background.png
deleted file mode 100644
index f269958..0000000
Binary files a/app/src/main/res/mipmap-hdpi/drawable-xhdpi/welcome_background.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-hdpi/drawable-xxhdpi/welcome_background.png b/app/src/main/res/mipmap-hdpi/drawable-xxhdpi/welcome_background.png
deleted file mode 100644
index fd5cc30..0000000
Binary files a/app/src/main/res/mipmap-hdpi/drawable-xxhdpi/welcome_background.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-hdpi/drawable-xxxhdpi/welcome_background.png b/app/src/main/res/mipmap-hdpi/drawable-xxxhdpi/welcome_background.png
deleted file mode 100644
index 3316f35..0000000
Binary files a/app/src/main/res/mipmap-hdpi/drawable-xxxhdpi/welcome_background.png and /dev/null differ
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index de7e047..b955ccf 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -102,4 +102,18 @@
Not submitted
You have no drivers
approval status
+ Approve
+ Deny
+ Decline
+ No drivers to show
+ There are no drivers present for your organisation.
+ You do not have permissions to view
+ You are not a super user. Contact us to get super user access.
+ Cannot retrieve data.
+ Check you are logged in correctly and have a working connection.
+ No connection
+ Make you have a valid internet connection.
+ Authentication has failed
+ There is a problem with authentication.
+ Image icon for feedback view.
diff --git a/database.rules.json b/database.rules.json
index 378f192..fadc7ba 100644
--- a/database.rules.json
+++ b/database.rules.json
@@ -1,9 +1,17 @@
{
"rules": {
"user": {
- ".read": true,
+ ".read": "root.child('user').child(auth.uid).child('role').val() == 'admin'",
"$user_id": {
- ".write": "$user_id === auth.uid"
+ ".write": "$user_id === auth.uid",
+ ".read": "$user_id === auth.uid",
+ "driver_number": {
+ ".write": "root.child('user').child(auth.uid).child('role').val() == 'admin'",
+ ".read": "root.child('user').child(auth.uid).child('role').val() == 'admin'"
+ },
+ "approvalsObject": {
+ ".write": "root.child('user').child(auth.uid).child('role').val() == 'admin'"
+ }
}
}
}