Passenger transportation company (app example)
A company provides passengers transportation with their vehicles.
They require an application to keep track of boarded passengers.
Information about which passengers are currently boarded or checking last un-boarding via ginstr web UI or with ginstr app itself.
The following would be used in the business process:
- each vehicle has unique identification name and other data
- each passenger has NFC tag for check in
- currently boarded passengers for each vehicle must be possible to view in app
- for each passengers check out, trip duration will be calculated
Often companies that provide transportation have similar business process in internal information systems and in most cases database structure will be provided by them.
App data structure
For this app, the following data structure is required:
- 1. table
vehicles
– storing vehicle record data vehicle_name
–text
Dt, required and uniquevehicle_costCentre
–text
Dt – not required, not unique (usually omitted)vehicle_comments
–text
Dt- 2. table
passengers
– storing passenger record data passenger_NFC
–text
Dt, required and uniquepassenger_firstName
–text
Dtpassenger_depertmentNumber
–number
Dt, no decimal point requiredpassenger_comments
–text
Dtpassenger_deviceSerial
–number
Dt, no decimal pointpassenger_recordCreationTimestamp
–dateTime
Dt- 3. table
bookings
– storing check-in / check-out record data booking_passenger_NFC
–text
Dt, requiredbooking_userName
–text
Dtbooking_passenger_firstName
–text
Dt, requiredbooking_passenger_lastName
–text
Dt, requiredbooking_passenger_departmentNumber
–number
Dt, requiredbooking_comments
–text
Dtbooking_passenger_location
–text
Dt, requiredbooking_deviceSerial
–number
Dt, requiredbooking_tripBegin
–dateTime
Dt, requiredbooking_tripEnd
–dateTime
Dtbooking_tripDurationInHours
–dateTime
Dtbooking_vehicleName
–pointer
Dt, requiredbooking_active
–text
Dt, required
First make root app folder busTransportationReport, which is requested appId in detailed app specification.
- Inside root folder create folders:
"control" folder
This folder must contain XML file widget_en_login.xml, which is a core part for login page in app.
This file can be taken from previous app example, where only references for graphical resources for widgets (e.g. android:background="@drawable/entering_field_transparent_2.9"
) may require changes, depending on required graphics used in app.
In "control" folder we will create additional XML files, used as core support for implementing GnListView
. In general each ListView is combined from 3 parts: header, row and column.
widget_en_listview.xml
File widget_en_listview.xml XML code:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#454545" > <FrameLayout android:id="@+id/lstHeader" android:layout_width="match_parent" android:layout_height="wrap_content" > </FrameLayout> <ListView android:id="@+id/lstListView" android:layout_width="match_parent" android:layout_height="match_parent" > </ListView> </LinearLayout>
widget_en_listview_header_container.xml
File widget_en_listview_header_container.xml XML code:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:background="#555555" > </LinearLayout>
widget_en_listview_row_container.xml
File widget_en_listview_row_container.xml XML code:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > </LinearLayout>
widget_en_listview_column_string.xml
File widget_en_listview_column_string.xml XML code:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="50dp" android:orientation="vertical" > <TextView android:id="@+id/tvDisplayView" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp"/> <LinearLayout android:id="@+id/lytEditContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone"> <com.ginstr.widgets.GnEditText android:id="@+id/etCellValueChange" android:layout_width="match_parent" android:layout_height="wrap_content" gn:s_sourceType="input" gn:s_hint="EnterValue" /> <Button android:id="@+id/etCellValueChangeConfirm" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="ChangeValue"/> </LinearLayout> </LinearLayout>
customList_listview.xml
File customList_listview.xml XML code:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#00000000" > <FrameLayout android:id="@+id/lstHeader" android:layout_width="match_parent" android:layout_height="wrap_content" > </FrameLayout> <com.ginstr.widgets.internal.ExpandableHeightListView android:id="@+id/lstListView" android:divider="#33333333" android:dividerHeight="0dp" android:scrollbarSize="4dp" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
attendeeList_header.xml
File attendeeList_header.xml XML code:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:visibility="gone" > </LinearLayout>
attendeeList_row.xml
File attendeeList_row.xml XML code:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout android:layout_height="50dp" android:layout_width="match_parent"> <TextView android:id="@+id/rowId" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" /> <FrameLayout android:layout_height="match_parent" android:layout_width="match_parent" android:background="#000000"> <LinearLayout android:layout_height="match_parent" android:layout_width="match_parent" android:layout_marginBottom="1dp" android:orientation="horizontal" android:background="#555555"> <com.ginstr.widgets.GnTextView android:id="@+id/booking_passenger_firstName" android:layout_width="0dp" android:layout_weight="1" android:background="#555555" android:layout_height="match_parent" android:textSize="20sp" android:gravity="center_vertical" android:layout_marginLeft="5dp" android:layout_marginRight="0dp"/> <com.ginstr.widgets.GnTextView android:id="@+id/booking_passenger_lastName" android:layout_width="0dp" android:layout_weight="1" android:background="#555555" android:layout_height="match_parent" android:textSize="20sp" android:gravity="center_vertical" android:layout_marginLeft="5dp" android:layout_marginRight="0dp" /> </LinearLayout> </FrameLayout> </FrameLayout>
Note that in XML code above, widget id’s are same as in table bookings.
"database" folder
Database folder contains the following XML files:
- configuration.xml
- table schemes (not yet implemented)
- queries.xml
- prepared query schemes with tables, columns and keys
- variables.xml
- variable definitions used in app
configuration.xml
File configuration.xml XML code:
<configuration> <tables> <table id="vehicles" name="busTransportation_vehicles"> <columnText id="vehicle_name" name="busTransportation_vehicle_name" required="true" unique="true"/> <columnText id="vehicle_costCentre" name="busTransportation_vehicle_costCentre"/> <columnText id="vehicle_comment" name="busTransportation_vehicle_comment"/> </table> <table id="employees" name="busTransportation_employees"> <columnText id="passenger_NFC" name="busTransportation_passenger_NFC" unique="true" required="true"/> <columnText id="passenger_firstName" name="busTransportation_passenger_firstName" required="true"/> <columnNumber id="passenger_lastName" name="busTransportation_passenger_lastName" required="true"/> <columnNumber id="passenger_departmentNumber" name="busTransportation_passenger_departmentNumber" required="true"/> <columnText id="passenger_comments" name="busTransportation_passenger_comments"/> <columnText id="passenger_address" name="busTransportation_passenger_address"/> <columnNumber id="passenger_deviceSerial" name="busTransportation_passenger_deviceSerial"/> <columnDateTime id="passenger_recordCreationTimestamp" name="busTransportation_passenger_recordCreationTimestamp"/> </table> <table id="bookings" name="busTransportation_bookings"> <columnPointer id="booking_passenger_NFC" name="busTransportation_booking_passenger_NFC" required="true"/> <columnText id="booking_userName" name="busTransportation_booking_userName" required="true"/> <columnText id="booking_passenger_firstName" name="busTransportation_booking_passenger_firstName" required="true"/> <columnText id="booking_passenger_lastName" name="busTransportation_booking_passenger_lastName" required="true"/> <columnNumber id="booking_passenger_departmentNumber" name="busTransportation_booking_passenger_departmentNumber" required="true"/> <columnText id="booking_comments" name="busTransportation_booking_comments"/> <columnText id="booking_passenger_location" name="busTransportation_booking_passenger_location" required="true"/> <columnDateTime id="booking_tripBegin" name="busTransportation_booking_tripBegin"/> <columnDateTime id="booking_tripEnd" name="busTransportation_booking_tripEnd"/> <columnHours id="booking_tripDurationInHours" name="busTransportation_booking_tripDurationInHours"/> <columnPointer id="booking_vehicleName" name="busTransportation_booking_vehicleName"/> <columnText id="booking_active" name="busTransportation_booking_active"/> <columnNumber id="booking_deviceSerial" name="busTransportation_booking_deviceSerial"/> </table> </tables> </configuration>
queries.xml
This XML file is used for querying data in app. Each of those actions for interacting with database must have a defined prepared query which relates to table definition.
XML code below will be used for this example app:
<?xml version="1.0" encoding="utf-8"?> <queries> <query id="vehicles" columns="vehicle_name, vehicle_costCentre, vehicle_comments" /> <query id="passengers" columns="passenger_NFC, passenger_firstName, passenger_lastName, passenger_departmentNumber, passenger_comments, passenger_address, passenger_deviceSerial, passenger_recordCreationTimestamp" keys="passenger_NFC" /> <query id="bookings" columns="booking_passenger_NFC, booking_userName, booking_passenger_firstName, booking_passenger_lastName, booking_passenger_departmentNumber, booking_comments, booking_passenger_location, booking_deviceSerial, booking_tripBegin, booking_tripEnd, booking_tripDurationInHours, booking_vehicleName, booking_active" keys="booking_passenger_NFC,booking_vehicleName,booking_active" /> <query id="bookingsByVehicleName" columns="booking_passenger_NFC, booking_userName, booking_passenger_firstName, booking_passenger_lastName, booking_passenger_departmentNumber, booking_comments, booking_passenger_location, booking_deviceSerial, booking_tripBegin, booking_tripEnd, booking_tripDurationInHours, booking_vehicleName, booking_active" keys="booking_vehicleName,booking_active" /> </queries>
The XML code above differs from queries.xml in previous app example in that some queries have defined keys.
It’s important to note that when query
is referencing some table, all columns must match with complete table.
variables.xml
This XML file contains definitions of variables used in app for storing values of widgets, calculation results or other helper task for implementing business logic. Each variable has id (needed for referencing in actions) and type.
Example of XML code:
<?xml version="1.0" encoding="utf-8"?> <variables> <variable id="firstName" type="text"></variable> </variable>
In the XML code above, we have only one variable with id (variable name) firstName
which is of data type text
. Other data types are number
, date
, etc.
More information about variables can be found here.
"values" folder
We can now introduce complete strings.xml files in both value and values-en folders. During development those files would be gradually made with implementation of app business logic.
It is important to create strings in logical sections and intuitive naming, because string names are directly connected to XML files in layout, while string values can be later adapted.
The bottom parts of strings.xml regarding user friendly table and column names in development will be taken from configuration.xml and adapted, while string values will be provided by other developers or customer.
"values" folder contains German language string resources.
strings.xml
Content of strings.xml in values folder:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="appName">Bus Transporte</string> <string name="appNameShortcut">Bus Transporte</string> <string name="in_settingsAppName">Bus Transporte</string> <!-- options menu --> <string name="txtBookingOptions">Kommen / Gehen</string> <string name="txtAssignTagOptions">Ausweis ausgeben</string> <string name="txtAttendanceOptions">Anwesenheit</string> <string name="txtNewVehicleOptions">Fahrzeug anlegen</string> <!-- new vehicle screen --> <string name="txtNewVehicleHeading">Fahrzeug anlegen</string> <string name="txtNewVehicleName">Fahrzeugbezeichnung*</string> <string name="txtNewVehicleNameHint">Bitte hier die Fahrzeugbezeichnung eingeben</string> <string name="txtNewVehicleNameRequired">Die Fahrzeugbezeichnung muss eingegeben werden</string> <string name="txtNewVehicleCostCentre">Kostenstelle*</string> <string name="txtNewVehicleCostCentreHint">Bitte hier die Kostenstelle eingeben</string> <string name="txtNewVehicleCostCentreRequired">Die Kostenstelle muss eingegeben werden</string> <string name="txtNewVehicleComment">Bemerkungen</string> <string name="txtNewVehicleCommentHint">Bitte Bemerkungen hier eingeben</string> <string name="txtNewVehicleSave">Fahrzeug-Stammdaten speichern</string> <string name="toastNewVehicleSaved">Die Fahrzeug-Stammdaten wurden erfolgreich gespeichert</string> <!-- assign passenger id card screen --> <string name="txtAssignTagHeading">Fahrgastausweis ausgeben</string> <string name="txtAssignTagScanPassengerTag">Fahrgastausweis Nummer*</string> <string name="txtAssignTagScanPassengerTagHint">Bitte den Fahrgastausweis einlesen</string> <string name="txtAssignTagScanPassengerTagRequired">Der Fahrgastausweis muss eingelesen werden</string> <string name="txtAssignTagFirstName">Vorname*</string> <string name="txtAssignTagFirstNameHint">Bitte hier den Vornamen eingeben</string> <string name="txtAssignTagFirstNameRequired">Der Vorname muss eingegeben werden</string> <string name="txtAssignTagLastName">Nachname*</string> <string name="txtAssignTagLastNameHint">Bitte hier den Nachnamen eingeben</string> <string name="txtAssignTagLastNameRequired">Der Nachname muss eingegeben werden</string> <string name="txtAssignTagDepartmentNumber">Abteilung*</string> <string name="txtAssignTagDepartmentNumberHint">Bitte hier die Abt.Nr. eingeben</string> <string name="txtAssignTagDepartmentNumberRequired">Die Abteilungsnummer muss eingegeben werden</string> <string name="txtAssignTagComment">Bemerkungen</string> <string name="txtAssignTagCommentHint">Bitte Bemerkungen hier eingeben</string> <string name="txtAssignTagAddress">Adresse*</string> <string name="txtAssignTagAddressHint">Bitte hier die Adresse des Fahrgastes eingeben</string> <string name="txtAssignTagAddressRequired">Die Adresse des Fahrgastes ist erforderlich</string> <string name="toastNfcPassengerIdCardIsUnknown">Dieser Fahrgastausweis ist unbekannt</string> <string name="toastNfcPassengerExists">Dieser Fahrgastausweis ist bereits einem Fahrgast zugeordnet</string> <string name="txtAssignTagSave">Fahrgastausweis speichern</string> <string name="toastAssignTagSaved">Der Fahrgastausweis wurde erfolgreich gespeichert</string> <string name="txtAssignTagUpdate">Fahrgastausweis aktualisieren</string> <string name="toastAssignTagUpdated">Der Fahrgastausweis wurde erfolgreich aktualisiert</string> <!-- booking screen --> <string name="txtBookingHeading">Kommen / Gehen</string> <string name="txtBookingSelectVehicle">Fahrzeug auswählen*</string> <string name="txtBookingSelectVehicleRequired">Ein Fahrzeug muss ausgewählt werden</string> <string name="txtBookingScanPassengerTag">Fahrgastausweis*</string> <string name="txtBookingScanPassengerTagHint">Bitte den Fahrgastausweis einlesen</string> <string name="txtBookingTagScanPassengerTagRequired">Der Fahrgastausweis muss eingelesen werden</string> <string name="txtBookingFirstName">Vorname*</string> <string name="txtBookingFirstNameHint">Bitte hier den Vornamen eingeben</string> <string name="txtBookingFirstNameRequired">Der Vorname muss eingegeben werden</string> <string name="txtBookingLastName">Nachname*</string> <string name="txtBookingLastNameHint">Bitte hier den Nachnamen eingeben</string> <string name="txtBookingLastNameRequired">Der Nachname muss eingegeben werden</string> <string name="txtBookingDepartmentNumber">Abteilung*</string> <string name="txtBookingDepartmentNumberHint">Bitte die Abteilungsnummer eingeben</string> <string name="txtBookingDepartmentNumberRequired">Die Eingabe der Abteilungsnummer ist erforderlich</string> <string name="txtBookingComment">Bemerkungen</string> <string name="txtBookingCommentHint">Bitte Bemerkungen hier eingeben</string> <string name="txtBookingAddress">Adresse*</string> <string name="txtBookingAddressHint">Bitte die Adresse hier eingeben</string> <string name="txtBookingAddressRequired">Die Eingabe der Adresse ist erforderlich</string> <string name="txtBookingStateStart">Ausweis einlesen</string> <string name="toastBookingInSaved">Der Einstieg des Fahrgasts wurde erfolgreich gespeichert</string> <string name="toastBookingOutSaved">Der Ausstieg des Fahrgasts wurde erfolgreich gespeichert</string> <string name="txtBookingStateCheckIn">Der Fahrgast ist im Fahrzeug</string> <string name="txtBookingStateCheckOut">Der Fahrgast hat das Fahrzeug verlassen</string> <string name="toastBookingNfcPassengerIdCardIsUnknown">Dieser Fahrgastausweis ist bereits einem anderen Fahrgast zugeordnet</string> <!-- attendence screen --> <string name="txtAttendanceHeading">Fahrgäste im Fahrzeug</string> <string name="txtAttendanceSelectVehicle">Fahrzeug auswählen*</string> <string name="txtAttendancePassengerFirstName">Vorname</string> <string name="txtAttendancePassengerLastName">Nachname</string> <string name="validateAllFields">Es wurden nicht alle Pflichtfelder ausgefüllt</string> <!-- table and column names --> <string name="busTransportation_vehicles">Fahrzeuge</string> <!-- table name --> <string name="busTransportation_vehicle_name">Fahrzeug</string> <!-- column name --> <string name="busTransportation_vehicle_costCentre">Kostenstelle</string> <!-- column name --> <string name="busTransportation_vehicle_comments">Bemerkungen</string> <!-- column name --> <string name="busTransportation_passengers">Fahrgäste</string> <!-- table name --> <string name="busTransportation_passenger_NFC">Fahrgastausweis Nr.</string> <!-- column name --> <string name="busTransportation_passenger_firstName">Vorname</string> <!-- column name --> <string name="busTransportation_passenger_lastName">Nachname</string> <!-- column name --> <string name="busTransportation_passenger_departmentNumber">Abteilung</string> <!-- column name --> <string name="busTransportation_passenger_comments">Bemerkungen</string> <!-- column name --> <string name="busTransportation_passenger_address">Adresse</string> <!-- column name --> <string name="busTransportation_passenger_deviceSerial">Seriennummer des Geräts mit dem der Datensatz angelegt wurde</string> <!-- column name --> <string name="busTransportation_passenger_recordCreationTimestamp">Datum und Uhrzeit wann der Datensatz angelegt wurde</string> <!-- column name --> <string name="busTransportation_bookings">Fahrten</string> <!-- table name --> <string name="busTransportation_booking_passenger_NFC">Fahrgastausweis Nr.</string> <!-- column name --> <string name="busTransportation_booking_userName">Benutzername</string> <!-- column name --> <string name="busTransportation_booking_passenger_firstName">Vorname</string> <!-- column name --> <string name="busTransportation_booking_passenger_lastName">Nachname</string> <!-- column name --> <string name="busTransportation_booking_passenger_departmentNumber">Abteilung</string> <!-- column name --> <string name="busTransportation_booking_comments">Bemerkungen</string> <!-- column name --> <string name="busTransportation_booking_passenger_location">Adresse</string> <!-- column name --> <string name="busTransportation_booking_deviceSerial">Seriennummer des Geräts mit dem der Datensatz angelegt wurde</string> <!-- column name --> <string name="busTransportation_booking_tripBegin">Fahrtbeginn</string> <!-- column name --> <string name="busTransportation_booking_tripEnd">Fahrtende</string> <!-- column name --> <string name="busTransportation_booking_tripDurationInHours">Fahrtdauer</string> <!-- column name --> <string name="busTransportation_booking_vehicleName">Fahrzeug</string> <!-- column name --> <string name="busTransportation_booking_active">Fahrgast ist derzeit im Bus</string> <!-- column name --> </resources>
"values-en"_folder
"values-en" folder contains English language string resources.
strings.xml
Content of strings.xml in values-en folder:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="appName">bus transportation report</string> <string name="appNameShortcut">bus transportation report</string> <string name="in_settingsAppName">bus transportation report</string> <!-- options menu --> <string name="txtBookingOptions">enter / leave</string> <string name="txtAssignTagOptions">assign passenger id</string> <string name="txtAttendanceOptions">passengers per vehicle</string> <string name="txtNewVehicleOptions">add vehicle</string> <!-- new vehicle screen --> <string name="txtNewVehicleHeading">add new vehicle</string> <string name="txtNewVehicleName">vehicle*</string> <string name="txtNewVehicleNameHint">please enter the vehicle name here</string> <string name="txtNewVehicleNameRequired">a vehicle name is required</string> <string name="txtNewVehicleCostCentre">cost centre*</string> <string name="txtNewVehicleCostCentreHint">please enter the cost centre here</string> <string name="txtNewVehicleCostCentreRequired">entering the cost centre is required</string> <string name="txtNewVehicleComment">comments</string> <string name="txtNewVehicleCommentHint">please enter any comments here</string> <string name="txtNewVehicleSave">save vehicle master data</string> <string name="toastNewVehicleSaved">vehicle master data was saved successfully</string> <!-- assign passenger id card screen --> <string name="txtAssignTagHeading">assign passenger id card</string> <string name="txtAssignTagScanPassengerTag">passenger id card*</string> <string name="txtAssignTagScanPassengerTagHint">please scan the passenger id card</string> <string name="txtAssignTagScanPassengerTagRequired">scanning the passenger id card is required</string> <string name="txtAssignTagFirstName">passenger first name*</string> <string name="txtAssignTagFirstNameHint">please enter the passenger first name here</string> <string name="txtAssignTagFirstNameRequired">entering the passenger first name is required</string> <string name="txtAssignTagLastName">last name*</string> <string name="txtAssignTagLastNameHint">please enter the passenger last name here</string> <string name="txtAssignTagLastNameRequired">entering the passenger last name is required</string> <string name="txtAssignTagDepartmentNumber">department number*</string> <string name="txtAssignTagDepartmentNumberHint">please enter the department number</string> <string name="txtAssignTagDepartmentNumberRequired">entering the department number is required</string> <string name="txtAssignTagComment">comments</string> <string name="txtAssignTagCommentHint">please enter any comments here</string> <string name="txtAssignTagAddress">address*</string> <string name="txtAssignTagAddressHint">please enter the address of the passenger here</string> <string name="txtAssignTagAddressRequired">location information is required</string> <string name="toastNfcPassengerIdCardIsUnknown">this passenger id card is not yet assigned to a passenger</string> <string name="toastNfcPassengerExists">this passenger id card is already assigned to another passenger</string> <string name="txtAssignTagSave">save passenger id card data</string> <string name="toastAssignTagSaved">passenger card has been saved successfully</string> <string name="txtAssignTagUpdate">update passenger id card data</string> <string name="toastAssignTagUpdated">passenger card has been saved successfully updated</string> <!-- booking screen --> <string name="txtBookingHeading">enter / exit</string> <string name="txtBookingSelectVehicle">please select a vehicle*</string> <string name="txtBookingSelectVehicleRequired">selecting a vehicle is required</string> <string name="txtBookingScanPassengerTag">passenger id card*</string> <string name="txtBookingScanPassengerTagHint">please scan the passenger id card</string> <string name="txtBookingTagScanPassengerTagRequired">scanning the passenger id card is required</string> <string name="txtBookingFirstName">first name*</string> <string name="txtBookingFirstNameHint">please enter the passenger first name here</string> <string name="txtBookingFirstNameRequired">entering the passenger first name is required</string> <string name="txtBookingLastName">last name*</string> <string name="txtBookingLastNameHint">please enter the passenger last name here</string> <string name="txtBookingLastNameRequired">entering the passenger last name is required</string> <string name="txtBookingDepartmentNumber">department number*</string> <string name="txtBookingDepartmentNumberHint">please enter the department number here</string> <string name="txtBookingDepartmentNumberRequired">entering the department number is required</string> <string name="txtBookingComment">comments</string> <string name="txtBookingCommentHint">please enter any comments here</string> <string name="txtBookingAddress">address*</string> <string name="txtBookingAddressHint">please enter the address of the passenger here</string> <string name="txtBookingAddressRequired">entering the address of the passenger is required</string> <string name="txtBookingStateStart">scan passenger id card</string> <string name="toastBookingInSaved">pickup of the passenger has been saved successfully</string> <string name="toastBookingOutSaved">exiting of the passenger has been saved successfully</string> <string name="txtBookingStateCheckIn">on board</string> <string name="txtBookingStateCheckOut">gone</string> <string name="toastBookingNfcPassengerIdCardIsUnknown">this passenger id card is unknown</string> <!-- attendence screen --> <string name="txtAttendanceHeading">passengers per vehicle</string> <string name="txtAttendanceSelectVehicle">please select a vehicle*</string> <string name="txtAttendancePassengerFirstName">passenger first name</string> <string name="txtAttendancePassengerLastName">passenger last name</string> <string name="validateAllFields">please fill in all obligatory fields</string> <!-- table and column names --> <string name="busTransportation_vehicles">vehicles</string> <!-- table name --> <string name="busTransportation_vehicle_name">vehicle name</string> <!-- column name --> <string name="busTransportation_vehicle_costCentre">cost centre</string> <!-- column name --> <string name="busTransportation_vehicle_comments">comments</string> <!-- column name --> <string name="busTransportation_passengers">passengers</string> <!-- table name --> <string name="busTransportation_passenger_NFC">passenger id</string> <!-- column name --> <string name="busTransportation_passenger_firstName">first name</string> <!-- column name --> <string name="busTransportation_passenger_lastName">last name</string> <!-- column name --> <string name="busTransportation_passenger_departmentNumber">department number</string> <!-- column name --> <string name="busTransportation_passenger_comments">comments</string> <!-- column name --> <string name="busTransportation_passenger_address">address</string> <!-- column name --> <string name="busTransportation_passenger_deviceSerial">serial number of the device used for creating this record</string> <!-- column name --> <string name="busTransportation_passenger_recordCreationTimestamp">date of time of creating this record</string> <!-- column name --> <string name="busTransportation_bookings">trips</string> <!-- table name --> <string name="busTransportation_booking_passenger_NFC">passenger id</string> <!-- column name --> <string name="busTransportation_booking_userName">user name</string> <!-- column name --> <string name="busTransportation_booking_passenger_firstName">first name</string> <!-- column name --> <string name="busTransportation_booking_passenger_lastName">last name</string> <!-- column name --> <string name="busTransportation_booking_passenger_departmentNumber">department</string> <!-- column name --> <string name="busTransportation_booking_comments">comments</string> <!-- column name --> <string name="busTransportation_booking_passenger_location">location</string> <!-- column name --> <string name="busTransportation_booking_deviceSerial">serial number of the device used to create this record</string> <!-- column name --> <string name="busTransportation_booking_tripBegin">trip begin</string> <!-- column name --> <string name="busTransportation_booking_tripEnd">trip end</string> <!-- column name --> <string name="busTransportation_booking_tripDurationInHours">trip duration (hours)</string> <!-- column name --> <string name="busTransportation_booking_vehicleName">vehicle</string> <!-- column name --> <string name="busTransportation_booking_active">passenger is currently in the vehicle</string> <!-- column name --> </resources>
"layout" folder
Since this app example is using 3 tables, 3 separate XML files will be created in layout folder. Note naming scheme for those files.
vehiclesTABLE.xml
Paste XML code below into file vehiclesTABLE.xml which will create table vehicles
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:gn="http://schemas.ginstr.com/ginstr" android:id="@+id/busSitesTABLE" android:layout_width="match_parent" android:layout_height="match_parent"> <com.ginstr.widgets.GnEditText android:id="@+id/vehicle_name" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="vehicle_name" gn:data_request="vehicles" gn:s_sourceType="input"/> <com.ginstr.widgets.GnEditText android:id="@+id/vehicle_costCentre" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="vehicle_costCentre" gn:data_request="vehicles" gn:s_sourceType="input"/> <com.ginstr.widgets.GnEditText android:id="@+id/vehicle_comments" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="vehicle_comments" gn:data_request="vehicles" gn:s_sourceType="input"/> </LinearLayout>
passengersTABLE.xml
Paste XML code below into file passengersTABLE.xml which will create table passengers
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:gn="http://schemas.ginstr.com/ginstr" android:id="@+id/passengersTABLE" android:layout_width="match_parent" android:layout_height="match_parent"> <com.ginstr.widgets.GnEditText android:id="@+id/passenger_NFC" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="passenger_NFC" gn:data_request="passengers" gn:s_sourceType="nfc" /> <com.ginstr.widgets.GnEditText android:id="@+id/passenger_firstName" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="passenger_firstName" gn:data_request="passengers" gn:s_sourceType="input" /> <com.ginstr.widgets.GnEditText android:id="@+id/passenger_lastName" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="passenger_lastName" gn:data_request="passengers" gn:s_sourceType="input" /> <com.ginstr.widgets.GnEditText android:id="@+id/passenger_departmentNumber" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="passenger_departmentNumber" gn:data_request="passengers" gn:dataType="number" gn:s_sourceType="input" gn:s_decimalFormat="#" /> <com.ginstr.widgets.GnEditText android:id="@+id/passenger_comments" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="passenger_comments" gn:data_request="passengers" gn:s_sourceType="input"/> <com.ginstr.widgets.GnEditText android:id="@+id/passenger_address" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="passenger_address" gn:data_request="passengers" gn:s_sourceType="gps" gn:showAddress="true" /> <com.ginstr.widgets.GnEditText android:id="@+id/passenger_deviceSerial" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:data_field="passenger_deviceSerial" gn:data_request="passengers" gn:inputType="NUMBER" gn:dataType="number" gn:s_decimalFormat="#" gn:s_sourceType="input" /> <com.ginstr.widgets.GnEditText android:id="@+id/passenger_recordCreationTimestamp" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:data_field="passenger_recordCreationTimestamp" gn:data_request="passengers" gn:s_sourceType="time"/> </LinearLayout>
bookingsTABLE.xml
Paste XML code below into file bookingsTABLE.xml which will create table bookings
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:gn="http://schemas.ginstr.com/ginstr" android:id="@+id/bookingsTABLE" android:layout_width="match_parent" android:layout_height="match_parent" > <com.ginstr.widgets.GnDropDown android:id="@+id/booking_vehicleName" android:layout_width="fill_parent" android:layout_height="wrap_content" gn:singleItemMode="true" gn:data_field="booking_vehicleName" gn:data_request="bookings" gn:s_sourceType="database" gn:data_source_request="vehicles" gn:data_source_field="vehicle_name" gn:act_beforeLoad="[gn:act_fill_dropdown]|[@+id/booking_vehicleName]" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_passenger_NFC" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="booking_passenger_NFC" gn:data_request="bookings" gn:s_sourceType="nfc" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_passenger_firstName" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="booking_passenger_firstName" gn:data_request="bookings" gn:s_sourceType="input" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_passenger_lastName" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="booking_passenger_lastName" gn:data_request="bookings" gn:s_sourceType="input" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_passenger_departmentNumber" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="booking_passenger_departmentNumber" gn:data_request="bookings" gn:inputType="NUMBER" gn:dataType="number" gn:s_decimalFormat="#" gn:s_sourceType="input" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_comments" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="booking_comments" gn:data_request="bookings" gn:s_sourceType="input" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_passenger_location" android:layout_width="match_parent" android:layout_height="wrap_content" gn:data_field="booking_passenger_location" gn:data_request="bookings" gn:s_sourceType="gps" gn:showAddress="true"/> <com.ginstr.widgets.GnEditText android:id="@+id/booking_deviceSerial" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:data_field="booking_deviceSerial" gn:data_request="bookings" gn:inputType="NUMBER" gn:dataType="number" gn:s_decimalFormat="#" gn:s_sourceType="input" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_userName" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:data_field="booking_userName" gn:data_request="bookings" gn:s_sourceType="aguser" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_tripBegin" android:layout_width="200dp" android:layout_height="wrap_content" gn:data_field="booking_tripBegin" gn:data_request="bookings" gn:s_sourceType="time" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_tripEnd" android:layout_width="200dp" android:layout_height="wrap_content" gn:data_field="booking_tripEnd" gn:data_request="bookings" gn:s_sourceType="time" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_tripDurationInHours" android:layout_width="200dp" android:layout_height="wrap_content" gn:data_field="booking_tripDurationInHours" gn:data_request="bookings" gn:s_sourceType="input" gn:dataType="number" gn:s_decimalFormat="#" /> <com.ginstr.widgets.GnEditText android:id="@+id/booking_active" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:data_field="booking_active" gn:data_request="bookings" gn:visible="false" gn:s_sourceType="input"/> </LinearLayout>
Note GnDropDown
widget @+id/booking_vehicleName
which will create pointer data type column. GnDropDown
widget can use local resources from arrays.xml or from database, referencing column. This will then ensure that source column value will be changed in pointer column.
Dropdowns and pointers can be challenging in app development due to their versatile usage. When using ginstr web UI for entering values in columns, pointer column will be offered to select related data and not entered manually.
start.xml
File start.xml for login screen has the following XML code:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:gn="http://schemas.ginstr.com/ginstr" android:id="@+id/start" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" gn:act_beforeLoad="[gn:act_setWinBgd]|[@drawable/background]" gn:act_afterLoad="[gn:act_lockScreenOrientation]|[portrait]"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <com.ginstr.widgets.GnPreferencesButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:background="@drawable/settings" gn:setBackgroundPressed="@drawable/settings_pressed" /> <ImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:src="@drawable/app_icon"/> <TextView android:id="@+id/txtAppName" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/icon" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_marginTop="20dp" android:layout_marginBottom="10dp" android:gravity="center_horizontal" android:text="@string/appName" android:textColor="#FFFFFF" android:textSize="30sp" android:typeface="sans" /> <com.ginstr.widgets.GnLogin android:id="@+id/btnLogin" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/txtAppName" android:layout_centerHorizontal="true" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" gn:act_toLayoutLogin="@+id/options" /> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/btnLogin"> <ImageView android:id="@+id/footer" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:src="@drawable/background_i"/> </RelativeLayout> </RelativeLayout> </ScrollView>
Comparing the above code to start.xml for previous app, we notice that very few things are changed other than colors and margin sizes.
options.xml
The first root layout after login will be @+id/options
.
XML code for options.xml is as follows:
<?xml version="1.0" encoding="UTF-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:gn="http://schemas.ginstr.com/ginstr" android:id="@+id/options" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/background" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginBottom="40dp" android:gravity="right" android:background="#44000000"> <com.ginstr.widgets.GnPreferencesButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/settings" gn:setBackgroundPressed="@drawable/settings_pressed"/> <Button android:id="@+id/bnt_logout" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:act_goLayoutBack="@+id/start" android:background="@drawable/icon_logout" gn:setBackgroundPressed="@drawable/icon_logout_pressed"/> </LinearLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/myImageView" android:padding="20dp" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:src="@drawable/button_half.9" gn:act_toLayoutClick="@+id/bookings" gn:setBackgroundPressed="@drawable/button_half_pressed.9" android:scaleType="fitXY"/> <TextView android:id="@+id/myImageViewText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="3dp" android:layout_centerInParent="true" android:text="@string/txtBookingOptions" android:textColor="#000000" android:textSize="20sp"/> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/myImageView" android:padding="20dp" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:src="@drawable/button_half.9" gn:setBackgroundPressed="@drawable/button_half_pressed.9" gn:act_toLayoutClick="@+id/manageTag" android:scaleType="fitXY"/> <TextView android:id="@+id/myImageViewText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="3dp" android:layout_centerInParent="true" android:text="@string/txtAssignTagOptions" android:textColor="#000000" android:textSize="20sp"/> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/myImageView" android:padding="20dp" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:src="@drawable/button_half.9" gn:setBackgroundPressed="@drawable/button_half_pressed.9" gn:act_toLayoutClick="@+id/site" android:scaleType="fitXY"/> <TextView android:id="@+id/myImageViewText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="3dp" android:layout_centerInParent="true" android:text="@string/txtAttendanceOptions" android:textColor="#000000" android:textSize="20sp"/> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/myImageView" android:padding="20dp" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:src="@drawable/button_half.9" gn:setBackgroundPressed="@drawable/button_half_pressed.9" gn:act_toLayoutClick="@+id/newSite" android:scaleType="fitXY"/> <TextView android:id="@+id/myImageViewText4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="3dp" android:layout_centerInParent="true" android:text="@string/txtNewVehicleOptions" android:textColor="#000000" android:textSize="20sp"/> </RelativeLayout> </LinearLayout> </ScrollView>
On Screenshot 1 (shown right) we can see that the options menu consists of 2 buttons in top right corner (used for entering into preferences screen and logging out) and 4 buttons to enter different app pages (screens). In XML code, each button is made with combination of ImageView
and TextView
widgets, where ImageView
has attached click event gn:act_toLayoutClick
with id of root layout page which will be loaded when click action occurs.
Options menus are often used in apps and their XML may differ in some points, for example a simple button can be made using only widget Button
. It is only important that visual appearance of app screen is consistent with ginstr apps design rules. In some cases, icon in top right corner will be replaced with buttons below others regarding app screens.
It is important to note that every options menu must have icon/button for entering into preferences and for logging out.
newsite.xml
Clicking on button “add vehicle” opens a screen with headline “add new vehicle”.
The XML file for this screen is named newsite.xml with root layout id @+id/newSite
.
XML code for newsite.xml is as follows:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:gn="http://schemas.ginstr.com/ginstr" android:id="@+id/newSite" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/background" gn:act_setBack="[gn:act_cleanWidgets]|[]"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="@string/txtNewVehicleHeading" android:textSize="35sp" android:textColor="#ffffff" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginBottom="5dp" android:text="@string/txtNewVehicleName" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/newSiteNameDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" gn:act_validate="[name:RequiredValidator],[message=@string/txtNewVehicleNameRequired]" android:background="@drawable/entering_field_transparent_2.9" android:textSize="20sp" android:textColor="#000000" android:textColorHint="#FFFFFF" gn:s_hint="@string/txtNewVehicleNameHint" gn:s_sourceType="input"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtNewVehicleCostCentre" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/siteCostCentreDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" gn:act_validate="[name:RequiredValidator],[message=@string/txtNewVehicleCostCentreRequired" android:background="@drawable/entering_field_transparent_2.9" android:textSize="20sp" android:textColor="#000000" android:textColorHint="#FFFFFF" gn:s_hint="@string/txtNewVehicleCostCentreHint" gn:s_sourceType="input"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtNewVehicleComment" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/commentDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" android:background="@drawable/entering_field_transparent_2.9" android:textSize="20sp" android:textColor="#000000" android:textColorHint="#FFFFFF" gn:s_hint="@string/txtNewVehicleCommentHint" gn:s_sourceType="input"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:textSize="20sp" android:background="@drawable/button_half.9" gn:setBackgroundPressed="@drawable/button_half_pressed.9" android:textColor="#000000" android:text="@string/txtNewVehicleSave" gn:act_setClick="[gn:act_if]|[[gn:act_hasTableRights]|[vehicles!=C]--[gn:act_toast]|[@string/txtNoWriteRights]; [gn:act_break]|[]::], [gn:act_if]|[[gn:act_validateScreen]|[]--[gn:act_rawWriteValues]|[vehicles; vehicle_name, vehicle_costCentre, vehicle_comments; @+id/newSiteNameDATA, @+id/siteCostCentreDATA, @+id/commentDATA]; [gn:act_cleanWidgets]|[]; [gn:act_toast]|[@string/toastNewVehicleSaved]; [gn:act_goLayoutBack]|[@+id/options]::[gn:act_toast]|[@string/validateAllFields]]" /> </LinearLayout> </RelativeLayout> </ScrollView>
Root layout has attached event for cleaning all widgets when leaving this screen, because we do not want to have widgets filled with previously entered data. The rest of the XML consists of input boxes, each with labels above and a button for saving entered data.
Validators are attached to widgets which must be filled. When clicking on button, logic for checking validators and saving data is executed. Before any data is interacted with database, currently logged in user will be checked for user rights for given table. Each account can be configured for CRUD (create-read-update-delete) rights.
In our XML code, user is checked for create rights to table vehicles (statement can be translated as “check if user does not have rights to create new record in table vehicles”). If action returns true
, toast information message will be displayed to user and further action execution will be stopped using gn:act_break
. If statement is false
, meaning that user has create rights then further actions will follow.
First action before saving data is validating screen which checks each validator attached to widget. If validators are passed then saving data takes place. To provide user feedback, we toast information message that data is saved after which all widgets are cleared and app goes back to the options menu. With this last statement page business logic is concluded. Saved data can be checked in ginstr web UI, searching first app name and then opening table in portlet. New record should be visible.
Note that for going back to previous page must be always used [gn:act_goLayoutBack]|[1|@+id/targetLayout]
and not [gn:act_toLayoutClick]|[@+id/targetLayout]
. gn:act_toLayoutClick
should only be used to go to next pages.
tag.xml
For entering new NFC tags, which will be used by passengers as id cards, we will create tag.xml
XML code for tag.xml is as follows:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:gn="http://schemas.ginstr.com/ginstr" android:id="@+id/manageTag" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/background" gn:act_beforeLoad="[gn:act_hideWidgets]|[@+id/btnAssignTagUpdate],[gn:act_showWidgets]|[@+id/btnAssignTagSave]" gn:act_setBack="[gn:act_cleanWidgets]|[]"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" > <!-- save passenger data --> <FrameLayout android:id="@+id/savePassenger" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" gn:act_set="[gn:act_if]|[[gn:act_hasTableRights]|[passengers!=C]--[gn:act_toast]|[@string/txtNoWriteRights]; [gn:act_break]|[]::], [gn:act_reloadAutoFilledWidgets]|[@+id/timestampDATA,@+id/deviceSerial], [gn:act_copyData]|[@+id/deviceSerial, @+id/deviceSerialDATA], [gn:act_if]|[[gn:act_validateScreen]|[]--[gn:act_rawWriteValues]|[passengers; passenger_NFC, passenger_firstName, passenger_lastName, passenger_departmentNumber, passenger_comments, passenger_address, passenger_deviceSerial, passenger_recordCreationTimestamp; @+id/nfcPassengerTagDATA, @+id/namePassengerDATA, @+id/surnamePassengerDATA, @+id/personnelNumPassengerDATA, @+id/personnelCommentDATA, @+id/gpsLocationDATA, @+id/deviceSerialDATA, @+id/timestampDATA]; [gn:act_hideKeyboard]|[]; [gn:act_cleanWidgets]|[]; [gn:act_toast]|[@string/toastAssignTagSaved]; [gn:act_reloadAutoFilledWidgets]|[@+id/timestampDATA, @+id/deviceSerial]; [gn:act_trigger]|[@+id/showSaveButton, gn:act_set]::[gn:act_toast]|[@string/validateAllFields]]" /> <!-- update passenger data --> <FrameLayout android:id="@+id/updatePassenger" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" gn:act_set="[gn:act_if]|[[gn:act_hasTableRights]|[passengers!=U]--[gn:act_toast]|[@string/txtNoUpdateRights]; [gn:act_break]|[]::], [gn:act_reloadAutoFilledWidgets]|[@+id/timestampDATA, @+id/deviceSerial], [gn:act_copyData]|[@+id/deviceSerial, @+id/deviceSerialDATA], [gn:act_if]|[[gn:act_validateScreen]|[]--[gn:act_rawUpdateValues]|[passengers, @+id/passengerUpdatePointer; passenger_NFC, passenger_firstName, passenger_lastName, passenger_departmentNumber, passenger_comments, passenger_address, passenger_deviceSerial, passenger_recordCreationTimestamp; @+id/nfcPassengerTagDATA, @+id/namePassengerDATA, @+id/surnamePassengerDATA, @+id/personnelNumPassengerDATA, @+id/personnelCommentDATA, @+id/gpsLocationDATA, @+id/deviceSerialDATA, @+id/timestampDATA]; [gn:act_hideKeyboard]|[]; [gn:act_cleanWidgets]|[]; [gn:act_toast]|[@string/toastAssignTagUpdated]; [gn:act_reloadAutoFilledWidgets]|[@+id/timestampDATA, @+id/deviceSerial]; [gn:act_trigger]|[@+id/showSaveButton, gn:act_set]::[gn:act_toast]|[@string/validateAllFields]]" /> <!-- show save button --> <FrameLayout android:id="@+id/showSaveButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" gn:act_set="[gn:act_showWidgets]|[@+id/btnAssignTagSave], [gn:act_hideWidgets]|[@+id/btnAssignTagUpdate]" /> <!-- show update button --> <FrameLayout android:id="@+id/showUpdateButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" gn:act_set="[gn:act_hideWidgets]|[@+id/btnAssignTagSave], [gn:act_showWidgets]|[@+id/btnAssignTagUpdate]" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginBottom="20dp" android:text="@string/txtAssignTagHeading" android:textSize="35sp" android:textColor="#ffffff" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:text="@string/txtAssignTagScanPassengerTag" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnNfcEmulator android:id="@+id/myEmulator" android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.ginstr.widgets.GnEditText android:id="@+id/nfcPassengerTagDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" gn:s_hint="@string/txtAssignTagScanPassengerTagHint" gn:s_sourceType="nfc" gn:nfcReadingsInterval="5" android:textColor="#000000" android:textColorHint="#FFFFFF" android:textSize="20sp" android:background="@drawable/entering_field_transparent_2.9" gn:s_nfcAlertType="vibrate" gn:act_setOnNfc="[gn:act_if]|[[gn:act_hasTableRights]|[passengers!=R]--[gn:act_toast]|[@string/txtNoReadRights]; [gn:act_break]|[]::], [gn:act_cleanWidgets]|[@+id/namePassengerDATA, @+id/surnamePassengerDATA, @+id/personnelNumPassengerDATA, @+id/personnelCommentDATA, @+id/gpsLocationDATA, @+id/passengerUpdatePointer], [gn:act_if]|[[gn:act_rawQueryToWidgets]|[passengers, passengers; passenger_NFC, passenger_firstName, passenger_lastName, passenger_departmentNumber, passenger_comments, passenger_address, passenger_deviceSerial, passenger_recordCreationTimestamp, $tableId; @+id/nfcPassengerTagDATA, @+id/namePassengerDATA, @+id/surnamePassengerDATA, @+id/personnelNumPassengerDATA, @+id/personnelCommentDATA, @+id/gpsLocationDATA, @+id/deviceSerialDATA, @+id/timestampDATA, @+id/passengerUpdatePointer; @+id/nfcPassengerTagDATA]--[gn:act_trigger]|[@+id/showUpdateButton,gn:act_set]; [gn:act_toast]|[@string/toastNfcPassengerExists]::[gn:act_toast]|[@string/toastNfcPassengerIdCardIsUnknown]; [gn:act_cleanWidgets]|[@+id/namePassengerDATA, @+id/surnamePassengerDATA, @+id/personnelNumPassengerDATA, @+id/personnelCommentDATA, @+id/gpsLocationDATA, @+id/passengerUpdatePointer]]; [gn:act_trigger]|[@+id/showSaveButton,gn:act_set]]" gn:act_validate="[name:RequiredValidator], [message=@string/txtAssignTagScanPassengerTagRequired]" /> <com.ginstr.widgets.GnEditText android:id="@+id/passengerUpdatePointer" android:layout_width="match_parent" android:layout_height="wrap_content" gn:s_sourceType="input" gn:visible="false" gn:dataType="pointer" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtAssignTagFirstName" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/namePassengerDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" gn:s_sourceType="input" android:textColor="#000000" android:textColorHint="#FFFFFF" android:textSize="20sp" android:background="@drawable/entering_field_transparent_2.9" gn:s_hint="@string/txtAssignTagFirstNameHint" gn:act_validate="[name:RequiredValidator], [message=@string/txtAssignTagFirstNameRequired]" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtAssignTagLastName" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/surnamePassengerDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" gn:s_sourceType="input" android:textColor="#000000" android:textColorHint="#FFFFFF" android:textSize="20sp" android:background="@drawable/entering_field_transparent_2.9" gn:s_hint="@string/txtAssignTagLastNameHint" gn:act_validate="[name:RequiredValidator], [message=@string/txtAssignTagLastNameRequired]" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtAssignTagDepartmentNumber" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/personnelNumPassengerDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" android:textColor="#000000" android:textColorHint="#FFFFFF" android:textSize="20sp" android:background="@drawable/entering_field_transparent_2.9" gn:inputType="NUMBER" gn:dataType="number" gn:s_decimalFormat="#" gn:s_sourceType="input" gn:s_hint="@string/txtAssignTagDepartmentNumberHint" gn:act_validate="[name:RequiredValidator], [message=@string/txtAssignTagDepartmentNumberRequired]" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtAssignTagComment" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/personnelCommentDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" android:textColor="#000000" android:textColorHint="#FFFFFF" android:textSize="20sp" android:background="@drawable/entering_field_transparent_2.9" gn:s_sourceType="input" gn:s_hint="@string/txtAssignTagCommentHint" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtAssignTagAddress" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/gpsLocationDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" android:textColor="#000000" android:textColorHint="#FFFFFF" android:textSize="20sp" android:background="@drawable/entering_field_transparent_2.9" gn:s_sourceType="gps" gn:s_hint="@string/txtAssignTagAddressHint" gn:act_validate="[name:RequiredValidator], [message=@string/txtAssignTagAddressRequired]" gn:showAddress="true"/> <com.ginstr.widgets.GnEditText android:id="@+id/deviceSerial" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:s_sourceType="serial" gn:visible="false"/> <com.ginstr.widgets.GnEditText android:id="@+id/deviceSerialDATA" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:inputType="NUMBER" gn:dataType="number" gn:s_decimalFormat="#" gn:s_sourceType="input" gn:visible="false" /> <com.ginstr.widgets.GnEditText android:id="@+id/timestampDATA" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:s_sourceType="time" gn:visible="false"/> <Button android:id="@+id/btnAssignTagSave" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:textSize="20sp" android:background="@drawable/button_half.9" gn:setBackgroundPressed="@drawable/button_half_pressed.9" android:textColor="#000000" android:text="@string/txtAssignTagSave" gn:act_setClick="[gn:act_trigger]|[@+id/savePassenger,gn:act_set]" /> <Button android:id="@+id/btnAssignTagUpdate" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:textSize="20sp" android:background="@drawable/button_half.9" gn:setBackgroundPressed="@drawable/button_half_pressed.9" android:textColor="#000000" android:text="@string/txtAssignTagUpdate" gn:act_setClick="[gn:act_trigger]|[@+id/updatePassenger,gn:act_set]" /> </LinearLayout> </RelativeLayout> </ScrollView>
Before page is loaded, 2 actions are executed for showing and hiding widgets. Since this screen will be used to save new NFC tag and update existing NFC tag data, we will use 2 buttons interchangeably, save and update. When page is loaded, button for save is displayed and if NFC tag already exists in database then update button will be shown to user.
Widget @+id/nfcPassengerTagDATA
has attached event for reading NFC tags. On screenshot 2, this is the first input box.
When NFC tag is read by device, action chain in executed in event gn:act_setOnNfc
. First, we check user credentials for reading database data and if user has rights, database table with passengers is checked for existence of read NFC tag. This is done with action gn:act_rawQueryToWidgets
which is encapsulated into IF statement. Action gn:act_rawQueryToWidgets
has 3 parts with which is table queried. The first 2 parameters are table name and query. Table name is to which table is query intended and query is for purpose of pointing which keys are used. If we check database folder file queries.xml query with id “passengers”
has for a key “passenger_NFC”
. Table name and query name will not be the same and in other examples, soon we will see why.
The middle part of action for querying table is list of columns and list of widget into which returned column values will be stored. The same applies for writing to database, columns and target widgets are ordered in pairs. Last part is id of widget which has value used for key in this query. So table passengers will be queried by scanned NFC tag. If action gn:act_rawQueryToWidgets
returns true
, then column values will be retrieved and displayed in widgets. In this case button “update” will be shown. Otherwise, query returns false
and toast message additionally informs user about that and button “save” will appear and “update” will be hidden. Cleaning widget content must be done before new data is examined to ensure previously entered (retrieved) data is erased from widgets.
Case when scanned NFC tag is not found should proceed with entering passenger data and to be concluded with saving that data to database. Triggers are used in business logic implementation to keep action chains in logically grouped. So when button for saving is clicked, user rights to create new record in table passengers is executed. After that, we reload values for current timestamp and serial number of device. Since serial number value number is textual format and app requires that this should be stored as number, textual serial number is copied to another widget which will then ensure saving this value as number
data type. Saving different data type to column then configured in table will cause invalid app behaviour.
Updating data will be case when read NFC tag already exists in database, after which query will set record data to widgets in app. Business logic ensures that NFC tag is unique for passengers while data can also be changed and updated via ginstr app. Additional parameters are used when updating data. Note $tableId
as last parameter in gn:act_rawQueryToWidgets
which is paired with @+id/passengerUpdatePointer
. Into this widget will be loaded id of table record and used for telling update action which record to update. Examining in @+id/updatePassenger
action gn:act_rawUpdateValues
after table name it is placed this record id. Other parameters are same as in action for writing new record in widget. For more details see Actions page.
book.xml
Next we will implement screen for actual saving check-in / check-out of passengers.
For this create XML file book.xml with following code:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:gn="http://schemas.ginstr.com/ginstr" android:id="@+id/bookings" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/background" gn:act_setBack="[gn:act_cleanWidgets]|[]" gn:act_beforeLoad="[gn:act_showWidgets]|[@+id/txtBookingStateStartLayout], [gn:act_hideWidgets]|[@+id/txtBookingStateCheckInLayout, @+id/txtBookingStateCheckOutLayout]"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginLeft="20dp" android:layout_marginRight="20dp"> <!-- Query employee by scanned nfc tag --> <FrameLayout android:id="@+id/scanPassengerByNfc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" gn:act_set="[gn:act_copyData]|[@+id/deviceSerial, @+id/deviceSerialDATA], [gn:act_if]|[[gn:act_hasTableRights]|[passengers!=R]--[gn:act_toast]|[@string/txtNoReadRights]; [gn:act_break]|[]::], [gn:act_if]|[[gn:act_widgetContent]|[empty,@+id/booking_vehicleName]--[gn:act_toast]|[@string/txtBookingSelectVehicleRequired]; [gn:act_cleanWidgets]|[@+id/nfcPassengerTagDATA, @+id/namePassengerDATA, @+id/surnamePassengerDATA, @+id/personnelNumPassengerDATA, @+id/commentDATA, @+id/gpsLocationDATA]; [gn:act_break]|[]::], [gn:act_cleanWidgets]|[@+id/timestampDATA, @+id/timestampEndDATA, @+id/timestampDifferenceDATA, @+id/activityPassenger], [gn:act_if]|[[gn:act_rawQueryToWidgets]|[passengers, passengers; passenger_firstName, passenger_lastName, passenger_departmentNumber, passenger_comments, passenger_address; @+id/namePassengerDATA, @+id/surnamePassengerDATA, @+id/personnelNumPassengerDATA, @+id/commentDATA, @+id/gpsLocationDATA; @+id/nfcPassengerTagDATA]--[gn:act_trigger]|[@+id/queryBookingsByNfc, gn:act_set]::[gn:act_toast]|[@string/toastBookingNfcPassengerIdCardIsUnknown]; [gn:act_cleanWidgets]|[@+id/namePassengerDATA, @+id/surnamePassengerDATA, @+id/personnelNumPassengerDATA, @+id/commentDATA, @+id/gpsLocationDATA]; [gn:act_showWidgets]|[@+id/txtBookingStateStartLayout]; [gn:act_hideWidgets]|[@+id/txtBookingStateCheckInLayout, @+id/txtBookingStateCheckOutLayout]]"/> <!-- decide to check in or check out --> <FrameLayout android:id="@+id/queryBookingsByNfc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" gn:act_set="[gn:act_if]|[[gn:act_hasTableRights]|[bookings!=R]--[gn:act_toast]|[@string/txtNoReadRights]; [gn:act_break]|[]::], [gn:act_if]|[[gn:act_rawQueryToWidgets]|[bookings, bookings; booking_passenger_NFC, booking_active, booking_tripBegin, $tableId; @+id/nfcPassengerTagDATA, @+id/activityPassenger, @+id/timestampDATA, @+id/booking_vehicleNamePointer; @+id/nfcPassengerTagDATA, @+id/booking_vehicleName, true]--[gn:act_trigger]|[@+id/checkOutPassenger, gn:act_set]::[gn:act_trigger]|[@+id/checkInPassenger, gn:act_set]]" /> <!-- check in --> <FrameLayout android:id="@+id/checkInPassenger" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" gn:act_set="[gn:act_showWidgets]|[@+id/txtBookingStateCheckInLayout], [gn:act_hideWidgets]|[@+id/txtBookingStateStartLayout, @+id/txtBookingStateCheckOutLayout], [gn:act_reloadAutoFilledWidgets]|[@+id/timestampDATA, @+id/deviceUserDATA], [gn:act_cleanWidgets]|[@+id/timestampEndDATA, @+id/timestampDifferenceDA-TA], [gn:act_rawWriteValues]|[bookings; booking_passenger_NFC, booking_active, booking_vehicleName, booking_userName, booking_passenger_firstName, booking_passenger_lastName, booking_passenger_departmentNumber, booking_comments, booking_passenger_location, booking_deviceSerial, booking_tripBegin, booking_tripEnd, booking_tripDurationInHours; @+id/nfcPassengerTagDATA, true, @+id/booking_vehicleName, @+id/deviceUserDATA, @+id/namePassengerDATA, @+id/surnamePassengerDATA, @+id/personnelNumPassengerDATA, @+id/commentDATA, @+id/gpsLocationDATA, @+id/deviceSerialDATA, @+id/timestampDATA, @+id/timestampEndDATA, @+id/timestampDifferenceDATA], [gn:act_toast]|[@string/toastBookingInSaved]" /> <!-- check out --> <FrameLayout android:id="@+id/checkOutPassenger" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" gn:act_set="[gn:act_showWidgets]|[@+id/txtBookingStateCheckOutLayout], [gn:act_hideWidgets]|[@+id/txtBookingStateStartLayout, @+id/txtBookingStateCheckInLayout], [gn:act_reloadAutoFilledWidgets]|[@+id/timestampEndDATA], [gn:act_calculate]|[@+id/timestampDifferenceDATA, ((@+id/timestampEndDATA-@+id/timestampDATA)/1000)/3600], [gn:act_rawUpdateValues]|[bookings, @+id/booking_vehicleNamePointer; booking_active, booking_tripEnd, booking_tripDurationInHours; false, @+id/timestampEndDATA, @+id/timestampDifferenceDATA], [gn:act_toast]|[@string/toastBookingOutSaved]" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginBottom="20dp" android:text="@string/txtBookingHeading" android:textSize="35sp" android:textColor="#ffffff" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:text="@string/txtBookingSelectVehicle" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnDropDown android:id="@+id/booking_vehicleName" android:layout_width="fill_parent" android:layout_height="wrap_content" gn:act_validate="[name:RequiredValidator], [message=@string/txtBookingSelectVehicleRequired]" gn:data_source_request="vehicles" gn:data_source_field="vehicle_name" gn:act_setItemSelect="[gn:act_saveToVariable]|[@variable/siteSelection, @+id/booking_vehicleName], [gn:act_setVariable]|[@variable/siteSelection, @+id/booking_vehicleName], [gn:act_saveToVariable]|[@variable/siteSelectionText, @+id/nfcPassengerTagDATA]" gn:s_sourceType="database" gn:act_beforeLoad="[gn:act_rawQueryToWidget]|[vehicles, vehicles, vehicle_name, @+id/booking_vehicleName], [gn:act_setSelectedItem]|[@+id/booking_vehicleName, @variable/siteSelection]" /> <!-- helper pointer to update booking --> <com.ginstr.widgets.GnEditText android:id="@+id/booking_vehicleNamePointer" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" gn:s_sourceType="input" gn:dataType="pointer" gn:visible="false" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:text="@string/txtBookingScanPassengerTag" android:textSize="20sp" android:textColor="#ffffff"/> <com.ginstr.widgets.GnNfcEmulator android:id="@+id/myEmulator" android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.ginstr.widgets.GnEditText android:id="@+id/nfcPassengerTagDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" gn:s_hint="@string/txtBookingScanPassengerTagHint" gn:s_sourceType="nfc" android:textColor="#000000" android:textColorHint="#FFFFFF" android:background="@drawable/entering_field_transparent_2.9" android:textSize="20sp" gn:s_nfcAlertType="vibrate" gn:nfcReadingsInterval="5" gn:focusable="false" gn:act_setOnNfc="[gn:act_trigger]|[@+id/scanPassengerByNfc,gn:act_set]" gn:act_validate="[name:RequiredValidator], [message=@string/txtBookingTagScanPassengerTagRequired]" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtBookingFirstName" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/namePassengerDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" android:textSize="20sp" android:textColor="#000000" android:textColorHint="#FFFFFF" android:background="@drawable/entering_field_transparent_2.9" gn:s_sourceType="input" gn:s_hint="@string/txtBookingFirstNameHint" gn:focusable="false" gn:act_validate="[name:RequiredValidator], [message=@string/txtBookingFirstNameRequired]" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtBookingLastName" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/surnamePassengerDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" android:textColor="#000000" android:textSize="20sp" android:textColorHint="#FFFFFF" android:background="@drawable/entering_field_transparent_2.9" gn:s_sourceType="input" gn:s_hint="@string/txtBookingLastNameHint" gn:focusable="false" gn:act_validate="[name:RequiredValidator], [message=@string/txtBookingLastNameRequired]" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtBookingDepartmentNumber" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/personnelNumPassengerDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" android:textColor="#000000" android:textColorHint="#FFFFFF" android:background="@drawable/entering_field_transparent_2.9" android:textSize="20sp" gn:inputType="NUMBER" gn:dataType="number" gn:s_decimalFormat="#" gn:s_sourceType="input" gn:s_hint="@string/txtBookingDepartmentNumberHint" gn:focusable="false" gn:act_validate="[name:RequiredValidator], [message=@string/txtBookingDepartmentNumberRequired]" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtBookingComment" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/commentDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" android:textColor="#000000" android:textColorHint="#FFFFFF" android:textSize="20sp" android:background="@drawable/entering_field_transparent_2.9" gn:s_sourceType="input" gn:focusable="false" gn:s_hint="@string/txtBookingCommentHint" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="@string/txtBookingAddress" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnEditText android:id="@+id/gpsLocationDATA" android:layout_width="match_parent" android:layout_height="wrap_content" gn:paddingTop="2dp" gn:paddingBottom="2dp" android:textColor="#000000" android:textColorHint="#FFFFFF" android:background="@drawable/entering_field_transparent_2.9" android:textSize="20sp" gn:s_sourceType="gps" gn:s_hint="@string/txtBookingAddressHint" gn:act_validate="[name:RequiredValidator], [message=@string/txtBookingAddressRequired]" gn:focusable="false" gn:showAddress="true"/> <com.ginstr.widgets.GnEditText android:id="@+id/deviceSerial" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:s_sourceType="serial" gn:visible="false"/> <com.ginstr.widgets.GnEditText android:id="@+id/deviceSerialDATA" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:inputType="NUMBER" gn:dataType="number" gn:s_decimalFormat="#" gn:s_sourceType="input" gn:visible="false" /> <com.ginstr.widgets.GnEditText android:id="@+id/deviceUserDATA" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:s_sourceType="aguser" gn:visible="false"/> <com.ginstr.widgets.GnEditText android:id="@+id/timestampDATA" android:layout_width="200dp" android:layout_height="wrap_content" gn:s_sourceType="time" gn:visible="false"/> <com.ginstr.widgets.GnEditText android:id="@+id/timestampEndDATA" android:layout_width="200dp" android:layout_height="wrap_content" gn:s_sourceType="time" gn:visible="false"/> <com.ginstr.widgets.GnEditText android:id="@+id/timestampDifferenceDATA" android:layout_width="200dp" android:layout_height="wrap_content" gn:s_sourceType="input" gn:dataType="number" gn:s_decimalFormat="0.00" gn:visible="false"/> <com.ginstr.widgets.GnEditText android:id="@+id/activityPassenger" android:layout_width="wrap_content" android:layout_height="wrap_content" gn:visible="false" gn:s_sourceType="input"/> <LinearLayout android:id="@+id/txtBookingStateStartLayout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/txtBookingStateStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="5dp" android:text="@string/txtBookingStateStart" android:textSize="22sp" android:textColor="#ffffff" /> </LinearLayout> <LinearLayout android:id="@+id/txtBookingStateCheckInLayout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/txtBookingStateCheckIn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="5dp" android:text="@string/txtBookingStateCheckIn" android:textSize="22sp" android:textColor="#ffffff" /> </LinearLayout> <LinearLayout android:id="@+id/txtBookingStateCheckOutLayout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/txtBookingStateCheckOut" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="5dp" android:text="@string/txtBookingStateCheckOut" android:textSize="22sp" android:textColor="#ffffff" /> </LinearLayout> </LinearLayout> </RelativeLayout> </ScrollView>
On loading page “enter / exit” some widgets on bottom of screen are shown while others are hidden. Those widgets are used as statuses in respect of what action has taken place. Initially, this would be “scan passenger id card” displayed at the bottom of the page. When passenger boards the vehicle (checks in), then this would be “on board” and on disembarking would have status “gone” at the bottom. So we have 3 widgets which are displayed or hidden depending on business logic.
First widget in page is dropdown @+id/booking_vehicleName
which has pre-loaded vehicles from table vehicles. This is executed based on attached event gn:act_beforeLoad
to the widget. In the same action, item is selected in dropdown based on value in variable @variable/siteSelection
which holds item selected in last app usage. This variable is changing in event gn:act_setSelectedItem
.
Scanning NFC tag starts checking if scanned NFC tag should be recorded as passenger checking in or checking out. Event gn:act_setOnNfc
is attached to widget @+id/nfcPassengerTagDATA
which then has references to widget with business logic. First, this is in @+id/scanPassengerByNfc
which queries table passengers based on scanned NFC tag. If NFC tag is not known, then business logic stops with toast message. In other case NFC tag passenger data is loaded into widgets, filling first name, last name and others with execution of trigger action @+id/queryBookingsByNfc
. In table bookings NFC tag is checked with text value true
in column “active”
, which would return if some passenger is boarded or not. If this record is not found then all widgets on the screen are saved into new record, indicating that this passenger is boarding a vehicle. If NFC tag is found in bookings with status true
, then calculation of duration between check out time subtracting and check in time is performed. Widgets @+id/checkInPassenger
and @+id/checkOutPassenger
trigger action sets for saving / updating data in table bookings. Note that when querying record in bookings, same query name is used for referencing query which uses keys, this is second parameter after table name. This is not always a good solution.
For scenario when dropdown must be populated with all records from certain table in database, loading item value is working identical as in other queries. For this usage query in queries.xml should not have any keys defined and in this case table name and query would be same in gn:act_rawQueryToWidget
action. Therefore, it is good practice to have a “pure” query for table without keys, and make an additional query with keys for other uses.
site.xml
For overview of passengers currently boarded on some vehicle we will make site.xml as follows:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:gn="http://schemas.ginstr.com/ginstr" android:layout_width="fill_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:id="@+id/vehicle" android:background="@drawable/background"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginBottom="20dp" android:text="@string/txtAttendanceHeading" android:textSize="35sp" android:textColor="#ffffff" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:text="@string/txtAttendanceSelectVehicle" android:textSize="20sp" android:textColor="#ffffff" /> <com.ginstr.widgets.GnDropDown android:id="@+id/vehiclesDD" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginBottom="15dp" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" gn:singleItemMode="true" gn:act_validate="[name:RequiredValidator], [message=@string/txtAttendanceSelectVehicle]" gn:act_setItemSelect="[gn:act_if]|[[gn:act_rawQueryToWidget]|[bookings, bookingsByVehicleName, @+id/lstSite; @+id/vehiclesDD,true]--::[gn:act_cleanWidgets]|[@+id/lstSite]]" gn:s_sourceType="database" gn:act_beforeLoad="[gn:act_rawQueryToWidget]|[vehicles, vehicles, vehicle_name, @+id/vehiclesDD]" /> <!-- listview header --> <LinearLayout android:layout_height="50dp" android:layout_width="match_parent" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:background="#666666" android:orientation="horizontal"> <com.ginstr.widgets.GnTextView android:id="@+id/txtAttendancePassengerFirstName" android:layout_width="0dp" android:layout_weight="1" android:layout_marginLeft="5dp" android:layout_height="match_parent" android:text="@string/txtAttendancePassengerFirstName" android:textSize="20sp" gn:s_textStyle="bold" android:gravity="center_vertical" android:background="#666666" android:textColor="#FFFFFF"/> <com.ginstr.widgets.GnTextView android:id="@+id/txtAttendancePassengerLastName" android:layout_width="0dp" android:layout_weight="1" android:layout_marginLeft="5dp" android:layout_height="match_parent" android:text="@string/txtAttendancePassengerLastName" android:textSize="20sp" gn:s_textStyle="bold" android:gravity="center_vertical" android:background="#666666" android:textColor="#FFFFFF"/> </LinearLayout> <com.ginstr.widgets.GnListView android:id="@+id/lstSite" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal" gn:data_filters="[#today]|[=]|[booking_tripBegin]" gn:data_columns_sort="booking_passenger_firstName, booking_passenger_lastName" gn:data_columns_name="@string/txtAttendancePassengerFirstName, @string/txtAttendancePassengerLastName" gn:data_columns_width="50,50" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" gn:data_columns_id="booking_passenger_firstName, booking_passenger_lastName" gn:data_columns_type="text,text" android:background="#555555" gn:serverTableGenerator="ignore" gn:lstRefreshNoFilter="true" gn:custom_xml="customList_listview.xml" gn:custom_header_xml="attendeeList_header.xml" gn:custom_row_xml="attendeeList_row.xml" gn:complex_cell_layout="true" /> </LinearLayout>
Dropdown @+id/vehiclesDD
offers all vehicles selection loaded from table vehicles. On attached event setSelectedItem
special bulk query is executed which will load data into @+id/lstSite
. More about bulk gn:act_rawQueryToWidgets
here.
When a vehicle is selected from the dropdown, all records from table bookings which have “booking_active”
column value “active”
and matching selected item (vehicle name) are loaded into widget. For this, query bookingsByVehicleName
is used. Returned data will be presented in GnListView
.
Each found record will have first and last name from table bookings and also in widget GnListView
, records will be examined for column booking_tripBegin
with attribute data_filters
to check if this value is equal to current date of app usage. In effect, this would mean that only passengers that are boarded today will be shown.
Note that @+id/lstSite
has attributes for customising GnListView
look. Those attributes begin with “gn:custom_
” referencing XML files in "control" folder. From the point of correct data translation from query to @+id/lstSite
, the most important is attendeeList_row.xml. In this XML file, GnTextView
widgets must column names from table bookings. Note that GnListView
is highly customisable, see here for full usage information.