mirror of
https://github.com/hmalik144/Driver.git
synced 2025-12-10 02:45:20 +00:00
Storage permission dispatcher (#33)
- storage permissions request updated - test suite expanded
This commit is contained in:
@@ -20,16 +20,10 @@ commands:
|
|||||||
command: |
|
command: |
|
||||||
echo "$GOOGLE_SERVICES_KEY" > "app/google-services.json"
|
echo "$GOOGLE_SERVICES_KEY" > "app/google-services.json"
|
||||||
- android/restore-gradle-cache
|
- android/restore-gradle-cache
|
||||||
build_gradle:
|
|
||||||
description: Build the gradle
|
|
||||||
steps:
|
|
||||||
- android/restore-gradle-cache
|
|
||||||
- run:
|
- run:
|
||||||
name: Download Dependencies
|
name: allow gradle
|
||||||
command: |
|
command: |
|
||||||
sudo chmod +x ./gradlew
|
sudo chmod +x ./gradlew
|
||||||
./gradlew androidDependencies
|
|
||||||
- android/save-gradle-cache
|
|
||||||
run_tests:
|
run_tests:
|
||||||
description: run non-instrumentation tests for flavour specified
|
description: run non-instrumentation tests for flavour specified
|
||||||
parameters:
|
parameters:
|
||||||
@@ -38,11 +32,11 @@ commands:
|
|||||||
default: "Driver"
|
default: "Driver"
|
||||||
steps:
|
steps:
|
||||||
# The next step will run the unit tests
|
# The next step will run the unit tests
|
||||||
- build_gradle
|
|
||||||
- run:
|
- run:
|
||||||
name: Run non-instrumentation unit tests
|
name: Run non-instrumentation unit tests
|
||||||
command: |
|
command: |
|
||||||
./gradlew test<< parameters.flavour >>DebugUnitTest --continue
|
./gradlew test<< parameters.flavour >>DebugUnitTest
|
||||||
|
- android/save-gradle-cache
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: app/build/reports
|
path: app/build/reports
|
||||||
destination: reports
|
destination: reports
|
||||||
@@ -53,10 +47,9 @@ commands:
|
|||||||
parameters:
|
parameters:
|
||||||
flavour:
|
flavour:
|
||||||
type: string
|
type: string
|
||||||
default: "AtlasWeather"
|
default: "Driver"
|
||||||
steps:
|
steps:
|
||||||
# Download and cache dependencies
|
# Download and cache dependencies
|
||||||
- build_gradle
|
|
||||||
- run:
|
- run:
|
||||||
name: Setup subtree for test data
|
name: Setup subtree for test data
|
||||||
command: |
|
command: |
|
||||||
@@ -78,9 +71,7 @@ commands:
|
|||||||
post-emulator-launch-assemble-command: ./gradlew assemble<< parameters.flavour >>DebugAndroidTest
|
post-emulator-launch-assemble-command: ./gradlew assemble<< parameters.flavour >>DebugAndroidTest
|
||||||
test-command: ./gradlew connected<< parameters.flavour >>DebugAndroidTest
|
test-command: ./gradlew connected<< parameters.flavour >>DebugAndroidTest
|
||||||
system-image: system-images;android-25;google_apis;x86
|
system-image: system-images;android-25;google_apis;x86
|
||||||
pull-data: true
|
pre-test-command: adb push driver_app_data/images /sdcard/Camera
|
||||||
pull-data-path: /storage/emulated/0/Android/data/
|
|
||||||
pull-data-target: ~/app-data
|
|
||||||
pre-emulator-wait-steps:
|
pre-emulator-wait-steps:
|
||||||
# Start firebase emulator in the background while waiting to start testing
|
# Start firebase emulator in the background while waiting to start testing
|
||||||
- run:
|
- run:
|
||||||
@@ -94,6 +85,13 @@ commands:
|
|||||||
paths:
|
paths:
|
||||||
- ~/.cache/firebase/emulators/
|
- ~/.cache/firebase/emulators/
|
||||||
key: emulator-cache-v1-{{ epoch }}
|
key: emulator-cache-v1-{{ epoch }}
|
||||||
|
# store screenshots for failed ui tests
|
||||||
|
- when:
|
||||||
|
condition: on_fail
|
||||||
|
steps:
|
||||||
|
- store_artifacts:
|
||||||
|
path: app/build/outputs/connected_android_test_additional_output/
|
||||||
|
destination: connected_android_test
|
||||||
# store test reports
|
# store test reports
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: app/build/reports/androidTests/connected
|
path: app/build/reports/androidTests/connected
|
||||||
@@ -128,7 +126,6 @@ commands:
|
|||||||
name: Setup playstore key
|
name: Setup playstore key
|
||||||
command: |
|
command: |
|
||||||
echo "$GOOGLE_PLAY_KEY" > "google-play-key.json"
|
echo "$GOOGLE_PLAY_KEY" > "google-play-key.json"
|
||||||
- build_gradle
|
|
||||||
- run:
|
- run:
|
||||||
name: Run fastlane command to deploy to playstore
|
name: Run fastlane command to deploy to playstore
|
||||||
command: |
|
command: |
|
||||||
@@ -156,8 +153,23 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Checkout the code and its submodule as the first step.
|
# Checkout the code and its submodule as the first step.
|
||||||
- setup_repo
|
- setup_repo
|
||||||
# - run_tests:
|
- run_tests:
|
||||||
# flavour: << parameters.flavour >>
|
flavour: << parameters.flavour >>
|
||||||
|
run_instrumentation_test:
|
||||||
|
# Parameters used for determining
|
||||||
|
parameters:
|
||||||
|
flavour:
|
||||||
|
type: string
|
||||||
|
default: "Driver"
|
||||||
|
# These next lines define the Android machine image executor.
|
||||||
|
# See: https://circleci.com/docs/2.0/executor-types/
|
||||||
|
executor:
|
||||||
|
name: android/android-machine
|
||||||
|
tag: 2023.05.1
|
||||||
|
# Add steps to the job
|
||||||
|
# See: https://circleci.com/docs/2.0/configuration-reference/#steps
|
||||||
|
steps:
|
||||||
|
- setup_repo
|
||||||
- run_ui_tests:
|
- run_ui_tests:
|
||||||
flavour: << parameters.flavour >>
|
flavour: << parameters.flavour >>
|
||||||
deploy-to-playstore:
|
deploy-to-playstore:
|
||||||
@@ -187,6 +199,14 @@ workflows:
|
|||||||
branches:
|
branches:
|
||||||
ignore:
|
ignore:
|
||||||
- main_admin
|
- main_admin
|
||||||
|
- run_instrumentation_test:
|
||||||
|
context: appttude
|
||||||
|
flavour: "Driver"
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- main_driver
|
||||||
- deploy-to-playstore:
|
- deploy-to-playstore:
|
||||||
context: appttude
|
context: appttude
|
||||||
flavour: "Driver"
|
flavour: "Driver"
|
||||||
@@ -195,7 +215,7 @@ workflows:
|
|||||||
only:
|
only:
|
||||||
- main_driver
|
- main_driver
|
||||||
requires:
|
requires:
|
||||||
- build-and-test
|
- run_instrumentation_test
|
||||||
build-release-admin:
|
build-release-admin:
|
||||||
jobs:
|
jobs:
|
||||||
- build-and-test:
|
- build-and-test:
|
||||||
@@ -205,12 +225,20 @@ workflows:
|
|||||||
branches:
|
branches:
|
||||||
ignore:
|
ignore:
|
||||||
- main_driver
|
- main_driver
|
||||||
- deploy-to-playstore:
|
- run_instrumentation_test:
|
||||||
context: appttude
|
context: appttude
|
||||||
flavour: "Admin"
|
flavour: "Admin"
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- main_admin
|
||||||
|
- deploy-to-playstore:
|
||||||
|
context: appttude
|
||||||
|
flavour: "Driver"
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- main_admin
|
- main_admin
|
||||||
requires:
|
requires:
|
||||||
- build-and-test
|
- run_instrumentation_test
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -27,3 +27,5 @@ local
|
|||||||
# Firebase emulator
|
# Firebase emulator
|
||||||
database-debug.log
|
database-debug.log
|
||||||
firebase-debug.log
|
firebase-debug.log
|
||||||
|
# Subtree
|
||||||
|
driver_app_data
|
||||||
|
|||||||
@@ -166,4 +166,10 @@ dependencies {
|
|||||||
androidTestImplementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
androidTestImplementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
||||||
/ * screenshot library */
|
/ * screenshot library */
|
||||||
androidTestImplementation 'tools.fastlane:screengrab:2.1.1'
|
androidTestImplementation 'tools.fastlane:screengrab:2.1.1'
|
||||||
|
/ * Permissions dispatcher * /
|
||||||
|
def dispatcher_ver = "4.9.2"
|
||||||
|
implementation "com.github.permissions-dispatcher:permissionsdispatcher:${dispatcher_ver}"
|
||||||
|
kapt "com.github.permissions-dispatcher:permissionsdispatcher-processor:${dispatcher_ver}"
|
||||||
|
/ * Date utils * /
|
||||||
|
implementation 'net.danlew:android.joda:2.12.5'
|
||||||
}
|
}
|
||||||
|
|||||||
9
app/src/androidTest/assets/driver_details.json
Normal file
9
app/src/androidTest/assets/driver_details.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"address": "123 test street update",
|
||||||
|
"dateFirst": "26/01/2019",
|
||||||
|
"dob": "26/01/1979",
|
||||||
|
"forenames": "Alex Smith",
|
||||||
|
"driverPic": "driver_profile_pic.jpg",
|
||||||
|
"ni": "NI 12 34 56 A",
|
||||||
|
"postcode": "EC1V 2AL"
|
||||||
|
}
|
||||||
5
app/src/androidTest/assets/drivers_license.json
Normal file
5
app/src/androidTest/assets/drivers_license.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"licenseExpiry": "27/04/2019",
|
||||||
|
"licenseImageString": "driver_license_driver.jpg",
|
||||||
|
"licenseNumber": "FARME100165AB5EW"
|
||||||
|
}
|
||||||
7
app/src/androidTest/assets/insurance_details.json
Normal file
7
app/src/androidTest/assets/insurance_details.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"expiryDate": "03/02/2019",
|
||||||
|
"insurerName": "Insurer",
|
||||||
|
"photoStrings": [
|
||||||
|
"driver_insurance.jpg"
|
||||||
|
]
|
||||||
|
}
|
||||||
4
app/src/androidTest/assets/log_book.json
Normal file
4
app/src/androidTest/assets/log_book.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"photoString": "driver_logbook.jpg",
|
||||||
|
"v5cnumber": "NJ59NTV"
|
||||||
|
}
|
||||||
4
app/src/androidTest/assets/mot_details.json
Normal file
4
app/src/androidTest/assets/mot_details.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"motExpiry": "11/06/2019",
|
||||||
|
"motImageString": "driver_mot.jpg"
|
||||||
|
}
|
||||||
5
app/src/androidTest/assets/private_hire_license.json
Normal file
5
app/src/androidTest/assets/private_hire_license.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"phExpiry": "27/04/2019",
|
||||||
|
"phImageString": "driver_license_private_hire.jpg",
|
||||||
|
"phNumber": "987651"
|
||||||
|
}
|
||||||
5
app/src/androidTest/assets/private_hire_vehicle.json
Normal file
5
app/src/androidTest/assets/private_hire_vehicle.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"phCarExpiry": "28/01/2019",
|
||||||
|
"phCarImageString": "driver_license_private_hire_car.jpg",
|
||||||
|
"phCarNumber": "4602060501"
|
||||||
|
}
|
||||||
11
app/src/androidTest/assets/vehicle_details.json
Normal file
11
app/src/androidTest/assets/vehicle_details.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"colour": "Black",
|
||||||
|
"keeperAddress": "483 Green lanes London",
|
||||||
|
"keeperName": "Adam Cars Ltd",
|
||||||
|
"keeperPostCode": "N13 4BS",
|
||||||
|
"make": "Toyota",
|
||||||
|
"model": "Prius",
|
||||||
|
"reg": "NG59ERY",
|
||||||
|
"seized": false,
|
||||||
|
"startDate": "04/02/2019"
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import android.content.res.Resources
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.DatePicker
|
import android.widget.DatePicker
|
||||||
import android.widget.ListView
|
import androidx.annotation.IdRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||||
import androidx.test.espresso.Espresso.onData
|
import androidx.test.espresso.Espresso.onData
|
||||||
@@ -17,13 +17,13 @@ import androidx.test.espresso.ViewAction
|
|||||||
import androidx.test.espresso.ViewInteraction
|
import androidx.test.espresso.ViewInteraction
|
||||||
import androidx.test.espresso.action.ViewActions
|
import androidx.test.espresso.action.ViewActions
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.action.ViewActions.scrollTo
|
||||||
import androidx.test.espresso.action.ViewActions.swipeDown
|
import androidx.test.espresso.action.ViewActions.swipeDown
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.contrib.PickerActions
|
import androidx.test.espresso.contrib.PickerActions
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||||
import androidx.test.espresso.intent.Intents
|
import androidx.test.espresso.intent.Intents
|
||||||
import androidx.test.espresso.intent.Intents.intending
|
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.intent.matcher.IntentMatchers.hasAction
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
|
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||||
@@ -41,34 +41,40 @@ import java.io.File
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
open class BaseTestRobot {
|
open class BaseTestRobot {
|
||||||
|
|
||||||
fun fillEditText(resId: Int, text: String?): ViewInteraction =
|
fun fillEditText(@IdRes resId: Int, text: String?): ViewInteraction =
|
||||||
onView(withId(resId)).perform(
|
onView(withId(resId)).perform(
|
||||||
ViewActions.replaceText(text),
|
ViewActions.replaceText(text),
|
||||||
ViewActions.closeSoftKeyboard()
|
ViewActions.closeSoftKeyboard()
|
||||||
)
|
)
|
||||||
|
|
||||||
fun clickButton(resId: Int): ViewInteraction =
|
fun scrollAndFillEditText(@IdRes resId: Int, text: String?): ViewInteraction =
|
||||||
|
onView(withId(resId)).perform(
|
||||||
|
scrollTo(),
|
||||||
|
ViewActions.replaceText(text),
|
||||||
|
ViewActions.closeSoftKeyboard()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun clickButton(@IdRes resId: Int): ViewInteraction =
|
||||||
onView((withId(resId))).perform(click())
|
onView((withId(resId))).perform(click())
|
||||||
|
|
||||||
fun matchView(resId: Int): ViewInteraction = onView(withId(resId))
|
fun matchView(@IdRes resId: Int): ViewInteraction = onView(withId(resId))
|
||||||
|
|
||||||
fun matchViewWaitFor(resId: Int): ViewInteraction = waitForView(withId(resId))
|
fun matchViewWaitFor(@IdRes resId: Int): ViewInteraction = waitForView(withId(resId))
|
||||||
|
|
||||||
fun matchText(viewInteraction: ViewInteraction, text: String): ViewInteraction = viewInteraction
|
fun matchText(viewInteraction: ViewInteraction, text: String): ViewInteraction = viewInteraction
|
||||||
.check(matches(withText(text)))
|
.check(matches(withText(text)))
|
||||||
|
|
||||||
fun matchText(viewId: Int, textId: Int): ViewInteraction = onView(withId(viewId))
|
fun matchText(@StringRes stringId:Int): ViewInteraction = onView(withText(stringId))
|
||||||
.check(matches(withText(textId)))
|
|
||||||
|
|
||||||
fun matchText(resId: Int, text: String): ViewInteraction = matchText(matchView(resId), text)
|
fun matchText(@IdRes resId: Int, text: String): ViewInteraction = matchText(matchView(resId), text)
|
||||||
|
|
||||||
fun clickListItem(listRes: Int, position: Int) {
|
fun clickListItem(@IdRes listRes: Int, position: Int) {
|
||||||
onData(anything())
|
onData(anything())
|
||||||
.inAdapterView(allOf(withId(listRes)))
|
.inAdapterView(allOf(withId(listRes)))
|
||||||
.atPosition(position).perform(click())
|
.atPosition(position).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <VH : ViewHolder> scrollToRecyclerItem(recyclerId: Int, text: String): ViewInteraction? {
|
fun <VH : ViewHolder> scrollToRecyclerItem(@IdRes recyclerId: Int, text: String): ViewInteraction? {
|
||||||
return matchView(recyclerId)
|
return matchView(recyclerId)
|
||||||
.perform(
|
.perform(
|
||||||
// scrollTo will fail the test if no item matches.
|
// scrollTo will fail the test if no item matches.
|
||||||
@@ -78,7 +84,7 @@ open class BaseTestRobot {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <VH : ViewHolder> scrollToRecyclerItem(recyclerId: Int, resIdForString: Int): ViewInteraction? {
|
fun <VH : ViewHolder> scrollToRecyclerItem(@IdRes recyclerId: Int, resIdForString: Int): ViewInteraction? {
|
||||||
return matchView(recyclerId)
|
return matchView(recyclerId)
|
||||||
.perform(
|
.perform(
|
||||||
// scrollTo will fail the test if no item matches.
|
// scrollTo will fail the test if no item matches.
|
||||||
@@ -88,7 +94,7 @@ open class BaseTestRobot {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <VH : ViewHolder> scrollToRecyclerItemByPosition(recyclerId: Int, position: Int): ViewInteraction? {
|
fun <VH : ViewHolder> scrollToRecyclerItemByPosition(@IdRes recyclerId: Int, position: Int): ViewInteraction? {
|
||||||
return matchView(recyclerId)
|
return matchView(recyclerId)
|
||||||
.perform(
|
.perform(
|
||||||
// scrollTo will fail the test if no item matches.
|
// scrollTo will fail the test if no item matches.
|
||||||
@@ -96,7 +102,7 @@ open class BaseTestRobot {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <VH : ViewHolder> clickViewInRecycler(recyclerId: Int, text: String) {
|
fun <VH : ViewHolder> clickViewInRecycler(@IdRes recyclerId: Int, text: String) {
|
||||||
matchView(recyclerId)
|
matchView(recyclerId)
|
||||||
.perform(
|
.perform(
|
||||||
// scrollTo will fail the test if no item matches.
|
// scrollTo will fail the test if no item matches.
|
||||||
@@ -104,7 +110,7 @@ open class BaseTestRobot {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <VH : ViewHolder> clickViewInRecycler(recyclerId: Int, resIdForString: Int) {
|
fun <VH : ViewHolder> clickViewInRecycler(@IdRes recyclerId: Int, resIdForString: Int) {
|
||||||
matchView(recyclerId)
|
matchView(recyclerId)
|
||||||
.perform(
|
.perform(
|
||||||
// scrollTo will fail the test if no item matches.
|
// scrollTo will fail the test if no item matches.
|
||||||
@@ -112,7 +118,7 @@ open class BaseTestRobot {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <VH : ViewHolder> clickSubViewInRecycler(recyclerId: Int, text: String, subView: Int) {
|
fun <VH : ViewHolder> clickSubViewInRecycler(@IdRes recyclerId: Int, text: String, subView: Int) {
|
||||||
scrollToRecyclerItem<VH>(recyclerId, text)
|
scrollToRecyclerItem<VH>(recyclerId, text)
|
||||||
?.perform(
|
?.perform(
|
||||||
// scrollTo will fail the test if no item matches.
|
// scrollTo will fail the test if no item matches.
|
||||||
@@ -128,13 +134,13 @@ open class BaseTestRobot {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkErrorOnTextEntry(resId: Int, errorMessage: String): ViewInteraction =
|
fun checkErrorOnTextEntry(@IdRes resId: Int, errorMessage: String): ViewInteraction =
|
||||||
onView(withId(resId)).check(matches(checkErrorMessage(errorMessage)))
|
onView(withId(resId)).check(matches(checkErrorMessage(errorMessage)))
|
||||||
|
|
||||||
fun checkImageViewHasImage(resId: Int): ViewInteraction =
|
fun checkImageViewDoesNotHaveDefaultImage(@IdRes resId: Int): ViewInteraction =
|
||||||
onView(withId(resId)).check(matches(checkImage()))
|
onView(withId(resId)).check(matches(checkImage()))
|
||||||
|
|
||||||
fun swipeDown(resId: Int): ViewInteraction =
|
fun swipeDown(@IdRes resId: Int): ViewInteraction =
|
||||||
onView(withId(resId)).perform(swipeDown())
|
onView(withId(resId)).perform(swipeDown())
|
||||||
|
|
||||||
fun getStringFromResource(@StringRes resId: Int): String =
|
fun getStringFromResource(@StringRes resId: Int): String =
|
||||||
@@ -150,12 +156,12 @@ open class BaseTestRobot {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectSingleImageFromGallery(filePath: FormRobot.FilePath, openSelector: () -> Unit) {
|
fun selectSingleImageFromGallery(filePath: String, openSelector: () -> Unit) {
|
||||||
Intents.init()
|
Intents.init()
|
||||||
// Build the result to return when the activity is launched.
|
// Build the result to return when the activity is launched.
|
||||||
val resultData = Intent()
|
val resultData = Intent()
|
||||||
resultData.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
resultData.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
resultData.data = Uri.fromFile(File(FormRobot.FilePath.getFilePath(filePath)))
|
resultData.data = Uri.fromFile(File("/sdcard/Camera/", filePath))
|
||||||
val result = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)
|
val result = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)
|
||||||
// Set up result stubbing when an intent sent to image picker is seen.
|
// Set up result stubbing when an intent sent to image picker is seen.
|
||||||
intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(result)
|
intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(result)
|
||||||
@@ -164,7 +170,7 @@ open class BaseTestRobot {
|
|||||||
Intents.release()
|
Intents.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectMultipleImageFromGallery(filePaths: Array<String>, openSelector: () -> Unit) {
|
fun selectMultipleImageFromGallery(filePaths: List<String>, openSelector: () -> Unit) {
|
||||||
Intents.init()
|
Intents.init()
|
||||||
// Build the result to return when the activity is launched.
|
// Build the result to return when the activity is launched.
|
||||||
val resultData = Intent()
|
val resultData = Intent()
|
||||||
@@ -172,7 +178,7 @@ open class BaseTestRobot {
|
|||||||
resultData.clipData = clipData
|
resultData.clipData = clipData
|
||||||
val result = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)
|
val result = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)
|
||||||
// Set up result stubbing when an intent sent to "contacts" is seen.
|
// Set up result stubbing when an intent sent to "contacts" is seen.
|
||||||
intending(IntentMatchers.toPackage("android.intent.action.PICK")).respondWith(result)
|
intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(result)
|
||||||
|
|
||||||
openSelector()
|
openSelector()
|
||||||
Intents.release()
|
Intents.release()
|
||||||
|
|||||||
@@ -3,38 +3,41 @@ package h_mal.appttude.com.driver
|
|||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.R
|
import android.R
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.espresso.*
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.IdlingRegistry
|
||||||
|
import androidx.test.espresso.IdlingResource
|
||||||
|
import androidx.test.espresso.UiController
|
||||||
|
import androidx.test.espresso.ViewAction
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||||
import androidx.test.rule.GrantPermissionRule
|
import androidx.test.rule.GrantPermissionRule
|
||||||
|
import com.google.gson.Gson
|
||||||
import h_mal.appttude.com.driver.base.BaseActivity
|
import h_mal.appttude.com.driver.base.BaseActivity
|
||||||
import h_mal.appttude.com.driver.helpers.BaseViewAction
|
import h_mal.appttude.com.driver.helpers.BaseViewAction
|
||||||
import h_mal.appttude.com.driver.helpers.SnapshotRule
|
import h_mal.appttude.com.driver.helpers.SnapshotRule
|
||||||
import org.hamcrest.CoreMatchers
|
import org.hamcrest.CoreMatchers
|
||||||
import org.hamcrest.Description
|
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.hamcrest.TypeSafeMatcher
|
|
||||||
import org.hamcrest.core.AllOf
|
import org.hamcrest.core.AllOf
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.ClassRule
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import tools.fastlane.screengrab.Screengrab
|
import tools.fastlane.screengrab.Screengrab
|
||||||
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy
|
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy
|
||||||
import tools.fastlane.screengrab.locale.LocaleTestRule
|
import tools.fastlane.screengrab.locale.LocaleTestRule
|
||||||
|
import java.io.BufferedReader
|
||||||
|
|
||||||
|
|
||||||
open class BaseUiTest<T : BaseActivity<*, *>>(
|
open class BaseUiTest<T : BaseActivity<*, *>>(
|
||||||
private val activity: Class<T>
|
private val activity: Class<T>
|
||||||
) {
|
) {
|
||||||
|
val gson by lazy { Gson() }
|
||||||
|
|
||||||
private lateinit var mActivityScenarioRule: ActivityScenario<T>
|
private lateinit var mActivityScenarioRule: ActivityScenario<T>
|
||||||
private var mIdlingResource: IdlingResource? = null
|
private var mIdlingResource: IdlingResource? = null
|
||||||
@@ -42,7 +45,7 @@ open class BaseUiTest<T : BaseActivity<*, *>>(
|
|||||||
private lateinit var currentActivity: Activity
|
private lateinit var currentActivity: Activity
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
var permissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
var permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
var snapshotRule: SnapshotRule = SnapshotRule()
|
var snapshotRule: SnapshotRule = SnapshotRule()
|
||||||
@@ -59,8 +62,8 @@ open class BaseUiTest<T : BaseActivity<*, *>>(
|
|||||||
mActivityScenarioRule.onActivity {
|
mActivityScenarioRule.onActivity {
|
||||||
mIdlingResource = it.getIdlingResource()!!
|
mIdlingResource = it.getIdlingResource()!!
|
||||||
IdlingRegistry.getInstance().register(mIdlingResource)
|
IdlingRegistry.getInstance().register(mIdlingResource)
|
||||||
afterLaunch(it)
|
|
||||||
}
|
}
|
||||||
|
afterLaunch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -87,36 +90,7 @@ open class BaseUiTest<T : BaseActivity<*, *>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
open fun beforeLaunch() {}
|
open fun beforeLaunch() {}
|
||||||
open fun afterLaunch(context: Context) {}
|
open fun afterLaunch() {}
|
||||||
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
fun checkToastMessage(message: String) {
|
|
||||||
onView(withText(message)).inRoot(object : TypeSafeMatcher<Root>() {
|
|
||||||
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) {
|
fun checkSnackBarDisplayedByMessage(message: String) {
|
||||||
onView(
|
onView(
|
||||||
@@ -138,4 +112,22 @@ open class BaseUiTest<T : BaseActivity<*, *>>(
|
|||||||
})
|
})
|
||||||
return currentActivity
|
return currentActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T: Any> readDataFromAsset(fileName: String, clazz: Class<T>): T {
|
||||||
|
val iStream =
|
||||||
|
getInstrumentation().context.assets.open("$fileName.json")
|
||||||
|
val data = iStream.bufferedReader().use(BufferedReader::readText)
|
||||||
|
return gson.fromJson(data, clazz)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified M: Any> readDataFromAsset(fileName: String): M {
|
||||||
|
val iStream =
|
||||||
|
getInstrumentation().context.assets.open("$fileName.json")
|
||||||
|
val data = iStream.bufferedReader().use(BufferedReader::readText)
|
||||||
|
return fromJson<M>(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified M> fromJson(json: String)
|
||||||
|
= gson.fromJson<M>(json, M::class.java)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,41 +5,32 @@ import android.view.View
|
|||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import org.hamcrest.Description
|
import h_mal.appttude.com.driver.helpers.BaseMatcher
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.hamcrest.TypeSafeMatcher
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Matcher for testing error of TextInputLayout
|
* Matcher for testing error of TextInputLayout
|
||||||
*/
|
*/
|
||||||
fun checkErrorMessage(expectedErrorText: String): Matcher<View?> {
|
fun checkErrorMessage(expectedErrorText: String): Matcher<View> {
|
||||||
return object : TypeSafeMatcher<View?>() {
|
return object : BaseMatcher<View>() {
|
||||||
override fun matchesSafely(view: View?): Boolean {
|
override fun match(item: View): Boolean {
|
||||||
if (view is EditText) {
|
if (item is EditText) {
|
||||||
return view.error.toString() == expectedErrorText
|
return item.error.toString() == expectedErrorText
|
||||||
}
|
}
|
||||||
|
|
||||||
if (view !is TextInputLayout) return false
|
if (item !is TextInputLayout) return false
|
||||||
|
|
||||||
val error = view.error ?: return false
|
val error = item.error ?: return false
|
||||||
return expectedErrorText == error.toString()
|
return expectedErrorText == error.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun describeTo(d: Description?) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkImage(): Matcher<View?> {
|
@Suppress("UNCHECKED_CAST")
|
||||||
return object : TypeSafeMatcher<View?>() {
|
fun checkImage(): Matcher<View> {
|
||||||
override fun matchesSafely(view: View?): Boolean {
|
return object: BaseMatcher<ImageView>() {
|
||||||
if (view is ImageView) {
|
override fun match(item: ImageView): Boolean = hasImage(item)
|
||||||
return hasImage(view)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun describeTo(d: Description?) {}
|
|
||||||
|
|
||||||
private fun hasImage(view: ImageView): Boolean {
|
private fun hasImage(view: ImageView): Boolean {
|
||||||
val drawable = view.drawable
|
val drawable = view.drawable
|
||||||
@@ -49,6 +40,6 @@ fun checkImage(): Matcher<View?> {
|
|||||||
}
|
}
|
||||||
return hasImage
|
return hasImage
|
||||||
}
|
}
|
||||||
}
|
} as Matcher<View>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,27 @@
|
|||||||
package h_mal.appttude.com.driver
|
package h_mal.appttude.com.driver
|
||||||
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.ViewInteraction
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.action.ViewActions.scrollTo
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import h_mal.appttude.com.driver.helpers.getImagePath
|
import h_mal.appttude.com.driver.helpers.EspressoHelper.trying
|
||||||
|
import h_mal.appttude.com.driver.model.Model
|
||||||
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
|
||||||
open class FormRobot : BaseTestRobot() {
|
open class FormRobot<T : Model> : BaseTestRobot() {
|
||||||
fun submit() = clickButton(R.id.submit)
|
|
||||||
|
fun submit() = onView(
|
||||||
|
allOf(
|
||||||
|
withId(R.id.submit),
|
||||||
|
isAssignableFrom(com.google.android.material.button.MaterialButton::class.java)
|
||||||
|
)
|
||||||
|
).perform(click())
|
||||||
|
|
||||||
fun setDate(datePickerLaunchViewId: Int, year: Int, monthOfYear: Int, dayOfMonth: Int) {
|
fun setDate(datePickerLaunchViewId: Int, year: Int, monthOfYear: Int, dayOfMonth: Int) {
|
||||||
onView(withId(datePickerLaunchViewId)).perform(click())
|
onView(withId(datePickerLaunchViewId)).perform(click())
|
||||||
selectDateInPicker(year, monthOfYear, dayOfMonth)
|
selectDateInPicker(year, monthOfYear, dayOfMonth)
|
||||||
@@ -15,32 +29,50 @@ open class FormRobot : BaseTestRobot() {
|
|||||||
onView(withId(android.R.id.button1)).perform(click())
|
onView(withId(android.R.id.button1)).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectSingleImage(imagePickerLauncherViewId: Int, filePath: FilePath) {
|
fun setDate(datePickerLaunchViewId: Int, dateString: String) {
|
||||||
selectSingleImageFromGallery(filePath) {
|
onView(withId(datePickerLaunchViewId)).perform(click())
|
||||||
onView(withId(imagePickerLauncherViewId)).perform(click())
|
val date = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("dd/MM/yyyy"))
|
||||||
}
|
selectDateInPicker(date.year, date.monthValue, date.dayOfMonth)
|
||||||
// click ok in date picker
|
// click ok in date picker
|
||||||
|
onView(withId(android.R.id.button1)).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectMultipleImage(imagePickerLauncherViewId: Int, filePaths: Array<String>) {
|
fun scrollAndSetDate(datePickerLaunchViewId: Int, dateString: String) {
|
||||||
selectMultipleImageFromGallery(filePaths) {
|
onView(withId(datePickerLaunchViewId)).perform(scrollTo(), click())
|
||||||
|
val date = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("dd/MM/yyyy"))
|
||||||
|
selectDateInPicker(date.year, date.monthValue, date.dayOfMonth)
|
||||||
|
// click ok in date picker
|
||||||
|
onView(withId(android.R.id.button1)).perform(click())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectSingleImage(imagePickerLauncherViewId: Int, fileName: String) {
|
||||||
|
selectSingleImageFromGallery(fileName) {
|
||||||
onView(withId(imagePickerLauncherViewId)).perform(click())
|
onView(withId(imagePickerLauncherViewId)).perform(click())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class FilePath(val path: String) {
|
fun selectSingleImage(imagePickerViewInteraction: ViewInteraction, fileName: String) {
|
||||||
PROFILE_PIC("driver_profile_pic.jpg"),
|
selectSingleImageFromGallery(fileName) {
|
||||||
INSURANCE("driver_insurance.jpg"),
|
imagePickerViewInteraction.perform(click())
|
||||||
PRIVATE_HIRE("driver_license_private_hire.jpg"),
|
}
|
||||||
PRIVATE_HIRE_CAR("driver_license_private_hire_car.jpg"),
|
}
|
||||||
LOGBOOK("driver_logbook.jpg"),
|
|
||||||
MOT("driver_mot.jpg"),
|
|
||||||
LICENSE("driver_license_driver.jpg");
|
|
||||||
|
|
||||||
companion object {
|
fun selectMultipleImage(imagePickerLauncherViewId: Int, filePaths: List<String>) {
|
||||||
fun getFilePath(filePath: FilePath): String {
|
selectMultipleImageFromGallery(filePaths.map { "/sdcard/Camera/$it" }) {
|
||||||
return getImagePath(filePath.path)
|
onView(withId(imagePickerLauncherViewId)).perform(click())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun submitForm(data: T) {
|
||||||
|
(trying {
|
||||||
|
onView(withId(R.id.submit)).perform(scrollTo())
|
||||||
|
} ?: onView(withId(R.id.submit))).perform(click())
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun validateSubmission(data: T) {}
|
||||||
|
|
||||||
|
open fun submitAndValidate(data: T) {
|
||||||
|
submitForm(data)
|
||||||
|
validateSubmission(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,22 @@
|
|||||||
package h_mal.appttude.com.driver.helpers
|
package h_mal.appttude.com.driver.helpers
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import org.hamcrest.BaseMatcher
|
|
||||||
import org.hamcrest.Description
|
import org.hamcrest.Description
|
||||||
|
import org.hamcrest.TypeSafeMatcher
|
||||||
|
|
||||||
class BaseMatcher: BaseMatcher<View>() {
|
open class BaseMatcher<T: Any>: TypeSafeMatcher<T>() {
|
||||||
override fun describeTo(description: Description?) {
|
override fun describeTo(description: Description?) { }
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun matches(actual: Any?): Boolean {
|
override fun describeMismatchSafely(item: T, mismatchDescription: Description?) {
|
||||||
TODO("Not yet implemented")
|
describe(item, mismatchDescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun matchesSafely(item: T): Boolean = match(item)
|
||||||
|
|
||||||
|
open fun match(item: T): Boolean { return false }
|
||||||
|
|
||||||
|
open fun describe(item: T, mismatchDescription: Description?) {
|
||||||
|
super.describeMismatchSafely(item, mismatchDescription)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,14 +1 @@
|
|||||||
package h_mal.appttude.com.driver.helpers
|
package h_mal.appttude.com.driver.helpers
|
||||||
|
|
||||||
import android.os.Environment
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
|
||||||
* File paths for images on device
|
|
||||||
*/
|
|
||||||
fun getImagePath(imageConst: String): String {
|
|
||||||
return File(
|
|
||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
|
|
||||||
"/Camera/images/$imageConst"
|
|
||||||
).absolutePath
|
|
||||||
}
|
|
||||||
@@ -18,10 +18,9 @@ object DataHelper {
|
|||||||
|
|
||||||
fun createClipData(filePath: String) = createClipData(createClipItem(filePath))
|
fun createClipData(filePath: String) = createClipData(createClipItem(filePath))
|
||||||
|
|
||||||
fun createClipData(filePaths: Array<String>): ClipData {
|
fun createClipData(filePaths: List<String>): ClipData {
|
||||||
val clipData = createClipData(filePaths[0])
|
val clipData = createClipData(filePaths[0])
|
||||||
val remainingFiles = filePaths.copyOfRange(1, filePaths.size - 1)
|
filePaths.filterIndexed { i, _ -> i > 0 }.let { clipData.addFilePaths(it.toTypedArray()) }
|
||||||
clipData.addFilePaths(remainingFiles)
|
|
||||||
return clipData
|
return clipData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,4 +120,50 @@ object EspressoHelper {
|
|||||||
|
|
||||||
throw Exception("Error finding a view matching $viewMatcher")
|
throw Exception("Error finding a view matching $viewMatcher")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* try and perform a view interaction for
|
||||||
|
* @param waitMillis at intervals of
|
||||||
|
* @param waitMillisPerTry,
|
||||||
|
* upon failure to locate an element, it will return null
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun ViewInteraction.tryPerform(
|
||||||
|
vararg viewActions: ViewAction,
|
||||||
|
waitMillis: Int = 1000,
|
||||||
|
waitMillisPerTry: Long = 200,
|
||||||
|
): ViewInteraction? {
|
||||||
|
|
||||||
|
// Derive the max tries
|
||||||
|
val maxTries = waitMillis / waitMillisPerTry.toInt()
|
||||||
|
|
||||||
|
var tries = 0
|
||||||
|
|
||||||
|
for (i in 0..maxTries)
|
||||||
|
try {
|
||||||
|
// Track the amount of times we've tried
|
||||||
|
tries++
|
||||||
|
|
||||||
|
// Search the root for the view
|
||||||
|
return perform(*viewActions)
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
|
||||||
|
if (tries == maxTries) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
sleep(waitMillisPerTry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T: Any> trying(action: () -> T): T? {
|
||||||
|
return try {
|
||||||
|
val result = action.invoke()
|
||||||
|
result
|
||||||
|
}catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@ package h_mal.appttude.com.driver.helpers
|
|||||||
import org.junit.rules.TestWatcher
|
import org.junit.rules.TestWatcher
|
||||||
import org.junit.runner.Description
|
import org.junit.runner.Description
|
||||||
import tools.fastlane.screengrab.Screengrab
|
import tools.fastlane.screengrab.Screengrab
|
||||||
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Junit rule that takes a screenshot when a test fails.
|
* Junit rule that takes a screenshot when a test fails.
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
package h_mal.appttude.com.driver.utils
|
package h_mal.appttude.com.driver.untiTests
|
||||||
|
|
||||||
|
import androidx.startup.AppInitializer
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import h_mal.appttude.com.driver.utils.DateUtils
|
||||||
import h_mal.appttude.com.driver.utils.DateUtils.convertDateStringDatePattern
|
import h_mal.appttude.com.driver.utils.DateUtils.convertDateStringDatePattern
|
||||||
import org.junit.Assert.*
|
import net.danlew.android.joda.JodaTimeInitializer
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class DateUtilsTest {
|
class DateUtilsTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
AppInitializer.getInstance(InstrumentationRegistry.getInstrumentation().context.applicationContext)
|
||||||
|
.initializeComponent(JodaTimeInitializer::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_getDateTimeStamp() {
|
fun test_getDateTimeStamp() {
|
||||||
val regex1 = "[0-9]{8}_[0-9]{6}".toRegex()
|
val regex1 = "[0-9]{8}_[0-9]{6}".toRegex()
|
||||||
@@ -31,6 +44,8 @@ class DateUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_parseCalenderIntoDateString() {
|
fun test_getDateString() {
|
||||||
|
val date = DateUtils.getDateString(2019, 8, 1)
|
||||||
|
assertEquals(date, "01/08/2019")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package h_mal.appttude.com.driver.robots
|
package h_mal.appttude.com.driver.robots
|
||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.BaseTestRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
|
||||||
fun delete(func: DeleteRobot.() -> Unit) = DeleteRobot().apply { func() }
|
fun delete(func: DeleteRobot.() -> Unit) = DeleteRobot().apply { func() }
|
||||||
class DeleteRobot : FormRobot() {
|
class DeleteRobot : BaseTestRobot() {
|
||||||
|
|
||||||
|
fun submit() = clickButton(R.id.submit)
|
||||||
fun enterEmail(email: String) = fillEditText(R.id.email_update, email)
|
fun enterEmail(email: String) = fillEditText(R.id.email_update, email)
|
||||||
fun enterPassword(password: String) = fillEditText(R.id.password_top, password)
|
fun enterPassword(password: String) = fillEditText(R.id.password_top, password)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package h_mal.appttude.com.driver.robots
|
package h_mal.appttude.com.driver.robots
|
||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.BaseTestRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
|
||||||
fun updateEmail(func: UpdateEmailRobot.() -> Unit) = UpdateEmailRobot().apply { func() }
|
fun updateEmail(func: UpdateEmailRobot.() -> Unit) = UpdateEmailRobot().apply { func() }
|
||||||
class UpdateEmailRobot : FormRobot() {
|
class UpdateEmailRobot : BaseTestRobot() {
|
||||||
|
|
||||||
|
fun submit() = clickButton(R.id.submit)
|
||||||
fun enterEmail(email: String) = fillEditText(R.id.email_update, email)
|
fun enterEmail(email: String) = fillEditText(R.id.email_update, email)
|
||||||
fun enterPassword(password: String) = fillEditText(R.id.password_top, password)
|
fun enterPassword(password: String) = fillEditText(R.id.password_top, password)
|
||||||
fun enterNewEmail(email: String) = fillEditText(R.id.new_email, email)
|
fun enterNewEmail(email: String) = fillEditText(R.id.new_email, email)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package h_mal.appttude.com.driver.robots
|
package h_mal.appttude.com.driver.robots
|
||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.BaseTestRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
|
||||||
fun updatePassword(func: UpdatePasswordRobot.() -> Unit) = UpdatePasswordRobot().apply { func() }
|
fun updatePassword(func: UpdatePasswordRobot.() -> Unit) = UpdatePasswordRobot().apply { func() }
|
||||||
class UpdatePasswordRobot : FormRobot() {
|
class UpdatePasswordRobot : BaseTestRobot() {
|
||||||
|
|
||||||
|
fun submit() = clickButton(R.id.submit)
|
||||||
fun enterEmail(email: String) = fillEditText(R.id.email_update, email)
|
fun enterEmail(email: String) = fillEditText(R.id.email_update, email)
|
||||||
fun enterPassword(password: String) = fillEditText(R.id.password_top, password)
|
fun enterPassword(password: String) = fillEditText(R.id.password_top, password)
|
||||||
fun enterNewPassword(email: String) = fillEditText(R.id.password_bottom, email)
|
fun enterNewPassword(email: String) = fillEditText(R.id.password_bottom, email)
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
package h_mal.appttude.com.driver.robots
|
package h_mal.appttude.com.driver.robots
|
||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.BaseTestRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
|
||||||
fun updateProfile(func: UpdateProfileRobot.() -> Unit) = UpdateProfileRobot().apply { func() }
|
fun updateProfile(func: UpdateProfileRobot.() -> Unit) = UpdateProfileRobot().apply { func() }
|
||||||
class UpdateProfileRobot : FormRobot() {
|
class UpdateProfileRobot : BaseTestRobot() {
|
||||||
|
|
||||||
|
fun submit() = clickButton(R.id.submit)
|
||||||
fun enterName(name: String) = fillEditText(R.id.update_name, name)
|
fun enterName(name: String) = fillEditText(R.id.update_name, name)
|
||||||
fun selectImage() = selectSingleImage(R.id.profile_img, FilePath.PROFILE_PIC)
|
|
||||||
|
|
||||||
fun submitForm(name: String) {
|
fun submitForm(name: String) {
|
||||||
// selectImage()
|
selectSingleImageFromGallery("driver_profile_pic") {
|
||||||
|
clickButton(R.id.profile_img)
|
||||||
|
}
|
||||||
enterName(name)
|
enterName(name)
|
||||||
submit()
|
submit()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,26 +2,23 @@ package h_mal.appttude.com.driver.robots.driver
|
|||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.FormRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
import h_mal.appttude.com.driver.model.DriversLicense
|
||||||
|
|
||||||
fun driversLicense(func: DriversLicenseRobot.() -> Unit) = DriversLicenseRobot().apply { func() }
|
fun driversLicense(func: DriversLicenseRobot.() -> Unit) = DriversLicenseRobot().apply { func() }
|
||||||
class DriversLicenseRobot : FormRobot() {
|
class DriversLicenseRobot : FormRobot<DriversLicense>() {
|
||||||
|
|
||||||
fun enterLicenseNumber(text: String) = fillEditText(R.id.lic_no, text)
|
fun enterLicenseNumber(text: String) = fillEditText(R.id.lic_no, text)
|
||||||
fun enterLicenseExpiry(year: Int, monthOfYear: Int, dayOfMonth: Int) =
|
fun enterLicenseExpiry(data: String) = setDate(R.id.lic_expiry, data)
|
||||||
setDate(R.id.lic_expiry, year, monthOfYear, dayOfMonth)
|
|
||||||
|
|
||||||
fun selectImage() = selectSingleImage(R.id.search_image, FilePath.LICENSE)
|
override fun submitForm(data: DriversLicense) {
|
||||||
|
selectSingleImage(R.id.search_image, data.licenseImageString!!)
|
||||||
fun submitForm(licenseNumber: String, year: Int, monthOfYear: Int, dayOfMonth: Int) {
|
enterLicenseExpiry(data.licenseExpiry!!)
|
||||||
selectImage()
|
enterLicenseNumber(data.licenseNumber!!)
|
||||||
enterLicenseNumber(licenseNumber)
|
super.submitForm(data)
|
||||||
enterLicenseExpiry(year, monthOfYear, dayOfMonth)
|
|
||||||
submit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun validate() {
|
override fun validateSubmission(data: DriversLicense) {
|
||||||
checkImageViewHasImage(R.id.driversli_img)
|
checkImageViewDoesNotHaveDefaultImage(R.id.driversli_img)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,39 @@
|
|||||||
package h_mal.appttude.com.driver.robots.driver
|
package h_mal.appttude.com.driver.robots.driver
|
||||||
|
|
||||||
|
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||||
|
import androidx.test.espresso.action.ViewActions.scrollTo
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.FormRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
import h_mal.appttude.com.driver.model.DriverProfile
|
||||||
|
|
||||||
fun driversProfile(func: DriversProfileRobot.() -> Unit) = DriversProfileRobot().apply { func() }
|
fun driversProfile(func: DriversProfileRobot.() -> Unit) = DriversProfileRobot().apply { func() }
|
||||||
class DriversProfileRobot : FormRobot() {
|
class DriversProfileRobot : FormRobot<DriverProfile>() {
|
||||||
|
|
||||||
fun enterName(name: String) = fillEditText(R.id.names_input, name)
|
fun enterName(name: String) = fillEditText(R.id.names_input, name)
|
||||||
fun enterAddress(address: String) = fillEditText(R.id.address_input, address)
|
fun enterAddress(address: String) = fillEditText(R.id.address_input, address)
|
||||||
fun enterPostcode(postcode: String) = fillEditText(R.id.postcode_input, postcode)
|
fun enterPostcode(postcode: String) = fillEditText(R.id.postcode_input, postcode)
|
||||||
fun enterDateOfBirth(dob: String) = fillEditText(R.id.dob_input, dob)
|
fun enterDateOfBirth(date: String) = setDate(R.id.dob_input, date)
|
||||||
|
|
||||||
fun enterNINumber(niNumber: String) = fillEditText(R.id.ni_number, niNumber)
|
fun enterNINumber(niNumber: String) = fillEditText(R.id.ni_number, niNumber)
|
||||||
fun enterDateFirstAvailable(year: Int, monthOfYear: Int, dayOfMonth: Int) =
|
fun enterDateFirstAvailable(date: String) {
|
||||||
setDate(R.id.date_first, year, monthOfYear, dayOfMonth)
|
closeSoftKeyboard()
|
||||||
|
matchView(R.id.date_first).perform(scrollTo())
|
||||||
|
setDate(R.id.date_first, date)
|
||||||
|
}
|
||||||
|
|
||||||
fun selectImage() = selectSingleImage(R.id.add_photo, FilePath.PROFILE_PIC)
|
override fun validateSubmission(data: DriverProfile) {
|
||||||
|
checkImageViewDoesNotHaveDefaultImage(R.id.driver_pic)
|
||||||
|
matchText(R.id.names_input, data.forenames!!)
|
||||||
|
}
|
||||||
|
|
||||||
fun submitForm(
|
override fun submitForm(data: DriverProfile) = data.run {
|
||||||
name: String,
|
selectSingleImage(R.id.add_photo, driverPic!!)
|
||||||
address: String,
|
enterName(forenames!!)
|
||||||
postcode: String,
|
enterAddress(address!!)
|
||||||
dob: String,
|
enterPostcode(postcode!!)
|
||||||
niNumber: String,
|
enterDateOfBirth(dob!!)
|
||||||
year: Int,
|
enterNINumber(ni!!)
|
||||||
monthOfYear: Int,
|
enterDateFirstAvailable(dateFirst!!)
|
||||||
dayOfMonth: Int
|
super.submitForm(data)
|
||||||
) {
|
|
||||||
selectImage()
|
|
||||||
enterName(name)
|
|
||||||
enterAddress(address)
|
|
||||||
enterPostcode(postcode)
|
|
||||||
enterDateOfBirth(dob)
|
|
||||||
enterNINumber(niNumber)
|
|
||||||
enterDateFirstAvailable(year, monthOfYear, dayOfMonth)
|
|
||||||
submit()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,22 +2,29 @@ package h_mal.appttude.com.driver.robots.driver
|
|||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.FormRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
import h_mal.appttude.com.driver.model.PrivateHireLicense
|
||||||
|
|
||||||
fun privateHireLicenseRobot(func: PrivateHireLicenseRobot.() -> Unit) =
|
fun privateHireLicenseRobot(func: PrivateHireLicenseRobot.() -> Unit) =
|
||||||
PrivateHireLicenseRobot().apply { func() }
|
PrivateHireLicenseRobot().apply { func() }
|
||||||
|
|
||||||
class PrivateHireLicenseRobot : FormRobot() {
|
class PrivateHireLicenseRobot : FormRobot<PrivateHireLicense>() {
|
||||||
|
|
||||||
fun enterLicenseNumber(text: String) = fillEditText(R.id.ph_no, text)
|
fun enterLicenseNumber(text: String) = fillEditText(R.id.ph_no, text)
|
||||||
fun enterLicenseExpiry(year: Int, monthOfYear: Int, dayOfMonth: Int) =
|
fun enterLicenseExpiry(date: String) = setDate(R.id.ph_expiry, date)
|
||||||
setDate(R.id.ph_expiry, year, monthOfYear, dayOfMonth)
|
|
||||||
|
|
||||||
fun selectImage() = selectSingleImage(R.id.uploadphlic, FilePath.PRIVATE_HIRE)
|
fun selectImage(fileName: String) = selectSingleImage(R.id.uploadphlic, fileName)
|
||||||
|
|
||||||
fun submitForm(licenseNumber: String, year: Int, monthOfYear: Int, dayOfMonth: Int) {
|
override fun submitForm(data: PrivateHireLicense) {
|
||||||
selectImage()
|
selectImage(data.phImageString!!)
|
||||||
enterLicenseNumber(licenseNumber)
|
enterLicenseNumber(data.phNumber!!)
|
||||||
enterLicenseExpiry(year, monthOfYear, dayOfMonth)
|
enterLicenseExpiry(data.phExpiry!!)
|
||||||
submit()
|
super.submitForm(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun validate(data: PrivateHireLicense) {
|
||||||
|
checkImageViewDoesNotHaveDefaultImage(R.id.imageView2)
|
||||||
|
matchText(R.id.ph_expiry, data.phExpiry!!)
|
||||||
|
matchText(R.id.ph_no, data.phNumber!!)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,24 @@
|
|||||||
package h_mal.appttude.com.driver.robots.vehicle
|
package h_mal.appttude.com.driver.robots.vehicle
|
||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.FormRobot
|
||||||
import h_mal.appttude.com.driver.FormRobot.FilePath.Companion.getFilePath
|
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
import h_mal.appttude.com.driver.model.Insurance
|
||||||
|
|
||||||
fun insurance(func: InsuranceRobot.() -> Unit) = InsuranceRobot().apply { func() }
|
fun insurance(func: InsuranceRobot.() -> Unit) = InsuranceRobot().apply { func() }
|
||||||
class InsuranceRobot : FormRobot() {
|
class InsuranceRobot : FormRobot<Insurance>() {
|
||||||
|
|
||||||
fun enterInsurance(text: String) = fillEditText(R.id.insurer, text)
|
fun enterInsurance(text: String) = fillEditText(R.id.insurer, text)
|
||||||
fun enterInsuranceExpiry(year: Int, monthOfYear: Int, dayOfMonth: Int) =
|
fun enterInsuranceExpiry(date: String) = setDate(R.id.insurance_exp, date)
|
||||||
setDate(R.id.insurance_exp, year, monthOfYear, dayOfMonth)
|
|
||||||
|
|
||||||
fun selectImages() =
|
override fun submitForm(data: Insurance) {
|
||||||
selectMultipleImage(R.id.uploadInsurance, arrayOf(getFilePath(FilePath.INSURANCE)))
|
selectMultipleImage(R.id.uploadInsurance, data.photoStrings!!.map { it!! })
|
||||||
|
enterInsurance(data.insurerName!!)
|
||||||
|
enterInsuranceExpiry(data.expiryDate!!)
|
||||||
|
super.submitForm(data)
|
||||||
|
}
|
||||||
|
|
||||||
fun submitForm(insurer: String, year: Int, monthOfYear: Int, dayOfMonth: Int) {
|
override fun validateSubmission(data: Insurance) {
|
||||||
selectImages()
|
matchText(R.id.insurer, data.insurerName!!)
|
||||||
enterInsurance(insurer)
|
matchText(R.id.insurance_exp, data.expiryDate!!)
|
||||||
enterInsuranceExpiry(year, monthOfYear, dayOfMonth)
|
|
||||||
submit()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,17 +2,21 @@ package h_mal.appttude.com.driver.robots.vehicle
|
|||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.FormRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
import h_mal.appttude.com.driver.model.Logbook
|
||||||
|
|
||||||
fun logbook(func: LogbookRobot.() -> Unit) = LogbookRobot().apply { func() }
|
fun logbook(func: LogbookRobot.() -> Unit) = LogbookRobot().apply { func() }
|
||||||
class LogbookRobot : FormRobot() {
|
class LogbookRobot : FormRobot<Logbook>() {
|
||||||
|
|
||||||
fun selectImages() = selectSingleImage(R.id.uploadmot, FilePath.MOT)
|
fun enterV5c(v5c: String) = fillEditText(R.id.v5c_no, v5c)
|
||||||
fun enterExpiryDate(year: Int, monthOfYear: Int, dayOfMonth: Int) =
|
|
||||||
setDate(R.id.mot_expiry, year, monthOfYear, dayOfMonth)
|
|
||||||
|
|
||||||
fun submitForm(year: Int, monthOfYear: Int, dayOfMonth: Int) {
|
override fun submitForm(data: Logbook) {
|
||||||
selectImages()
|
selectSingleImage(R.id.upload_lb, data.photoString!!)
|
||||||
enterExpiryDate(year, monthOfYear, dayOfMonth)
|
enterV5c(data.v5cnumber!!)
|
||||||
submit()
|
super.submitForm(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun validateSubmission(data: Logbook) {
|
||||||
|
checkImageViewDoesNotHaveDefaultImage(R.id.log_book_img)
|
||||||
|
matchText(R.id.v5c_no, data.v5cnumber!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,16 +2,21 @@ package h_mal.appttude.com.driver.robots.vehicle
|
|||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.FormRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
import h_mal.appttude.com.driver.model.Mot
|
||||||
|
|
||||||
fun mot(func: MOTRobot.() -> Unit) = MOTRobot().apply { func() }
|
fun mot(func: MOTRobot.() -> Unit) = MOTRobot().apply { func() }
|
||||||
class MOTRobot : FormRobot() {
|
class MOTRobot : FormRobot<Mot>() {
|
||||||
|
|
||||||
fun enterV5cNumber(v5c: String) = fillEditText(R.id.mot_expiry, v5c)
|
fun enterMotExpiry(expiry: String) = setDate(R.id.mot_expiry, expiry)
|
||||||
fun selectImages() = selectSingleImage(R.id.mot_expiry, FilePath.LOGBOOK)
|
|
||||||
|
|
||||||
fun submitForm(v5c: String) {
|
override fun submitForm(data: Mot) {
|
||||||
selectImages()
|
selectSingleImage(R.id.uploadmot, data.motImageString!!)
|
||||||
enterV5cNumber(v5c)
|
enterMotExpiry(data.motExpiry!!)
|
||||||
submit()
|
super.submitForm(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun validateSubmission(data: Mot) {
|
||||||
|
checkImageViewDoesNotHaveDefaultImage(R.id.mot_img)
|
||||||
|
matchText(R.id.mot_expiry, data.motExpiry!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,22 +2,28 @@ package h_mal.appttude.com.driver.robots.vehicle
|
|||||||
|
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.FormRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
|
import h_mal.appttude.com.driver.model.PrivateHireVehicle
|
||||||
|
|
||||||
fun privateHireVehicleLicense(func: PrivateHireVehicleLicenseRobot.() -> Unit) =
|
fun privateHireVehicleLicense(func: PrivateHireVehicleLicenseRobot.() -> Unit) =
|
||||||
PrivateHireVehicleLicenseRobot().apply { func() }
|
PrivateHireVehicleLicenseRobot().apply { func() }
|
||||||
|
class PrivateHireVehicleLicenseRobot : FormRobot<PrivateHireVehicle>() {
|
||||||
class PrivateHireVehicleLicenseRobot : FormRobot() {
|
|
||||||
|
|
||||||
fun enterLicenseNumber(text: String) = fillEditText(R.id.ph_no, text)
|
fun enterLicenseNumber(text: String) = fillEditText(R.id.ph_no, text)
|
||||||
fun enterLicenseExpiry(year: Int, monthOfYear: Int, dayOfMonth: Int) =
|
fun enterLicenseExpiry(date: String) = setDate(R.id.ph_expiry, date)
|
||||||
setDate(R.id.ph_expiry, year, monthOfYear, dayOfMonth)
|
|
||||||
|
|
||||||
fun selectImage() = selectSingleImage(R.id.uploadphlic, FilePath.PRIVATE_HIRE)
|
override fun submitForm(data: PrivateHireVehicle) {
|
||||||
|
selectSingleImage(
|
||||||
|
matchText(R.string.upload_private_hire_photo),
|
||||||
|
data.phCarImageString!!
|
||||||
|
)
|
||||||
|
enterLicenseNumber(data.phCarNumber!!)
|
||||||
|
enterLicenseExpiry(data.phCarExpiry!!)
|
||||||
|
super.submitForm(data)
|
||||||
|
}
|
||||||
|
|
||||||
fun submitForm(licenseNumber: String, year: Int, monthOfYear: Int, dayOfMonth: Int) {
|
override fun validateSubmission(data: PrivateHireVehicle) {
|
||||||
selectImage()
|
checkImageViewDoesNotHaveDefaultImage(R.id.imageView2)
|
||||||
enterLicenseNumber(licenseNumber)
|
matchText(R.id.ph_no, data.phCarNumber!!)
|
||||||
enterLicenseExpiry(year, monthOfYear, dayOfMonth)
|
matchText(R.id.ph_expiry, data.phCarExpiry!!)
|
||||||
submit()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,46 +1,51 @@
|
|||||||
package h_mal.appttude.com.driver.robots.vehicle
|
package h_mal.appttude.com.driver.robots.vehicle
|
||||||
|
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
|
||||||
import h_mal.appttude.com.driver.FormRobot
|
import h_mal.appttude.com.driver.FormRobot
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
import h_mal.appttude.com.driver.helpers.EspressoHelper.setChecked
|
import h_mal.appttude.com.driver.helpers.EspressoHelper.setChecked
|
||||||
|
import h_mal.appttude.com.driver.model.VehicleProfile
|
||||||
|
|
||||||
fun vehicleProfile(func: VehicleProfileRobot.() -> Unit) = VehicleProfileRobot().apply { func() }
|
fun vehicleProfile(func: VehicleProfileRobot.() -> Unit) = VehicleProfileRobot().apply { func() }
|
||||||
class VehicleProfileRobot : FormRobot() {
|
class VehicleProfileRobot : FormRobot<VehicleProfile>() {
|
||||||
|
|
||||||
fun enterRegistration(reg: String) = fillEditText(R.id.reg, reg)
|
|
||||||
fun enterMake(make: String) = fillEditText(R.id.make, make)
|
|
||||||
fun enterModel(model: String) = fillEditText(R.id.car_model, model)
|
|
||||||
fun enterColour(colour: String) = fillEditText(R.id.colour, colour)
|
|
||||||
fun enterAddress(address: String) = fillEditText(R.id.address, address)
|
|
||||||
fun enterPostcode(postCode: String) = fillEditText(R.id.postcode, postCode)
|
|
||||||
fun enterKeeperName(name: String) = fillEditText(R.id.keeper_name, name)
|
|
||||||
fun enterDateFirstAvailable(year: Int, monthOfYear: Int, dayOfMonth: Int) =
|
|
||||||
setDate(R.id.start_date, year, monthOfYear, dayOfMonth)
|
|
||||||
|
|
||||||
|
fun enterRegistration(reg: String) = scrollAndFillEditText(R.id.reg, reg)
|
||||||
|
fun enterMake(make: String) = scrollAndFillEditText(R.id.make, make)
|
||||||
|
fun enterModel(model: String) = scrollAndFillEditText(R.id.car_model, model)
|
||||||
|
fun enterColour(colour: String) = scrollAndFillEditText(R.id.colour, colour)
|
||||||
|
fun enterAddress(address: String) = scrollAndFillEditText(R.id.address, address)
|
||||||
|
fun enterPostcode(postCode: String) = scrollAndFillEditText(R.id.postcode, postCode)
|
||||||
|
fun enterKeeperName(name: String) = scrollAndFillEditText(R.id.keeper_name, name)
|
||||||
|
fun enterDateFirstAvailable(date: String) = scrollAndSetDate(R.id.start_date, date)
|
||||||
fun isSeized(seized: Boolean) = matchView(R.id.seized_checkbox).perform(setChecked(seized))
|
fun isSeized(seized: Boolean) = matchView(R.id.seized_checkbox).perform(setChecked(seized))
|
||||||
|
|
||||||
fun submitForm(
|
|
||||||
reg: String,
|
override fun submitForm(data: VehicleProfile) {
|
||||||
make: String,
|
enterRegistration(data.reg!!)
|
||||||
model: String,
|
enterMake(data.make!!)
|
||||||
colour: String,
|
enterModel(data.model!!)
|
||||||
address: String,
|
enterColour(data.colour!!)
|
||||||
postCode: String,
|
enterAddress(data.keeperAddress!!)
|
||||||
name: String,
|
enterPostcode(data.keeperPostCode!!)
|
||||||
year: Int,
|
enterKeeperName(data.keeperName!!)
|
||||||
monthOfYear: Int,
|
enterDateFirstAvailable(data.startDate!!)
|
||||||
dayOfMonth: Int,
|
isSeized(data.isSeized)
|
||||||
seized: Boolean = false
|
super.submitForm(data)
|
||||||
) {
|
}
|
||||||
enterRegistration(reg)
|
|
||||||
enterMake(make)
|
override fun validateSubmission(data: VehicleProfile) {
|
||||||
enterModel(model)
|
matchText(R.id.reg, data.reg!!)
|
||||||
enterColour(colour)
|
matchText(R.id.make, data.make!!)
|
||||||
enterAddress(address)
|
matchText(R.id.car_model, data.model!!)
|
||||||
enterPostcode(postCode)
|
matchText(R.id.colour, data.colour!!)
|
||||||
enterKeeperName(name)
|
matchText(R.id.address, data.keeperAddress!!)
|
||||||
enterDateFirstAvailable(year, monthOfYear, dayOfMonth)
|
matchText(R.id.postcode, data.keeperPostCode!!)
|
||||||
isSeized(seized)
|
matchText(R.id.keeper_name, data.keeperName!!)
|
||||||
submit()
|
matchText(R.id.start_date, data.startDate!!)
|
||||||
|
val checking = if (data.isSeized) isChecked() else isNotChecked()
|
||||||
|
matchView(R.id.seized_checkbox).check(matches(checking))
|
||||||
|
super.validateSubmission(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package h_mal.appttude.com.driver.tests.newUser
|
||||||
|
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
|
import h_mal.appttude.com.driver.FirebaseTest
|
||||||
|
import h_mal.appttude.com.driver.R
|
||||||
|
import h_mal.appttude.com.driver.helpers.EspressoHelper.trying
|
||||||
|
import h_mal.appttude.com.driver.helpers.EspressoHelper.waitForView
|
||||||
|
import h_mal.appttude.com.driver.model.DriverProfile
|
||||||
|
import h_mal.appttude.com.driver.model.DriversLicense
|
||||||
|
import h_mal.appttude.com.driver.model.Insurance
|
||||||
|
import h_mal.appttude.com.driver.model.Logbook
|
||||||
|
import h_mal.appttude.com.driver.model.Model
|
||||||
|
import h_mal.appttude.com.driver.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
|
||||||
|
import h_mal.appttude.com.driver.robots.home
|
||||||
|
import h_mal.appttude.com.driver.ui.MainActivity
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
open class DataSubmissionTest :
|
||||||
|
FirebaseTest<MainActivity>(MainActivity::class.java, registered = true, signedIn = true) {
|
||||||
|
|
||||||
|
|
||||||
|
override fun afterLaunch() {
|
||||||
|
super.afterLaunch()
|
||||||
|
home {
|
||||||
|
waitForView(withText(getResourceString(R.string.welcome_title)), waitMillis = 10000)
|
||||||
|
trying {
|
||||||
|
requestProfile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Model> getAssetData(): T {
|
||||||
|
val file = when (T::class) {
|
||||||
|
DriverProfile::class -> "driver_details"
|
||||||
|
DriversLicense::class -> "drivers_license"
|
||||||
|
Insurance::class -> "insurance_details"
|
||||||
|
Logbook::class -> "log_book"
|
||||||
|
Mot::class -> "mot_details"
|
||||||
|
PrivateHireLicense::class -> "private_hire_license"
|
||||||
|
PrivateHireVehicle::class -> "private_hire_vehicle"
|
||||||
|
VehicleProfile::class -> "vehicle_details"
|
||||||
|
else -> {
|
||||||
|
throw IOException("No file for ${T::class}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return readDataFromAsset(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package h_mal.appttude.com.driver.tests.newUser
|
||||||
|
|
||||||
|
import h_mal.appttude.com.driver.robots.home
|
||||||
|
|
||||||
|
open class DriverProfileTest : DataSubmissionTest() {
|
||||||
|
|
||||||
|
override fun afterLaunch() {
|
||||||
|
super.afterLaunch()
|
||||||
|
home {
|
||||||
|
openDriverProfile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package h_mal.appttude.com.driver.tests.newUser
|
|
||||||
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import androidx.test.rule.GrantPermissionRule
|
|
||||||
import h_mal.appttude.com.driver.FirebaseTest
|
|
||||||
import h_mal.appttude.com.driver.R
|
|
||||||
import h_mal.appttude.com.driver.robots.*
|
|
||||||
import h_mal.appttude.com.driver.robots.driver.driversLicense
|
|
||||||
import h_mal.appttude.com.driver.ui.MainActivity
|
|
||||||
import org.junit.*
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
|
|
||||||
@LargeTest
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class SubmitNewDataActivityTest :
|
|
||||||
FirebaseTest<MainActivity>(MainActivity::class.java, registered = true, signedIn = true) {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun verifyUserRegistration_validUsernameAndPassword_loggedIn() {
|
|
||||||
home {
|
|
||||||
waitFor(2500)
|
|
||||||
checkTitleExists(getResourceString(R.string.welcome_title))
|
|
||||||
requestProfile()
|
|
||||||
openDriverProfile()
|
|
||||||
}
|
|
||||||
driverScreen {
|
|
||||||
driverLicense()
|
|
||||||
}
|
|
||||||
driversLicense {
|
|
||||||
submitForm("SAMPLE8456310LTU", 2022, 10, 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package h_mal.appttude.com.driver.tests.newUser
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.filters.LargeTest
|
||||||
|
import h_mal.appttude.com.driver.model.DriverProfile
|
||||||
|
import h_mal.appttude.com.driver.model.DriversLicense
|
||||||
|
import h_mal.appttude.com.driver.model.PrivateHireLicense
|
||||||
|
import h_mal.appttude.com.driver.robots.*
|
||||||
|
import h_mal.appttude.com.driver.robots.driver.driversLicense
|
||||||
|
import h_mal.appttude.com.driver.robots.driver.driversProfile
|
||||||
|
import h_mal.appttude.com.driver.robots.driver.privateHireLicenseRobot
|
||||||
|
import org.junit.*
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
|
||||||
|
@LargeTest
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class SubmitNewDriverDataTest : DriverProfileTest() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun signedInUser_uploadsValidLicenseDetails_uploadSuccessful() {
|
||||||
|
driverScreen {
|
||||||
|
driverLicense()
|
||||||
|
}
|
||||||
|
driversLicense {
|
||||||
|
val data = getAssetData<DriversLicense>()
|
||||||
|
submitAndValidate(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun signedInUser_uploadsValidDriverDetails_uploadSuccessful() {
|
||||||
|
driverScreen {
|
||||||
|
driverProfile()
|
||||||
|
}
|
||||||
|
driversProfile {
|
||||||
|
val data = getAssetData<DriverProfile>()
|
||||||
|
submitAndValidate(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun signedInUser_uploadsValidPrivateHireDetails_uploadSuccessful() {
|
||||||
|
driverScreen {
|
||||||
|
privateHireLicense()
|
||||||
|
}
|
||||||
|
privateHireLicenseRobot {
|
||||||
|
val data = getAssetData<PrivateHireLicense>()
|
||||||
|
submitAndValidate(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package h_mal.appttude.com.driver.tests.newUser
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.filters.LargeTest
|
||||||
|
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
|
||||||
|
import h_mal.appttude.com.driver.robots.*
|
||||||
|
import h_mal.appttude.com.driver.robots.vehicle.insurance
|
||||||
|
import h_mal.appttude.com.driver.robots.vehicle.logbook
|
||||||
|
import h_mal.appttude.com.driver.robots.vehicle.mot
|
||||||
|
import h_mal.appttude.com.driver.robots.vehicle.privateHireVehicleLicense
|
||||||
|
import h_mal.appttude.com.driver.robots.vehicle.vehicleProfile
|
||||||
|
import org.junit.*
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
|
||||||
|
@LargeTest
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class SubmitNewVehicleDataTest : VehicleProfileTest() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun signedInUser_uploadsValidVehicleProfile_uploadSuccessful() {
|
||||||
|
vehicleScreen {
|
||||||
|
vehicleProfile()
|
||||||
|
}
|
||||||
|
vehicleProfile {
|
||||||
|
val data = getAssetData<VehicleProfile>()
|
||||||
|
submitAndValidate(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun signedInUser_uploadsValidInsurance_uploadSuccessful() {
|
||||||
|
vehicleScreen {
|
||||||
|
insurance()
|
||||||
|
}
|
||||||
|
insurance {
|
||||||
|
val data = getAssetData<Insurance>()
|
||||||
|
submitAndValidate(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun signedInUser_uploadsValidMot_uploadSuccessful() {
|
||||||
|
vehicleScreen {
|
||||||
|
mot()
|
||||||
|
}
|
||||||
|
mot {
|
||||||
|
val data = getAssetData<Mot>()
|
||||||
|
submitAndValidate(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun signedInUser_uploadsValidLogbook_uploadSuccessful() {
|
||||||
|
vehicleScreen {
|
||||||
|
logbook()
|
||||||
|
}
|
||||||
|
logbook {
|
||||||
|
val data = getAssetData<Logbook>()
|
||||||
|
submitAndValidate(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun signedInUser_uploadsValidPrivateHireVehicleLicense_uploadSuccessful() {
|
||||||
|
vehicleScreen {
|
||||||
|
privateHireVehicleLicense()
|
||||||
|
}
|
||||||
|
privateHireVehicleLicense {
|
||||||
|
val data = getAssetData<PrivateHireVehicle>()
|
||||||
|
submitAndValidate(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package h_mal.appttude.com.driver.tests.newUser
|
||||||
|
|
||||||
|
import h_mal.appttude.com.driver.robots.home
|
||||||
|
|
||||||
|
open class VehicleProfileTest : DataSubmissionTest() {
|
||||||
|
|
||||||
|
override fun afterLaunch() {
|
||||||
|
super.afterLaunch()
|
||||||
|
home {
|
||||||
|
openVehicleProfile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import h_mal.appttude.com.driver.data.FirebaseAuthSource
|
import h_mal.appttude.com.driver.data.FirebaseAuthSource
|
||||||
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
|
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
|
||||||
import h_mal.appttude.com.driver.data.FirebaseStorageSource
|
import h_mal.appttude.com.driver.data.FirebaseStorageSource
|
||||||
import h_mal.appttude.com.driver.data.prefs.PreferenceProvider
|
|
||||||
import h_mal.appttude.com.driver.viewmodels.*
|
import h_mal.appttude.com.driver.viewmodels.*
|
||||||
|
|
||||||
class ApplicationViewModelFactory(
|
class ApplicationViewModelFactory(
|
||||||
|
|||||||
@@ -1,18 +1,8 @@
|
|||||||
package h_mal.appttude.com.driver.application
|
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.bind
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
import org.kodein.di.generic.provider
|
import org.kodein.di.generic.provider
|
||||||
import org.kodein.di.generic.singleton
|
|
||||||
|
|
||||||
class DriverApplication : BaseApplication() {
|
class DriverApplication : BaseApplication() {
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package h_mal.appttude.com.driver.ui
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment
|
import h_mal.appttude.com.driver.base.BaseFragment
|
||||||
import h_mal.appttude.com.driver.data.DRIVER
|
import h_mal.appttude.com.driver.data.DRIVER
|
||||||
import h_mal.appttude.com.driver.databinding.FragmentHomeDriverBinding
|
import h_mal.appttude.com.driver.databinding.FragmentHomeDriverBinding
|
||||||
import h_mal.appttude.com.driver.utils.hide
|
import h_mal.appttude.com.driver.utils.hide
|
||||||
@@ -13,9 +13,7 @@ import h_mal.appttude.com.driver.viewmodels.RoleViewModel
|
|||||||
|
|
||||||
|
|
||||||
class HomeFragment :
|
class HomeFragment :
|
||||||
DataSubmissionBaseFragment<RoleViewModel, FragmentHomeDriverBinding, String>() {
|
BaseFragment<RoleViewModel, FragmentHomeDriverBinding>() {
|
||||||
|
|
||||||
override var model = String()
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import h_mal.appttude.com.driver.viewmodels.DriverLicenseViewModel
|
|||||||
class DriverLicenseFragment :
|
class DriverLicenseFragment :
|
||||||
DataSubmissionBaseFragment<DriverLicenseViewModel, FragmentDriverLicenseBinding, DriversLicense>() {
|
DataSubmissionBaseFragment<DriverLicenseViewModel, FragmentDriverLicenseBinding, DriversLicense>() {
|
||||||
|
|
||||||
override var model = DriversLicense()
|
|
||||||
|
|
||||||
override fun setupView(binding: FragmentDriverLicenseBinding) {
|
override fun setupView(binding: FragmentDriverLicenseBinding) {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
licExpiry.apply {
|
licExpiry.apply {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package h_mal.appttude.com.driver.ui.driverprofile
|
package h_mal.appttude.com.driver.ui.driverprofile
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import h_mal.appttude.com.driver.databinding.FragmentDriverProfileBinding
|
|
||||||
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment
|
import h_mal.appttude.com.driver.base.DataSubmissionBaseFragment
|
||||||
|
import h_mal.appttude.com.driver.databinding.FragmentDriverProfileBinding
|
||||||
import h_mal.appttude.com.driver.dialogs.DateDialog
|
import h_mal.appttude.com.driver.dialogs.DateDialog
|
||||||
import h_mal.appttude.com.driver.model.DriverProfile
|
import h_mal.appttude.com.driver.model.DriverProfile
|
||||||
import h_mal.appttude.com.driver.utils.isTrue
|
import h_mal.appttude.com.driver.utils.isTrue
|
||||||
@@ -13,8 +13,6 @@ import h_mal.appttude.com.driver.viewmodels.DriverProfileViewModel
|
|||||||
class DriverProfileFragment :
|
class DriverProfileFragment :
|
||||||
DataSubmissionBaseFragment<DriverProfileViewModel, FragmentDriverProfileBinding, DriverProfile>() {
|
DataSubmissionBaseFragment<DriverProfileViewModel, FragmentDriverProfileBinding, DriverProfile>() {
|
||||||
|
|
||||||
override var model = DriverProfile()
|
|
||||||
|
|
||||||
override fun setupView(binding: FragmentDriverProfileBinding) = binding.run {
|
override fun setupView(binding: FragmentDriverProfileBinding) = binding.run {
|
||||||
namesInput.setTextOnChange { model.forenames = it }
|
namesInput.setTextOnChange { model.forenames = it }
|
||||||
addressInput.setTextOnChange { model.address = it }
|
addressInput.setTextOnChange { model.address = it }
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import h_mal.appttude.com.driver.viewmodels.PrivateHireLicenseViewModel
|
|||||||
class PrivateHireLicenseFragment : DataSubmissionBaseFragment
|
class PrivateHireLicenseFragment : DataSubmissionBaseFragment
|
||||||
<PrivateHireLicenseViewModel, FragmentPrivateHireLicenseBinding, PrivateHireLicense>() {
|
<PrivateHireLicenseViewModel, FragmentPrivateHireLicenseBinding, PrivateHireLicense>() {
|
||||||
|
|
||||||
override var model = PrivateHireLicense()
|
|
||||||
|
|
||||||
override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run {
|
override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run {
|
||||||
phNo.setTextOnChange { model.phNumber = it }
|
phNo.setTextOnChange { model.phNumber = it }
|
||||||
phExpiry.apply {
|
phExpiry.apply {
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ class InsuranceFragment :
|
|||||||
|
|
||||||
private var selectedImages: List<Uri>? = listOf()
|
private var selectedImages: List<Uri>? = listOf()
|
||||||
|
|
||||||
override var model = Insurance()
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setImageSelectionAsMultiple()
|
setImageSelectionAsMultiple()
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import h_mal.appttude.com.driver.viewmodels.LogbookViewModel
|
|||||||
class LogbookFragment :
|
class LogbookFragment :
|
||||||
DataSubmissionBaseFragment<LogbookViewModel, FragmentLogbookBinding, Logbook>() {
|
DataSubmissionBaseFragment<LogbookViewModel, FragmentLogbookBinding, Logbook>() {
|
||||||
|
|
||||||
override var model = Logbook()
|
|
||||||
|
|
||||||
override fun setupView(binding: FragmentLogbookBinding) = binding.run {
|
override fun setupView(binding: FragmentLogbookBinding) = binding.run {
|
||||||
v5cNo.setTextOnChange { model.v5cnumber = it }
|
v5cNo.setTextOnChange { model.v5cnumber = it }
|
||||||
uploadLb.setOnClickListener { openGalleryWithPermissionRequest() }
|
uploadLb.setOnClickListener { openGalleryWithPermissionRequest() }
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import h_mal.appttude.com.driver.viewmodels.MotViewModel
|
|||||||
|
|
||||||
class MotFragment : DataSubmissionBaseFragment<MotViewModel, FragmentMotBinding, Mot>() {
|
class MotFragment : DataSubmissionBaseFragment<MotViewModel, FragmentMotBinding, Mot>() {
|
||||||
|
|
||||||
override var model = Mot()
|
|
||||||
|
|
||||||
override fun setupView(binding: FragmentMotBinding) = binding.run {
|
override fun setupView(binding: FragmentMotBinding) = binding.run {
|
||||||
motExpiry.apply {
|
motExpiry.apply {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import h_mal.appttude.com.driver.viewmodels.PrivateHireVehicleViewModel
|
|||||||
class PrivateHireVehicleFragment :
|
class PrivateHireVehicleFragment :
|
||||||
DataSubmissionBaseFragment<PrivateHireVehicleViewModel, FragmentPrivateHireLicenseBinding, PrivateHireVehicle>() {
|
DataSubmissionBaseFragment<PrivateHireVehicleViewModel, FragmentPrivateHireLicenseBinding, PrivateHireVehicle>() {
|
||||||
|
|
||||||
override var model = PrivateHireVehicle()
|
|
||||||
|
|
||||||
override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run {
|
override fun setupView(binding: FragmentPrivateHireLicenseBinding) = binding.run {
|
||||||
phNo.setTextOnChange { model.phCarNumber = it }
|
phNo.setTextOnChange { model.phCarNumber = it }
|
||||||
phExpiry.apply {
|
phExpiry.apply {
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import h_mal.appttude.com.driver.viewmodels.VehicleProfileViewModel
|
|||||||
class VehicleProfileFragment : DataSubmissionBaseFragment
|
class VehicleProfileFragment : DataSubmissionBaseFragment
|
||||||
<VehicleProfileViewModel, FragmentVehicleSetupBinding, VehicleProfile>() {
|
<VehicleProfileViewModel, FragmentVehicleSetupBinding, VehicleProfile>() {
|
||||||
|
|
||||||
override var model = VehicleProfile()
|
|
||||||
|
|
||||||
override fun setupView(binding: FragmentVehicleSetupBinding) = binding.run {
|
override fun setupView(binding: FragmentVehicleSetupBinding) = binding.run {
|
||||||
reg.setTextOnChange { model.reg = it }
|
reg.setTextOnChange { model.reg = it }
|
||||||
make.setTextOnChange { model.make = it }
|
make.setTextOnChange { model.make = it }
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.user.LoginActivity"
|
android:name=".ui.user.LoginActivity"
|
||||||
android:theme="@style/AppTheme.NoActionBar.User"
|
android:exported="true"
|
||||||
android:exported="true">
|
android:theme="@style/AppTheme.NoActionBar.User">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
package h_mal.appttude.com.driver.application
|
package h_mal.appttude.com.driver.application
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import androidx.startup.AppInitializer
|
||||||
import h_mal.appttude.com.driver.data.FirebaseAuthSource
|
import h_mal.appttude.com.driver.data.FirebaseAuthSource
|
||||||
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
|
import h_mal.appttude.com.driver.data.FirebaseDatabaseSource
|
||||||
import h_mal.appttude.com.driver.data.FirebaseStorageSource
|
import h_mal.appttude.com.driver.data.FirebaseStorageSource
|
||||||
|
import net.danlew.android.joda.JodaTimeInitializer
|
||||||
import org.kodein.di.Kodein
|
import org.kodein.di.Kodein
|
||||||
import org.kodein.di.KodeinAware
|
import org.kodein.di.KodeinAware
|
||||||
import org.kodein.di.android.x.androidXModule
|
import org.kodein.di.android.x.androidXModule
|
||||||
import org.kodein.di.generic.bind
|
import org.kodein.di.generic.bind
|
||||||
import org.kodein.di.generic.singleton
|
import org.kodein.di.generic.singleton
|
||||||
|
|
||||||
|
const val GLOBAL_FORMAT = "dd/MM/yyyy"
|
||||||
open class BaseApplication : Application(), KodeinAware {
|
open class BaseApplication : Application(), KodeinAware {
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
AppInitializer.getInstance(this).initializeComponent(JodaTimeInitializer::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
// Kodein aware to initialise the classes used for DI
|
// Kodein aware to initialise the classes used for DI
|
||||||
override val kodein = Kodein.lazy {
|
override val kodein = Kodein.lazy {
|
||||||
import(parentModule)
|
import(parentModule)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.content.Intent
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.OnAttachStateChangeListener
|
|
||||||
import android.view.ViewGroup.LayoutParams
|
import android.view.ViewGroup.LayoutParams
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
import android.view.ViewGroup.inflate
|
import android.view.ViewGroup.inflate
|
||||||
@@ -156,15 +155,18 @@ abstract class BaseActivity<V : BaseViewModel, VB : ViewBinding> : AppCompatActi
|
|||||||
super.onToastHidden()
|
super.onToastHidden()
|
||||||
mIdlingResource?.setIdleState(true)
|
mIdlingResource?.setIdleState(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onToastShown() {
|
override fun onToastShown() {
|
||||||
super.onToastShown()
|
super.onToastShown()
|
||||||
mIdlingResource?.setIdleState(false)
|
mIdlingResource?.setIdleState(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
toast.show()
|
toast.show()
|
||||||
|
} else {
|
||||||
|
mIdlingResource?.setIdleState(true)
|
||||||
|
toast.show()
|
||||||
|
mIdlingResource?.setIdleState(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showSnackBar(message: String) {
|
fun showSnackBar(message: String) {
|
||||||
@@ -178,6 +180,7 @@ abstract class BaseActivity<V : BaseViewModel, VB : ViewBinding> : AppCompatActi
|
|||||||
super.onShown(transientBottomBar)
|
super.onShown(transientBottomBar)
|
||||||
mIdlingResource?.setIdleState(false)
|
mIdlingResource?.setIdleState(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||||
super.onDismissed(transientBottomBar, event)
|
super.onDismissed(transientBottomBar, event)
|
||||||
mIdlingResource?.setIdleState(true)
|
mIdlingResource?.setIdleState(true)
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ open class BaseFirebaseAdapter<T : Any, VB : ViewBinding>(
|
|||||||
super.onDataChanged()
|
super.onDataChanged()
|
||||||
if (itemCount == 0) emptyList()
|
if (itemCount == 0) emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(error: DatabaseError) {
|
override fun onError(error: DatabaseError) {
|
||||||
super.onError(error)
|
super.onError(error)
|
||||||
when (error.code) {
|
when (error.code) {
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
package h_mal.appttude.com.driver.base
|
package h_mal.appttude.com.driver.base
|
||||||
|
|
||||||
import android.content.ClipData
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.activity.result.contract.ActivityResultContract
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.createViewModelLazy
|
import androidx.fragment.app.createViewModelLazy
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
@@ -16,7 +11,6 @@ import h_mal.appttude.com.driver.application.ApplicationViewModelFactory
|
|||||||
import h_mal.appttude.com.driver.data.ViewState
|
import h_mal.appttude.com.driver.data.ViewState
|
||||||
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
|
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
|
||||||
import h_mal.appttude.com.driver.utils.GenericsHelper.inflateBindingByType
|
import h_mal.appttude.com.driver.utils.GenericsHelper.inflateBindingByType
|
||||||
import h_mal.appttude.com.driver.utils.PermissionsUtils
|
|
||||||
import org.kodein.di.KodeinAware
|
import org.kodein.di.KodeinAware
|
||||||
import org.kodein.di.android.x.kodein
|
import org.kodein.di.android.x.kodein
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
@@ -36,12 +30,6 @@ abstract class BaseFragment<V : BaseViewModel, VB : ViewBinding> : Fragment(), K
|
|||||||
private fun getFragmentViewModel(): Lazy<V> =
|
private fun getFragmentViewModel(): Lazy<V> =
|
||||||
createViewModelLazy(getGenericClassAt(0), { viewModelStore }, factoryProducer = { factory })
|
createViewModelLazy(getGenericClassAt(0), { viewModelStore }, factoryProducer = { factory })
|
||||||
|
|
||||||
private var multipleImage: Boolean = false
|
|
||||||
|
|
||||||
fun setImageSelectionAsMultiple() {
|
|
||||||
multipleImage = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
@@ -101,73 +89,6 @@ abstract class BaseFragment<V : BaseViewModel, VB : ViewBinding> : Fragment(), K
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ClipData.convertToList(): List<Uri> = 0.rangeTo(itemCount).map { getItemAt(it).uri }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pair with {@link #Fragment.onRequestPermissionsResult}
|
|
||||||
* @param ourRequestCode
|
|
||||||
* @param requestCode
|
|
||||||
* checks that ourRequestCode was granted
|
|
||||||
* sends callback with
|
|
||||||
* @param permissionGranted
|
|
||||||
*/
|
|
||||||
fun onPermissionRequest(
|
|
||||||
requestCode: Int, ourRequestCode: Int, grantResults: IntArray,
|
|
||||||
permissionGranted: () -> Unit
|
|
||||||
) {
|
|
||||||
when (requestCode) {
|
|
||||||
ourRequestCode -> {
|
|
||||||
if (PermissionsUtils.isGranted(grantResults)) {
|
|
||||||
permissionGranted.invoke()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called on the result of image selection
|
|
||||||
*/
|
|
||||||
open fun onImageGalleryResult(imageUri: Uri?) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called on the result of multiple image selection
|
|
||||||
*/
|
|
||||||
open fun onImageGalleryResult(imageUris: List<Uri>?) {}
|
|
||||||
|
|
||||||
fun openGalleryForImage() {
|
|
||||||
permissionRequest.launch(multipleImage)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val permissionRequest = registerForActivityResult(getResultsContract()) { result ->
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
when (result) {
|
|
||||||
is Uri -> onImageGalleryResult(result)
|
|
||||||
is List<*> -> onImageGalleryResult(result as List<Uri>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getResultsContract(): ActivityResultContract<Boolean, Any?> {
|
|
||||||
return object : ActivityResultContract<Boolean, Any?>() {
|
|
||||||
override fun createIntent(context: Context, input: Boolean): Intent {
|
|
||||||
return Intent(Intent.ACTION_GET_CONTENT)
|
|
||||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, input)
|
|
||||||
.setType("image/*")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun parseResult(resultCode: Int, intent: Intent?): Any? {
|
|
||||||
intent?.clipData?.takeIf { it.itemCount > 1 }?.convertToList()?.let { clip ->
|
|
||||||
val list = clip.takeIf { it.size > 10 }?.let {
|
|
||||||
clip.subList(0, 9)
|
|
||||||
} ?: clip
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
return intent?.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showToast(message: String) = (activity as BaseActivity<*, *>).showToast(message)
|
fun showToast(message: String) = (activity as BaseActivity<*, *>).showToast(message)
|
||||||
fun showSnackBar(message: String) = (activity as BaseActivity<*, *>).showSnackBar(message)
|
fun showSnackBar(message: String) = (activity as BaseActivity<*, *>).showSnackBar(message)
|
||||||
}
|
}
|
||||||
@@ -1,26 +1,22 @@
|
|||||||
package h_mal.appttude.com.driver.base
|
package h_mal.appttude.com.driver.base
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import h_mal.appttude.com.driver.data.UserAuthState
|
import h_mal.appttude.com.driver.data.UserAuthState
|
||||||
|
import h_mal.appttude.com.driver.model.Model
|
||||||
import h_mal.appttude.com.driver.ui.user.LoginActivity
|
import h_mal.appttude.com.driver.ui.user.LoginActivity
|
||||||
import h_mal.appttude.com.driver.utils.PermissionsUtils.askForPermissions
|
import h_mal.appttude.com.driver.utils.GenericsHelper.getGenericClassAt
|
||||||
import h_mal.appttude.com.driver.utils.TextValidationUtils.validateEditText
|
import h_mal.appttude.com.driver.utils.TextValidationUtils.validateEditText
|
||||||
|
import kotlin.reflect.full.createInstance
|
||||||
|
|
||||||
private const val IMAGE_PERMISSION_RESULT = 402
|
abstract class DataSubmissionBaseFragment<V : DataSubmissionBaseViewModel<T>, VB : ViewBinding, T : Model> :
|
||||||
|
ImageSelectorFragment<V, VB>() {
|
||||||
|
|
||||||
abstract class DataSubmissionBaseFragment<V : DataSubmissionBaseViewModel<T>, VB : ViewBinding, T : Any> :
|
var model: T = getGenericClassAt<T>(2).createInstance()
|
||||||
BaseFragment<V, VB>() {
|
|
||||||
|
|
||||||
var picUri: Uri? = null
|
|
||||||
|
|
||||||
abstract var model: T
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
@@ -52,17 +48,7 @@ abstract class DataSubmissionBaseFragment<V : DataSubmissionBaseViewModel<T>, VB
|
|||||||
open fun submit() {}
|
open fun submit() {}
|
||||||
|
|
||||||
fun openGalleryWithPermissionRequest() {
|
fun openGalleryWithPermissionRequest() {
|
||||||
if (askForPermissions(Manifest.permission.READ_EXTERNAL_STORAGE, IMAGE_PERMISSION_RESULT)) {
|
showStorageWithPermissionCheck()
|
||||||
openGalleryForImage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
|
||||||
requestCode: Int,
|
|
||||||
permissions: Array<String>,
|
|
||||||
grantResults: IntArray
|
|
||||||
) = onPermissionRequest(requestCode, IMAGE_PERMISSION_RESULT, grantResults) {
|
|
||||||
openGalleryForImage()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun validateEditTexts(vararg editTexts: EditText): Boolean {
|
fun validateEditTexts(vararg editTexts: EditText): Boolean {
|
||||||
@@ -81,8 +67,4 @@ abstract class DataSubmissionBaseFragment<V : DataSubmissionBaseViewModel<T>, VB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onImageGalleryResult(imageUri: Uri?) {
|
|
||||||
super.onImageGalleryResult(imageUri)
|
|
||||||
picUri = imageUri
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package h_mal.appttude.com.driver.base
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import h_mal.appttude.com.driver.ui.permission.PermissionsDeclarationDialog
|
||||||
|
import permissions.dispatcher.NeedsPermission
|
||||||
|
import permissions.dispatcher.OnNeverAskAgain
|
||||||
|
import permissions.dispatcher.OnPermissionDenied
|
||||||
|
import permissions.dispatcher.OnShowRationale
|
||||||
|
import permissions.dispatcher.PermissionRequest
|
||||||
|
import permissions.dispatcher.RuntimePermissions
|
||||||
|
|
||||||
|
@RuntimePermissions
|
||||||
|
open class ImageSelectorFragment<V : BaseViewModel, VB : ViewBinding> : BaseFragment<V, VB>() {
|
||||||
|
private var multipleImage: Boolean = false
|
||||||
|
var picUri: Uri? = null
|
||||||
|
|
||||||
|
fun setImageSelectionAsMultiple() {
|
||||||
|
multipleImage = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openGalleryForImage() {
|
||||||
|
permissionRequest.launch(multipleImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val permissionRequest = registerForActivityResult(getResultsContract()) { result ->
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
when (result) {
|
||||||
|
is Uri -> onImageGalleryResult(result)
|
||||||
|
is List<*> -> onImageGalleryResult(result as List<Uri>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getResultsContract(): ActivityResultContract<Boolean, Any?> {
|
||||||
|
return object : ActivityResultContract<Boolean, Any?>() {
|
||||||
|
override fun createIntent(context: Context, input: Boolean): Intent {
|
||||||
|
return Intent(Intent.ACTION_GET_CONTENT)
|
||||||
|
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, input)
|
||||||
|
.setType("image/*")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseResult(resultCode: Int, intent: Intent?): Any? {
|
||||||
|
intent?.clipData?.takeIf { it.itemCount > 1 }?.convertToList()?.let { clip ->
|
||||||
|
val list = clip.takeIf { it.size > 10 }?.let {
|
||||||
|
clip.subList(0, 9)
|
||||||
|
} ?: clip
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
return intent?.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ClipData.convertToList(): List<Uri> = 0.rangeTo(itemCount).map { getItemAt(it).uri }
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
// NOTE: delegate the permission handling to generated method
|
||||||
|
onRequestPermissionsResult(requestCode, grantResults)
|
||||||
|
}
|
||||||
|
|
||||||
|
@NeedsPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
fun showStorage() {
|
||||||
|
openGalleryForImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnShowRationale(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
fun showRationaleForStorage(request: PermissionRequest) {
|
||||||
|
PermissionsDeclarationDialog(requireContext()).showDialog({
|
||||||
|
request.proceed()
|
||||||
|
}, {
|
||||||
|
request.cancel()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnPermissionDenied(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
fun onStorageDenied() {
|
||||||
|
showToast("Storage permissions have been denied")
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnNeverAskAgain(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
fun onStorageNeverAskAgain() {
|
||||||
|
showToast("Storage permissions have been to never ask again")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on the result of image selection
|
||||||
|
*/
|
||||||
|
open fun onImageGalleryResult(imageUri: Uri?) {
|
||||||
|
picUri = imageUri
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on the result of multiple image selection
|
||||||
|
*/
|
||||||
|
open fun onImageGalleryResult(imageUris: List<Uri>?) {}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import androidx.preference.PreferenceManager
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const val SORT_OPTION = "SORT_OPTION"
|
const val SORT_OPTION = "SORT_OPTION"
|
||||||
|
|
||||||
class PreferenceProvider(context: Context) {
|
class PreferenceProvider(context: Context) {
|
||||||
|
|
||||||
private val appContext = context.applicationContext
|
private val appContext = context.applicationContext
|
||||||
|
|||||||
@@ -3,14 +3,11 @@ package h_mal.appttude.com.driver.dialogs
|
|||||||
|
|
||||||
import android.app.DatePickerDialog
|
import android.app.DatePickerDialog
|
||||||
import android.app.DatePickerDialog.OnDateSetListener
|
import android.app.DatePickerDialog.OnDateSetListener
|
||||||
import android.icu.util.Calendar
|
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import h_mal.appttude.com.driver.R
|
import h_mal.appttude.com.driver.R
|
||||||
import h_mal.appttude.com.driver.utils.DateUtils
|
import h_mal.appttude.com.driver.utils.DateUtils
|
||||||
|
|
||||||
|
|
||||||
private const val DATE_FORMAT = "dd/MM/yyyy"
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
class DateDialog(
|
class DateDialog(
|
||||||
private val editText: EditText,
|
private val editText: EditText,
|
||||||
@@ -19,10 +16,7 @@ class DateDialog(
|
|||||||
|
|
||||||
private val dateSetListener: OnDateSetListener =
|
private val dateSetListener: OnDateSetListener =
|
||||||
OnDateSetListener { _, year, month, dayOfMonth ->
|
OnDateSetListener { _, year, month, dayOfMonth ->
|
||||||
val cal = Calendar.getInstance()
|
val date = DateUtils.getDateString(year, month, dayOfMonth)
|
||||||
cal.set(year, month + 1, dayOfMonth)
|
|
||||||
|
|
||||||
val date = DateUtils.parseCalenderIntoDateString(cal, DATE_FORMAT)
|
|
||||||
dateSelected(date)
|
dateSelected(date)
|
||||||
editText.setText(date)
|
editText.setText(date)
|
||||||
editText.error = null
|
editText.error = null
|
||||||
@@ -33,27 +27,17 @@ class DateDialog(
|
|||||||
spinnersShown = true
|
spinnersShown = true
|
||||||
calendarViewShown = false
|
calendarViewShown = false
|
||||||
}
|
}
|
||||||
val dateString = editText.text?.toString()
|
val dateString = editText.text.toString()
|
||||||
val date = if (dateString.isNullOrBlank()) {
|
val date = DateUtils.parseDateStringIntoCalender(dateString)
|
||||||
// Set time to now
|
|
||||||
Calendar.getInstance()
|
|
||||||
} else {
|
|
||||||
// Parse current edit text string and set value
|
|
||||||
DateUtils.parseDateStringIntoCalender(dateString, DATE_FORMAT)
|
|
||||||
?: Calendar.getInstance()
|
|
||||||
}
|
|
||||||
setDateFromCalender(date)
|
setDateFromCalender(date)
|
||||||
setOnDateSetListener(dateSetListener)
|
setOnDateSetListener(dateSetListener)
|
||||||
setTitle(context.getString(R.string.set_date))
|
setTitle(context.getString(R.string.set_date))
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setDateFromCalender(calendar: Calendar) {
|
private fun setDateFromCalender(calendar: org.joda.time.LocalDate) {
|
||||||
val mYear = calendar.get(Calendar.YEAR)
|
updateDate(calendar.year, calendar.monthOfYear, calendar.dayOfMonth)
|
||||||
val mMonth = calendar.get(Calendar.MONTH)
|
|
||||||
val mDay = calendar.get(Calendar.DAY_OF_MONTH)
|
|
||||||
|
|
||||||
updateDate(mYear, mMonth, mDay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,4 +8,4 @@ data class DriverProfile(
|
|||||||
var dob: String? = null,
|
var dob: String? = null,
|
||||||
var ni: String? = null,
|
var ni: String? = null,
|
||||||
var dateFirst: String? = null
|
var dateFirst: String? = null
|
||||||
)
|
) : Model
|
||||||
@@ -5,4 +5,4 @@ data class DriversLicense(
|
|||||||
var licenseImageString: String? = null,
|
var licenseImageString: String? = null,
|
||||||
var licenseNumber: String? = null,
|
var licenseNumber: String? = null,
|
||||||
var licenseExpiry: String? = null
|
var licenseExpiry: String? = null
|
||||||
)
|
) : Model
|
||||||
@@ -4,4 +4,4 @@ data class Insurance(
|
|||||||
var photoStrings: MutableList<String?>? = null,
|
var photoStrings: MutableList<String?>? = null,
|
||||||
var insurerName: String? = null,
|
var insurerName: String? = null,
|
||||||
var expiryDate: String? = null
|
var expiryDate: String? = null
|
||||||
)
|
) : Model
|
||||||
@@ -4,4 +4,4 @@ package h_mal.appttude.com.driver.model
|
|||||||
data class Logbook(
|
data class Logbook(
|
||||||
var photoString: String? = null,
|
var photoString: String? = null,
|
||||||
var v5cnumber: String? = null
|
var v5cnumber: String? = null
|
||||||
)
|
) : Model
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package h_mal.appttude.com.driver.model
|
||||||
|
|
||||||
|
interface Model
|
||||||
@@ -4,4 +4,4 @@ package h_mal.appttude.com.driver.model
|
|||||||
data class Mot(
|
data class Mot(
|
||||||
var motImageString: String? = null,
|
var motImageString: String? = null,
|
||||||
var motExpiry: String? = null
|
var motExpiry: String? = null
|
||||||
)
|
) : Model
|
||||||
@@ -5,4 +5,4 @@ data class PrivateHireLicense(
|
|||||||
var phImageString: String? = null,
|
var phImageString: String? = null,
|
||||||
var phNumber: String? = null,
|
var phNumber: String? = null,
|
||||||
var phExpiry: String? = null
|
var phExpiry: String? = null
|
||||||
)
|
) : Model
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package h_mal.appttude.com.driver.model
|
package h_mal.appttude.com.driver.model
|
||||||
|
|
||||||
|
|
||||||
class PrivateHireVehicle(
|
data class PrivateHireVehicle(
|
||||||
var phCarImageString: String? = null,
|
var phCarImageString: String? = null,
|
||||||
var phCarNumber: String? = null,
|
var phCarNumber: String? = null,
|
||||||
var phCarExpiry: String? = null
|
var phCarExpiry: String? = null
|
||||||
)
|
) : Model
|
||||||
@@ -11,4 +11,4 @@ data class VehicleProfile(
|
|||||||
var keeperPostCode: String? = null,
|
var keeperPostCode: String? = null,
|
||||||
var startDate: String? = null,
|
var startDate: String? = null,
|
||||||
var isSeized: Boolean = false
|
var isSeized: Boolean = false
|
||||||
)
|
) : Model
|
||||||
3
app/src/main/java/h_mal/appttude/com/driver/ui/.idea/.gitignore
generated
vendored
3
app/src/main/java/h_mal/appttude/com/driver/ui/.idea/.gitignore
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectDictionaryState">
|
|
||||||
<dictionary name="h_mal" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Kotlin2JsCompilerArguments">
|
|
||||||
<option name="sourceMapEmbedSources" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CMakeSettings">
|
|
||||||
<configurations>
|
|
||||||
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
|
|
||||||
</configurations>
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager">
|
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/ui.iml" filepath="$PROJECT_DIR$/ui.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$/../../../../../../../.." vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package h_mal.appttude.com.driver.ui.permission
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.Html
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
|
interface DeclarationBuilder {
|
||||||
|
val link: String
|
||||||
|
val message: String
|
||||||
|
|
||||||
|
fun Context.readFromResources(@StringRes id: Int) = resources.getString(id)
|
||||||
|
|
||||||
|
fun buildMessage(): CharSequence? {
|
||||||
|
val link1 = "<font color='blue'><a href=\"$link\">here</a></font>"
|
||||||
|
val message = "$message See my privacy policy: $link1"
|
||||||
|
return Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package h_mal.appttude.com.driver.ui.permission
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionsDeclarationDialog(context: Context) : BaseDeclarationDialog(context) {
|
||||||
|
|
||||||
|
override val link: String = "https://sites.google.com/view/hmaldev/home/choice-cars"
|
||||||
|
override val message: String =
|
||||||
|
"Storage is required to access images on the devices"
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BaseDeclarationDialog(val context: Context) : DeclarationBuilder {
|
||||||
|
abstract override val link: String
|
||||||
|
abstract override val message: String
|
||||||
|
|
||||||
|
lateinit var dialog: AlertDialog
|
||||||
|
|
||||||
|
fun showDialog(agreeCallback: () -> Unit = { }, disagreeCallback: () -> Unit = { }) {
|
||||||
|
val myMessage = buildMessage()
|
||||||
|
|
||||||
|
val builder = AlertDialog.Builder(context)
|
||||||
|
.setPositiveButton("agree") { _, _ ->
|
||||||
|
agreeCallback()
|
||||||
|
}
|
||||||
|
.setNegativeButton("disagree") { _, _ ->
|
||||||
|
disagreeCallback()
|
||||||
|
}
|
||||||
|
.setMessage(myMessage)
|
||||||
|
.setCancelable(false)
|
||||||
|
|
||||||
|
dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
// Make the textview clickable. Must be called after show()
|
||||||
|
val msgTxt = dialog.findViewById<View>(android.R.id.message) as TextView?
|
||||||
|
msgTxt?.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismiss() = dialog.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
package h_mal.appttude.com.driver.ui.update
|
package h_mal.appttude.com.driver.ui.update
|
||||||
|
|
||||||
import h_mal.appttude.com.driver.base.BaseActivity
|
import h_mal.appttude.com.driver.base.BaseActivity
|
||||||
import h_mal.appttude.com.driver.data.FirebaseCompletion
|
import h_mal.appttude.com.driver.data.FirebaseCompletion.Changed
|
||||||
|
import h_mal.appttude.com.driver.data.FirebaseCompletion.ProfileDeleted
|
||||||
import h_mal.appttude.com.driver.databinding.UpdateActivityBinding
|
import h_mal.appttude.com.driver.databinding.UpdateActivityBinding
|
||||||
import h_mal.appttude.com.driver.viewmodels.UpdateUserViewModel
|
import h_mal.appttude.com.driver.viewmodels.UpdateUserViewModel
|
||||||
|
|
||||||
@@ -10,7 +11,8 @@ class UpdateActivity : BaseActivity<UpdateUserViewModel, UpdateActivityBinding>(
|
|||||||
override fun onSuccess(data: Any?) {
|
override fun onSuccess(data: Any?) {
|
||||||
super.onSuccess(data)
|
super.onSuccess(data)
|
||||||
when (data) {
|
when (data) {
|
||||||
is FirebaseCompletion.Changed -> showToast(data.message)
|
is Changed -> showSnackBar(data.message)
|
||||||
|
is ProfileDeleted -> showToast(data.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
package h_mal.appttude.com.driver.ui.update
|
package h_mal.appttude.com.driver.ui.update
|
||||||
|
|
||||||
import android.Manifest.permission.READ_EXTERNAL_STORAGE
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import com.google.firebase.auth.FirebaseUser
|
import com.google.firebase.auth.FirebaseUser
|
||||||
import h_mal.appttude.com.driver.base.BaseFragment
|
import h_mal.appttude.com.driver.base.ImageSelectorFragment
|
||||||
import h_mal.appttude.com.driver.databinding.FragmentUpdateProfileBinding
|
import h_mal.appttude.com.driver.databinding.FragmentUpdateProfileBinding
|
||||||
import h_mal.appttude.com.driver.utils.PermissionsUtils.askForPermissions
|
|
||||||
import h_mal.appttude.com.driver.utils.setEnterPressedListener
|
import h_mal.appttude.com.driver.utils.setEnterPressedListener
|
||||||
import h_mal.appttude.com.driver.utils.setGlideImage
|
import h_mal.appttude.com.driver.utils.setGlideImage
|
||||||
import h_mal.appttude.com.driver.viewmodels.UpdateUserViewModel
|
import h_mal.appttude.com.driver.viewmodels.UpdateUserViewModel
|
||||||
|
|
||||||
const val TAG_CONST = "non-user"
|
const val TAG_CONST = "non-user"
|
||||||
private const val IMAGE_PERMISSION_RESULT = 402
|
|
||||||
|
|
||||||
class UpdateProfileFragment : BaseFragment<UpdateUserViewModel, FragmentUpdateProfileBinding>() {
|
class UpdateProfileFragment :
|
||||||
|
ImageSelectorFragment<UpdateUserViewModel, FragmentUpdateProfileBinding>() {
|
||||||
|
|
||||||
private var imageChangeListener: Boolean = false
|
private var imageChangeListener: Boolean = false
|
||||||
private var nameChangeListener: Boolean = false
|
private var nameChangeListener: Boolean = false
|
||||||
@@ -35,11 +33,7 @@ class UpdateProfileFragment : BaseFragment<UpdateUserViewModel, FragmentUpdatePr
|
|||||||
setEnterPressedListener { submitProfileUpdate() }
|
setEnterPressedListener { submitProfileUpdate() }
|
||||||
}
|
}
|
||||||
|
|
||||||
profileImg.setOnClickListener {
|
profileImg.setOnClickListener { openGalleryForImage() }
|
||||||
if (askForPermissions(READ_EXTERNAL_STORAGE, IMAGE_PERMISSION_RESULT)) {
|
|
||||||
openGalleryForImage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
submit.setOnClickListener { submitProfileUpdate() }
|
submit.setOnClickListener { submitProfileUpdate() }
|
||||||
}
|
}
|
||||||
@@ -53,15 +47,6 @@ class UpdateProfileFragment : BaseFragment<UpdateUserViewModel, FragmentUpdatePr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
|
||||||
requestCode: Int,
|
|
||||||
permissions: Array<out String>,
|
|
||||||
grantResults: IntArray
|
|
||||||
) = onPermissionRequest(requestCode, IMAGE_PERMISSION_RESULT, grantResults) {
|
|
||||||
openGalleryForImage()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun onSuccess(data: Any?) {
|
override fun onSuccess(data: Any?) {
|
||||||
super.onSuccess(data)
|
super.onSuccess(data)
|
||||||
if (data is FirebaseUser) setFields(data)
|
if (data is FirebaseUser) setFields(data)
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package h_mal.appttude.com.driver.utils
|
package h_mal.appttude.com.driver.utils
|
||||||
|
|
||||||
import android.icu.util.Calendar
|
import h_mal.appttude.com.driver.application.GLOBAL_FORMAT
|
||||||
|
import org.joda.time.LocalDate
|
||||||
|
import org.joda.time.format.DateTimeFormat
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
object DateUtils {
|
object DateUtils {
|
||||||
|
|
||||||
fun getDateTimeStamp(): String {
|
fun getDateTimeStamp(): String {
|
||||||
@@ -27,26 +30,19 @@ object DateUtils {
|
|||||||
|
|
||||||
private fun getSimpleDateFormat(format: String) = SimpleDateFormat(format, Locale.getDefault())
|
private fun getSimpleDateFormat(format: String) = SimpleDateFormat(format, Locale.getDefault())
|
||||||
|
|
||||||
fun parseDateStringIntoCalender(dateString: String, format: String): Calendar? {
|
fun parseDateStringIntoCalender(dateString: String, format: String = GLOBAL_FORMAT): LocalDate {
|
||||||
val dateFormat = getSimpleDateFormat(format)
|
if (dateString.isBlank()) {
|
||||||
val calendar = Calendar.getInstance()
|
return LocalDate.now()
|
||||||
return try {
|
|
||||||
calendar.time = dateFormat.parse(dateString)
|
|
||||||
calendar
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
val dtf = DateTimeFormat.forPattern(format)
|
||||||
|
return dtf.parseLocalDate(dateString)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseCalenderIntoDateString(calendar: Calendar, format: String): String? {
|
fun getDateString(year: Int, month: Int, dayOfMonth: Int): String {
|
||||||
val date = calendar.time
|
val date = LocalDate.now()
|
||||||
val dateFormat = getSimpleDateFormat(format)
|
.withYear(year)
|
||||||
|
.withMonthOfYear(month + 1)
|
||||||
return try {
|
.withDayOfMonth(dayOfMonth)
|
||||||
dateFormat.format(date)
|
return date.toString(GLOBAL_FORMAT)
|
||||||
} catch (e: ParseException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,31 +15,43 @@ class FirebaseException(
|
|||||||
|
|
||||||
enum class Status(private val code: Int) {
|
enum class Status(private val code: Int) {
|
||||||
DATA_STALE(-1),
|
DATA_STALE(-1),
|
||||||
|
|
||||||
/** The server indicated that this operation failed */
|
/** The server indicated that this operation failed */
|
||||||
OPERATION_FAILED(-2),
|
OPERATION_FAILED(-2),
|
||||||
|
|
||||||
/** This client does not have permission to perform this operation */
|
/** This client does not have permission to perform this operation */
|
||||||
PERMISSION_DENIED(-3),
|
PERMISSION_DENIED(-3),
|
||||||
|
|
||||||
/** The operation had to be aborted due to a network disconnect */
|
/** The operation had to be aborted due to a network disconnect */
|
||||||
DISCONNECTED(-4),
|
DISCONNECTED(-4),
|
||||||
|
|
||||||
/** The supplied auth token has expired */
|
/** The supplied auth token has expired */
|
||||||
EXPIRED_TOKEN(-6),
|
EXPIRED_TOKEN(-6),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The specified authentication token is invalid. This can occur when the token is malformed,
|
* 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.
|
* expired, or the secret that was used to generate it has been revoked.
|
||||||
*/
|
*/
|
||||||
INVALID_TOKEN(-7),
|
INVALID_TOKEN(-7),
|
||||||
|
|
||||||
/** The transaction had too many retries */
|
/** The transaction had too many retries */
|
||||||
MAX_RETRIES(-8),
|
MAX_RETRIES(-8),
|
||||||
|
|
||||||
/** The transaction was overridden by a subsequent set */
|
/** The transaction was overridden by a subsequent set */
|
||||||
OVERRIDDEN_BY_SET(-9),
|
OVERRIDDEN_BY_SET(-9),
|
||||||
|
|
||||||
/** The service is unavailable */
|
/** The service is unavailable */
|
||||||
UNAVAILABLE(-10),
|
UNAVAILABLE(-10),
|
||||||
|
|
||||||
/** An exception occurred in user code */
|
/** An exception occurred in user code */
|
||||||
USER_CODE_EXCEPTION(-11),
|
USER_CODE_EXCEPTION(-11),
|
||||||
|
|
||||||
/** The operation could not be performed due to a network error. */
|
/** The operation could not be performed due to a network error. */
|
||||||
NETWORK_ERROR(-24),
|
NETWORK_ERROR(-24),
|
||||||
|
|
||||||
/** The write was canceled locally */
|
/** The write was canceled locally */
|
||||||
WRITE_CANCELED(-25),
|
WRITE_CANCELED(-25),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unknown error occurred. Please refer to the error message and error details for more
|
* An unknown error occurred. Please refer to the error message and error details for more
|
||||||
* information.
|
* information.
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ suspend inline fun <reified T : Any> DatabaseReference.getDataFromDatabaseRef():
|
|||||||
is EventResponse.Changed -> {
|
is EventResponse.Changed -> {
|
||||||
response.snapshot.getValue(T::class.java)
|
response.snapshot.getValue(T::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
is EventResponse.Cancelled -> {
|
is EventResponse.Cancelled -> {
|
||||||
throw FirebaseException(response.error)
|
throw FirebaseException(response.error)
|
||||||
}
|
}
|
||||||
@@ -53,6 +54,7 @@ suspend inline fun <reified T : Any> DatabaseReference.getListDataFromDatabaseRe
|
|||||||
is EventResponse.Changed -> {
|
is EventResponse.Changed -> {
|
||||||
response.snapshot.children.map { it.getValue(T::class.java) }
|
response.snapshot.children.map { it.getValue(T::class.java) }
|
||||||
}
|
}
|
||||||
|
|
||||||
is EventResponse.Cancelled -> {
|
is EventResponse.Cancelled -> {
|
||||||
throw FirebaseException(response.error)
|
throw FirebaseException(response.error)
|
||||||
}
|
}
|
||||||
@@ -64,6 +66,7 @@ suspend fun <T: Any> DatabaseReference.getDataFromDatabaseRef(clazz : Class<T>):
|
|||||||
is EventResponse.Changed -> {
|
is EventResponse.Changed -> {
|
||||||
response.snapshot.getValue(clazz)
|
response.snapshot.getValue(clazz)
|
||||||
}
|
}
|
||||||
|
|
||||||
is EventResponse.Cancelled -> {
|
is EventResponse.Cancelled -> {
|
||||||
throw FirebaseException(response.error)
|
throw FirebaseException(response.error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import android.provider.Settings
|
|||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
|
|
||||||
|
|
||||||
object PermissionsUtils {
|
object PermissionsUtils {
|
||||||
@@ -39,12 +38,6 @@ object PermissionsUtils {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Fragment.askForPermissions(permission: String, requestCode: Int): Boolean =
|
|
||||||
requireActivity().askForPermissions(permission, requestCode)
|
|
||||||
|
|
||||||
fun isGranted(grantResults: IntArray): Boolean =
|
|
||||||
grantResults.getOrNull(0)?.equals(PERMISSION_GRANTED) ?: false
|
|
||||||
|
|
||||||
private fun Context.showPermissionDeniedDialog() {
|
private fun Context.showPermissionDeniedDialog() {
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setTitle("Permission Denied")
|
.setTitle("Permission Denied")
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
android:width="24dp"
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
android:width="24dp"
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<vector android:height="72dp" android:tint="#FFFFFF"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
android:width="72dp"
|
||||||
android:width="72dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="72dp"
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M19,3L4.99,3c-1.11,0 -1.98,0.89 -1.98,2L3,19c0,1.1 0.88,2 1.99,2L19,21c1.1,0 2,-0.9 2,-2L21,5c0,-1.11 -0.9,-2 -2,-2zM19,15h-4c0,1.66 -1.35,3 -3,3s-3,-1.34 -3,-3L4.99,15L4.99,5L19,5v10z"/>
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,3L4.99,3c-1.11,0 -1.98,0.89 -1.98,2L3,19c0,1.1 0.88,2 1.99,2L19,21c1.1,0 2,-0.9 2,-2L21,5c0,-1.11 -0.9,-2 -2,-2zM19,15h-4c0,1.66 -1.35,3 -3,3s-3,-1.34 -3,-3L4.99,15L4.99,5L19,5v10z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<vector android:height="24dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:tint="#FFFFFF"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="@android:color/white"
|
||||||
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z" />
|
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z" />
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<vector android:height="24dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:tint="#FFFFFF"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
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" />
|
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" />
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<vector android:height="24dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:tint="#FFFFFF"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
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" />
|
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" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/background_with_curve"
|
android:background="@drawable/background_with_curve"
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
android:id="@+id/appbar"
|
android:id="@+id/appbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:elevation="0dp"
|
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:theme="@style/AppTheme.AppBarOverlay">
|
android:theme="@style/AppTheme.AppBarOverlay"
|
||||||
|
app:elevation="0dp">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
|
|||||||
@@ -37,9 +37,9 @@
|
|||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/email_update"
|
android:id="@+id/email_update"
|
||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
|
android:autofillHints="emailAddress"
|
||||||
android:hint="@string/prompt_email"
|
android:hint="@string/prompt_email"
|
||||||
android:inputType="textEmailAddress"
|
android:inputType="textEmailAddress" />
|
||||||
android:autofillHints="emailAddress" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
@@ -52,9 +52,9 @@
|
|||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/password_top"
|
android:id="@+id/password_top"
|
||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
|
android:autofillHints="password"
|
||||||
android:hint="@string/prompt_password"
|
android:hint="@string/prompt_password"
|
||||||
android:inputType="textPassword"
|
android:inputType="textPassword" />
|
||||||
android:autofillHints="password" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
app:cardCornerRadius="28dp"
|
app:cardCornerRadius="28dp"
|
||||||
app:cardElevation="0dp"
|
app:cardElevation="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/til_lic_no"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent">
|
||||||
app:layout_constraintBottom_toTopOf="@id/til_lic_no">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/driversli_img"
|
android:id="@+id/driversli_img"
|
||||||
@@ -21,23 +21,22 @@
|
|||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:scaleType="centerCrop"
|
android:contentDescription="@string/image_description"
|
||||||
tools:src="@drawable/choice_img_round"
|
android:scaleType="centerCrop" />
|
||||||
android:contentDescription="@string/image_description" />
|
|
||||||
|
|
||||||
<com.mikhaellopez.circularimageview.CircularImageView
|
<com.mikhaellopez.circularimageview.CircularImageView
|
||||||
android:id="@+id/search_image"
|
android:id="@+id/search_image"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:src="@drawable/ic_baseline_photo_library_24"
|
android:layout_gravity="bottom|end"
|
||||||
android:scaleType="centerInside"
|
|
||||||
android:elevation="1dp"
|
|
||||||
app:civ_border_width="1dp"
|
|
||||||
app:civ_shadow_radius="0.5dp"
|
|
||||||
android:alpha="1"
|
|
||||||
app:civ_circle_color="@color/colour_one"
|
|
||||||
android:layout_margin="18dp"
|
android:layout_margin="18dp"
|
||||||
android:layout_gravity="bottom|end" />
|
android:alpha="1"
|
||||||
|
android:elevation="1dp"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:src="@drawable/ic_baseline_photo_library_24"
|
||||||
|
app:civ_border_width="1dp"
|
||||||
|
app:civ_circle_color="@color/colour_one"
|
||||||
|
app:civ_shadow_radius="0.5dp" />
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
@@ -53,24 +52,24 @@
|
|||||||
android:id="@+id/lic_no"
|
android:id="@+id/lic_no"
|
||||||
style="@style/EditTextStyle.Date"
|
style="@style/EditTextStyle.Date"
|
||||||
android:hint="@string/drivers_license_no"
|
android:hint="@string/drivers_license_no"
|
||||||
|
android:importantForAutofill="no"
|
||||||
android:inputType="none"
|
android:inputType="none"
|
||||||
android:maxLines="1"
|
android:maxLines="1" />
|
||||||
android:importantForAutofill="no" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/til_submission"
|
android:id="@+id/til_submission"
|
||||||
style="@style/text_input_layout"
|
style="@style/text_input_layout"
|
||||||
app:layout_constraintTop_toBottomOf="@id/til_lic_no"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent">
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/til_lic_no">
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:hint="@string/license_expiry_date"
|
|
||||||
android:id="@+id/lic_expiry"
|
android:id="@+id/lic_expiry"
|
||||||
style="@style/EditTextStyle.Date"
|
style="@style/EditTextStyle.Date"
|
||||||
android:autofillHints="date" />
|
android:autofillHints="date"
|
||||||
|
android:hint="@string/license_expiry_date" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
@@ -78,10 +77,10 @@
|
|||||||
android:id="@+id/submit"
|
android:id="@+id/submit"
|
||||||
style="@style/TextButton.WithIcon"
|
style="@style/TextButton.WithIcon"
|
||||||
android:text="@string/submit"
|
android:text="@string/submit"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/til_submission"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/til_submission"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_bias="0.8" />
|
app:layout_constraintVertical_bias="0.8" />
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,19 @@
|
|||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
style="@style/parent_constraint_layout"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginEnd="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_marginStart="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context=".ui.driverprofile.DriverProfileFragment">
|
tools:context=".ui.driverprofile.DriverProfileFragment">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
@@ -46,18 +52,17 @@
|
|||||||
tools:src="@drawable/choice_img_round" />
|
tools:src="@drawable/choice_img_round" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout style="@style/text_input_layout">
|
||||||
style="@style/text_input_layout">
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/names_input"
|
android:id="@+id/names_input"
|
||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
|
android:autofillHints="name"
|
||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:hint="@string/full_name"
|
android:hint="@string/full_name"
|
||||||
android:inputType="textPersonName"
|
android:inputType="textPersonName"
|
||||||
android:maxLines="1"
|
android:maxLines="1" />
|
||||||
android:autofillHints="name" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
@@ -67,6 +72,7 @@
|
|||||||
android:id="@+id/address_input"
|
android:id="@+id/address_input"
|
||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
|
android:autofillHints="postalAddress"
|
||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:gravity="top|start"
|
android:gravity="top|start"
|
||||||
android:hint="@string/address"
|
android:hint="@string/address"
|
||||||
@@ -75,8 +81,7 @@
|
|||||||
android:maxLines="7"
|
android:maxLines="7"
|
||||||
android:minLines="4"
|
android:minLines="4"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true" />
|
||||||
android:autofillHints="postalAddress" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
@@ -86,12 +91,12 @@
|
|||||||
android:id="@+id/postcode_input"
|
android:id="@+id/postcode_input"
|
||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
|
android:autofillHints="postalCode"
|
||||||
android:hint="@string/postcode"
|
android:hint="@string/postcode"
|
||||||
android:inputType="none"
|
android:inputType="none"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true" />
|
||||||
android:autofillHints="postalCode" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
@@ -101,8 +106,8 @@
|
|||||||
android:id="@+id/dob_input"
|
android:id="@+id/dob_input"
|
||||||
style="@style/EditTextStyle.Date"
|
style="@style/EditTextStyle.Date"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
android:hint="@string/date_of_birth"
|
android:autofillHints="date"
|
||||||
android:autofillHints="date" />
|
android:hint="@string/date_of_birth" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
@@ -113,9 +118,9 @@
|
|||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
android:hint="@string/ni_number"
|
android:hint="@string/ni_number"
|
||||||
android:maxLines="1"
|
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:inputType="none"
|
android:inputType="none"
|
||||||
|
android:maxLines="1"
|
||||||
tools:ignore="TextFields" />
|
tools:ignore="TextFields" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
@@ -126,8 +131,8 @@
|
|||||||
android:id="@+id/date_first"
|
android:id="@+id/date_first"
|
||||||
style="@style/EditTextStyle.Date"
|
style="@style/EditTextStyle.Date"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
android:hint="@string/date_first_available"
|
android:autofillHints="date"
|
||||||
android:autofillHints="date" />
|
android:hint="@string/date_first_available" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,9 @@
|
|||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/submission_et"
|
android:id="@+id/submission_et"
|
||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
|
android:autofillHints="emailAddress"
|
||||||
android:hint="@string/prompt_email"
|
android:hint="@string/prompt_email"
|
||||||
android:inputType="textEmailAddress"
|
android:inputType="textEmailAddress" />
|
||||||
android:autofillHints="emailAddress" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:src="@drawable/ic_file_download_black_24dp"
|
android:contentDescription="@string/floating_action_button"
|
||||||
android:contentDescription="@string/floating_action_button" />
|
android:src="@drawable/ic_file_download_black_24dp" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
@@ -10,12 +10,12 @@
|
|||||||
android:id="@+id/carouselView"
|
android:id="@+id/carouselView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/uploadInsurance"
|
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
app:autoPlay="false"
|
app:autoPlay="false"
|
||||||
app:fillColor="#FFFFFFFF"
|
app:fillColor="#FFFFFFFF"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/uploadInsurance"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:pageColor="#00000000"
|
app:pageColor="#00000000"
|
||||||
app:radius="6dp"
|
app:radius="6dp"
|
||||||
app:strokeColor="#FF777777"
|
app:strokeColor="#FF777777"
|
||||||
@@ -41,11 +41,11 @@
|
|||||||
android:id="@+id/insurer"
|
android:id="@+id/insurer"
|
||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
android:hint="@string/insurer"
|
android:hint="@string/insurer"
|
||||||
|
android:importantForAutofill="no"
|
||||||
android:inputType="none"
|
android:inputType="none"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true" />
|
||||||
android:importantForAutofill="no" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
@@ -61,9 +61,9 @@
|
|||||||
style="@style/EditTextStyle.Date"
|
style="@style/EditTextStyle.Date"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="date"
|
|
||||||
android:hint="@string/insurance_expiry"
|
android:hint="@string/insurance_expiry"
|
||||||
android:importantForAutofill="no" />
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="date" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
style="@style/parent_constraint_layout"
|
style="@style/parent_constraint_layout"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -13,9 +13,9 @@
|
|||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
app:cardCornerRadius="28dp"
|
app:cardCornerRadius="28dp"
|
||||||
app:cardElevation="0dp"
|
app:cardElevation="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/upload_lb"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent">
|
||||||
app:layout_constraintBottom_toTopOf="@id/upload_lb">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/log_book_img"
|
android:id="@+id/log_book_img"
|
||||||
@@ -23,47 +23,47 @@
|
|||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
|
android:contentDescription="@string/image_description"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@drawable/choice_img_round"
|
tools:src="@drawable/choice_img_round" />
|
||||||
android:contentDescription="@string/image_description" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/upload_lb"
|
android:id="@+id/upload_lb"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/til_v5c"
|
|
||||||
style="@style/TextButton.Rounded"
|
style="@style/TextButton.Rounded"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/upload_logbook" />
|
android:text="@string/upload_logbook"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/til_v5c"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/til_v5c"
|
android:id="@+id/til_v5c"
|
||||||
style="@style/text_input_layout"
|
style="@style/text_input_layout"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
app:layout_constraintBottom_toBottomOf="parent">
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/v5c_no"
|
android:id="@+id/v5c_no"
|
||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
android:hint="@string/v5c_number"
|
android:hint="@string/v5c_number"
|
||||||
android:inputType="none"
|
android:importantForAutofill="no"
|
||||||
android:importantForAutofill="no" />
|
android:inputType="none" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/submit"
|
android:id="@+id/submit"
|
||||||
style="@style/TextButton.WithIcon"
|
style="@style/TextButton.WithIcon"
|
||||||
android:text="@string/submit"
|
android:text="@string/submit"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintVertical_bias="0.8"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/til_v5c">
|
app:layout_constraintTop_toBottomOf="@+id/til_v5c"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.8">
|
||||||
|
|
||||||
</com.google.android.material.button.MaterialButton>
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
|
||||||
|
|||||||
@@ -45,9 +45,9 @@
|
|||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:autofillHints="emailAddress"
|
||||||
android:hint="@string/prompt_email"
|
android:hint="@string/prompt_email"
|
||||||
android:inputType="textEmailAddress"
|
android:inputType="textEmailAddress" />
|
||||||
android:autofillHints="emailAddress" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
@@ -63,9 +63,9 @@
|
|||||||
style="@style/EditTextStyle"
|
style="@style/EditTextStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:autofillHints="password"
|
||||||
android:hint="@string/prompt_password"
|
android:hint="@string/prompt_password"
|
||||||
android:inputType="textPassword"
|
android:inputType="textPassword" />
|
||||||
android:autofillHints="password" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
app:cardCornerRadius="28dp"
|
app:cardCornerRadius="28dp"
|
||||||
app:cardElevation="0dp"
|
app:cardElevation="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/uploadmot"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent">
|
||||||
app:layout_constraintBottom_toTopOf="@id/uploadmot">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/mot_img"
|
android:id="@+id/mot_img"
|
||||||
@@ -24,32 +24,32 @@
|
|||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
|
android:contentDescription="@string/image_description"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@drawable/choice_img_round"
|
tools:src="@drawable/choice_img_round" />
|
||||||
android:contentDescription="@string/image_description" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/uploadmot"
|
android:id="@+id/uploadmot"
|
||||||
style="@style/TextButton.Rounded"
|
style="@style/TextButton.Rounded"
|
||||||
android:text="@string/upload_mot"
|
android:text="@string/upload_mot"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/til_submission"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
app:layout_constraintBottom_toTopOf="@+id/til_submission" />
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/til_submission"
|
android:id="@+id/til_submission"
|
||||||
style="@style/text_input_layout"
|
style="@style/text_input_layout"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/mot_expiry"
|
android:id="@+id/mot_expiry"
|
||||||
style="@style/EditTextStyle.Date"
|
style="@style/EditTextStyle.Date"
|
||||||
android:hint="@string/mot_expiry_date"
|
android:autofillHints=""
|
||||||
android:autofillHints="" />
|
android:hint="@string/mot_expiry_date" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:maxWidth="350dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
|
android:maxWidth="350dp"
|
||||||
app:cardCornerRadius="28dp"
|
app:cardCornerRadius="28dp"
|
||||||
app:cardElevation="0dp"
|
app:cardElevation="0dp"
|
||||||
app:layout_constraintBottom_toTopOf="@id/uploadphlic"
|
app:layout_constraintBottom_toTopOf="@id/uploadphlic"
|
||||||
@@ -22,9 +22,9 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
|
android:contentDescription="@string/image_description"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@drawable/choice_img"
|
tools:src="@drawable/choice_img" />
|
||||||
android:contentDescription="@string/image_description" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
@@ -64,8 +64,8 @@
|
|||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/ph_expiry"
|
android:id="@+id/ph_expiry"
|
||||||
style="@style/EditTextStyle.Date"
|
style="@style/EditTextStyle.Date"
|
||||||
android:hint="@string/private_hire_license_expiry"
|
|
||||||
android:autofillHints="date"
|
android:autofillHints="date"
|
||||||
|
android:hint="@string/private_hire_license_expiry"
|
||||||
tools:text="30/12/2018" />
|
tools:text="30/12/2018" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user