From wiki.ginstr.com
Jump to: navigation, search

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_nametext Dt, required and unique
vehicle_costCentretext Dt – not required, not unique (usually omitted)
vehicle_commentstext Dt
2. table passengers – storing passenger record data
passenger_NFCtext Dt, required and unique
passenger_firstNametext Dt
passenger_depertmentNumbernumber Dt, no decimal point required
passenger_commentstext Dt
passenger_deviceSerialnumber Dt, no decimal point
passenger_recordCreationTimestampdateTime Dt
3. table bookings – storing check-in / check-out record data
booking_passenger_NFCtext Dt, required
booking_userNametext Dt
booking_passenger_firstNametext Dt, required
booking_passenger_lastNametext Dt, required
booking_passenger_departmentNumbernumber Dt, required
booking_commentstext Dt
booking_passenger_locationtext Dt, required
booking_deviceSerialnumber Dt, required
booking_tripBegindateTime Dt, required
booking_tripEnddateTime Dt
booking_tripDurationInHoursdateTime Dt
booking_vehicleNamepointer Dt, required
booking_activetext 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.

  1. vehiclesTABLE.xml
  2. passengersTABLE.xml
  3. bookingsTABLE.xml

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>
Screenshot 1. Options screen of passenger transportation company app

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>
Screenshot 2. Add new passenger screen of Passenger transportation company app

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>
Screenshot 3. Passengers per vehicle screen

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.