All functions will be under "Patterns.Mobile".
CordovaPage
This function inherits from ~WebShell, and has an IMPL Name of Cordova. Any function inheriting from this will use the Cordova-page.ctrl template. This pattern is to be used with any function using Cordova (PhoneGap) plugins.
GeoLocation
This function inherits from ~WebShell. The panel contains a field called "LatitudeLongitudeCoordinates" in "LocationP". It has a Control Name of "GeoLoc:HiddenArea:template=WebLocation". The "WebLocation.ctrl" template will fetch a latitude/longitude string whenever the panel is shown. An event "GeoLocationUpdated" tied to the Updated event on the field. The event itself only has an Edit Point in it at this level. Any functions that needs to use the device's GeoLocation feature will inherit from this pattern.
GoogleMaps
This function inherits from ~WebShell. The panel displays the following field with the following Control Name:
MobileGoogleMap | for | GoogleMap |
Control Name: | gMap:FullscreenArea:template=WebMap |
This uses our WebMap control template to display a Google Map centered on the value cast into it. In our action diagram, we are casting Local< LatitudeLongitudeCoordinates> to this field. So, any function that inherits from this function will need to set this local value to center the map.
IncidentMobilePage
This is a simple page template with the IMPL Name of "IncidentMobilePage". Any function that inherits from this will bring in the code from " IncidentMobilePage-page.ctrl", which initialized the CSS file, custom.js file, and Google map api.
ToolbarBottom
This is a simple page template with the IMPL Name of " ToolbarBottom". Any function that inherits from this will bring in the code from " ToolbarBottom-page.ctrl", which contains a footerbar, which 3 attach points.
MobileWebClientPanel
This is the basis for all of our Mobile panels. It inherits from ~WebShell, _Abstract.~WebMessages, and Patterns.Mobile.IncidentMobilePage. The panel contains these buttons:
Incident Reporter | |
Control Name: Events: Notes: | TitleBtn:ToolbarArea:align=center:cls=LabelButton None This is just being used as a Title for the application. |
Logout | |
Control Name: Event: Notes: | LogoutBtn:ToolbarArea:align=right:direction=left Pressed = Logout The Logout event will just call the Login Page again. |
*Blank (Back Button) | |
Control Name: Event: Notes: | BackBtn:ToolbarArea:align=left:ui=back:direction=right:iconCls=arrow_left Pressed = Close This button is hidden on most screens |
PlaceH | |
Control Name: Events: Notes: | PlaceholderBtn:ToolbarArea:cls=placeholderBtn:align=left:direction=right NONE This button is only shown on functions where the Back Button is hidden. |
It is used to keep the spacing even on the toolbar.
MobileMenuPanel
This pattern is being used by the screens that will have the menu items displayed in the footer bar. It inherits from Patterns.Mobile.MobileWebClientPanel and Patterns.Mobile.ToolbarBottom. It also has the "LoginEmail" as in Input field. It adds 3 buttons to the panel:
Info | |
Control Name: Event: Notes: | InfoBtn:ToolbarBottomCenter:direction=right:seq=1:iconCls=infomenu Pressed = Info Event calls " User Splashscreen.Mobile.Info" |
Report | |
Control Name: Event: Notes: | ReportBtn:ToolbarBottomCenter:direction=right:seq=2:iconCls=reportmenu Pressed = Report Event calls " Incident.Mobile.Report" |
My Reports | |
Control Name: Event: | MyReportsBtn:ToolbarBottomCenter:direction=right:seq=3: iconCls=myreportsmenu Pressed = My Reports |
Notes: Event calls "Incident.Mobile.MyReports"
Panel Setup
In this panel, we are displaying the following fields:
LoginEmail | for | DetailP |
Control Name: | LoginEmail:MainArea:fieldSet=Login:hint=Email/Cell:align=center | |
Password | for | DetailP |
Control Name: | LoginPassword:MainArea:fieldSet=Login:hint=Password:align=center: template=WebPasswordNoLabel | |
LoginEmail | for | CookieP |
Control Name: | UserID:MainArea:template=WebCookie | |
Password | for | CookieP |
Control Name: | Password:MainArea:template=WebCookie | |
YesNo | for | CookieP |
Control Name: Event: | Control Name: SaveUser:MainArea:fieldSet=Login:align=center Modified = SaveOptionChanged | |
Login Button | ||
Event: Control Name: | Pressed = Login LoginBtn:MainArea:fieldSet=3:align=center:direction=left | |
Register Button | ||
Event: Control Name: | Pressed = Register RegisterBtn:MainArea:fieldSet=4:align=center:direction=left |
Notes: Both the CookieP and CookieP fields have event mapped to Modified and Updated. These fields do not actually show up on the panel. The events are triggered from the WebCookie template. This will be explained further later on.
Action Diagram
Initialize
We initialize all of DetailP to Blank, and CookieP to No. We also are setting the "Login Valid?" and "Cookie Modified?" flags to No. These will be used later.
Subroutines
Sub Call Main Menu: | This is where we call the initial function after the user logs in. We are currently calling the "Info" panel. |
Sub Validate Password: | This is where validate the entered password compared to the one saved to the database. |
Sub Login: | We start by validating the Login information. The user can login with an email address or their cell phone number, so we need to check both. If the Login is valid, then we check to see if the user wants to save the login information to local storage, but it has not been saved yet. If this is the case, we set the CookieP fields and set the "CookieModified?" flag to Yes. This flag will prevent us from calling the next function until after the Cookie has been saved to local storage. This will be explained more later. The LoginEmail is also saved to the ObClient file. So, if we want to reference it later, we can. Finally, we call the "Main Menu" function if we are not currently saving the Cookie to local storage. |
Events
Login: | This just calls our Login Subroutine |
Register: | This calls our Register panel, which we will mention later in the document. If the user Registers correctly, we will also automatically log them in using their created information. |
SaveOptionChanged: | If the checkbox value is Yes, then we are setting the CookieP fields. If it is set to No, then we will clear the CookieP fields. Our templates with handle the adding/removing of the cookie from local storage. This will be explained further later. |
UserPWFound: | This event is triggered when the Cookie template loads a Cookie from local storage. It will take the information from the cookie and set it to the DetailP. |
UserPWSaved: | This event is triggered when the Cookie template saves the cookie to local storage. In our code, if the login information has already been validated, then we want to call the "Main Menu" function. |
Custom Templates
This function makes use of the WebCookie template included with WebClient Mobile. This causes the field to be a non-visible element in the dom. Any time a "Put" is done on a field using this template, the field information will be saved to the Local Storage and will trigger the Updated event for the field. Also, when the page is loaded, if the local storage element is found, then it will set the field value and trigger the Modified event for the field.
In this panel, we are displaying the following fields:
FirstName | for | DetailP |
Control Name: | FirstName:MainArea:hint=First Name:align=center:fieldSet=1 | |
LastName | for | DetailP |
Control Name: | LastName:MainArea:hint=Last Name:align=center:fieldSet=1 | |
LoginEmail | for | DetailP |
Control Name: | Email:MainArea:hint=Email:align=center:fieldSet=1 | |
Cell Number Text | for | DetailP |
Control Name: | CellNumTxt:MainArea:hint=Cell#:align=center:fieldSet=1: template=WebPhoneNumber |
Note: WebPhoneNumber is a template that brings up a different keypad when the use edits the field.
Password | for | DetailP |
Control Name: | Password:MainArea:hint=Password:align=center:fieldSet=1: template=WebPasswordNoLabel | |
Register Button | ||
Control Name: Events: | RegisterBtn:MainArea:align=center:direction=left:fieldSet=2 Pressed = Register |
Action Diagram
This field outputs the registered LoginEmail and Password, as well as a flag showing the user registered.
Initialize
We initialize all of the Output to Blank and the Registered flag to No.
Subroutines
Sub Validate DetailP: | This is where validate that all of the DetailP has been entered. |
Sub Create Record: | This is where we create the User record, set the Output, and exit the function. |
Events
Register: | This casts the CellPhoneText to the CellPhone field. CellPhoneText is used for display purposes. We call the Validate subroutine and, if valid, call the create record subroutine. |
This function only exists for Mobile. It is designed to load a customized image and message for a user. If one is not found, then a default image and message will be shown.
Panel
In this panel, we are displaying the following fields:
SplashImage | for | DetailP |
Control Name: | AppLogo:MainArea:template=WebURLPicture:seq=1:imageCls=FramedImage: imageAlign=center | |
SplashMessage | for | DetailP |
Control Name: | GeneralInfo:MainArea:template=WebMultilineEdit:align=center:seq=2: cls=generalInfo |
We are also protecting the "Info" button, so that the function cannot call itself. We also add the following element to the Control Name: "cls=protectedMenuButton". This will add some CSS changes to the button.
Action Diagram
This field outputs the registered LoginEmail and Password, as well as a flag showing the user registered.
Initialize
In the initialize, we try to load the User Splashscreen record. If it is found, we set the DetailP with the fetched data. If not, we set the values to their default values.
These functions are designed to show all of the Incidents reported by the current user.
Panel
In this panel, we are displaying the following fields, which are being populated in the BlockFetchSet:
ThumbnailURL | for | GridP |
IncidentStatusDescription | for | GridP |
DateString | for | GridP |
GridP has a Control Name of "Grid1P:FullscreenArea:template=WebGrid:grouped=false:tplGenerator=cmfirst.custom.myReportsTpl". The tplGenerator portion of the Control Name tells WebClient Mobile how it will display the grid. This Tpl can be found in the custom.js file in "incidentmobile/WebContent/js/cmfirst".
We are also protecting the "My Reports" button, so that the function cannot call itself. We also add the following element to the Control Name: " cls=protectedMenuButton". This will add some CSS changes to the button.
Action Diagram
We want to call the View Details function (mentioned later) on the Grid Selected event. Since Plex selects the first row after it loads the grid automatically, it would call the View Details function right away. To prevent this, in "Pre Point Start Load Grid", we set the "Allow SelectGrid?" flag to No. Then, in "Pre Point Start Select Grid" , if this flag is No, we set it to Yes, then exit the subroutine. This will prevent the Select Grid event from running it's code on the first pass.
Then, just add a call to our "IncidentDetails" function in the "Process Selected Grid Row" edit point.
These functions are used to create Incident records. An image can be attached and uploaded to server. This uses the Upload Servlet in the workspace to accomplish this. For more information in setting this up, please refer to our documentation on the File Upload Servlet.
Referencing an External Server
When the files are uploaded with the Upload Servlet, they are stored in a location relative to the Web Server. The problem with this is that if you deploy a new WAR file, all uploaded files stored here would be lost. To solve this, we will copy the files from this location to one outside of the workspace. In our case, we are using "C:\Uploads". For the WebServer to be able to access these files, we need to add a reference to this folder in the "server.xml" file. Please refer to this file and you will see the following line:
"C:/Uploads" path="incidentmobile/photos"/>"C:/Uploads" path="IncidentWeb/photos"/>
This will allow you to access the files in this folder using the "incidentmobile/photos
" and "IncidentWeb/photos
" paths. You can see evidence of this in the code setting the ThumbnailURL in the BlockFetchSet being used by the My Reports functions.
This function inherits from both "Patterns.Mobile.CordovaPage" and "Patterns.Mobile.GeoLocation". This allows the function to use Cordova (PhoneGap) plugins and will use our pattern GeoLocation code.
Panel
In this panel, we are displaying the following fields:
IncidentType | for | DetailP |
Control Name: Control Type: | IncidentType:MainArea:align=center:seq=1:fieldSet=1:label=Type Combo Box | |
IncidentDescription | for | DetailP |
Control Name: | IncidentDesc:MainArea:template=WebMultilineEdit:align=center:seq=3: fieldSet=1:label=Description | |
ImageName | for | DetailP |
Control Name: | ImageName:MainArea:template=WebCameraUpload:align=center: seq=2:fieldSet=2 | |
ImageLocation | for | DetailP |
Control Name: | ImgLoc:HiddenArea:template=WebHiddenEdit:align=center:seq=6: fieldSet=1 | |
Select Location Button | ||
Control Name: Events: | SelectLocBtn:MainArea:direction=right:fieldSet=1:seq=5 Pressed = SelectLocation |
Note: This stores the location of the Photo after it is uploaded to the server.
Submit Button | |
Control Name: Events: | SubmitBtn:MainArea:direction=right:fieldSet=3:seq=5 Pressed = Submit Report |
Please note that the ImageName field is using the " WebCameraUpload " template, which turns the field into a button. When pressed, this launches the camera. After a picture is taken and approved, it will be uploaded to the WebServer. The location in the WebServer is saved to the ImageLocation field in DetailP. This button uses Cordova's camera and file upload APIs, which take a few steps to setup. These steps are listed later in the document. And to use these features, you will need to install the application on an Android device, which is also explained later in the document. For now, if you try to test this function in the regular browser, you will receive messages like ones shown below. Please press "Cancel" on these messages, or you will crash the application.
Action Diagram
Initialize
We initialize the DetailP, "Image Selected?" flag, and "Address Selected?" flag. We call the subroutine to load the Incident Type Combobox. We also fetch the User record for later reference.
Please note that this function uses the GeoLocation template, which will run when the panel is shown.
Subroutines
Sub Validate Submit: | This is subroutine validates that all the information has been entered. It also checks if a location has been selected, either by the GeoLocation or by the "SelectLocation" function. If no image was selected, the user is warned, but allowed to continue without one. |
Sub Save Report: | This is where we create the Incident record. If an image was selected, we will copy the file to the permanent folder, create a thumbnail version of the image, and create the Incident Image record. Then, we call the My Reports function, which will show the newly created report. |
Sub Load IncidentType Combobox: | This is subroutine that populates the Incident Type Combobox based on the records in the Incident Type table. |
Sub UpdateLatLng | This subroutine breaks up the Latitude/Longitude string fetched from the GeoLocation control into the Latitude and Longitude to be saved to the database. |
Sub Select Location: | This subroutine calls the "Select Location" function. It will warn the user if a GeoLocation has already been set by the GeoLocator template. |
Events
GeoLocationUpdated: | This event is triggered by the GeoLocation template. We just do a Go Sub UpdateLatLng to process what was fetched. |
Submit Report: | This subroutine makes sure the Latitude/Longitude has been set, then validates the details. If it passes validation, then it calls the Save Report subroutine. |
Image Selected: | This event is triggered after the photo has been selected and uploaded to the Web Server. We are just setting a flag showing that an image was selected. |
SelectLocation: | This subroutine just calls the Select Location subroutine. |
Panel
In this panel, we are displaying the following fields:
IncidentDate | for | DetailP |
Control Name: | IncDate:MainArea:template=DatePicker:align=center:seq=1: label=Report Date | |
IncidentType | for | DetailP |
Control Name: | IncType:MainArea:align=center:seq=2:label=Type | |
IncidentStatus | for | DetailP |
Control Name: | IncStatus:MainArea:align=center:seq=3:label=Status | |
IncidentDescription | for | DetailP |
Control Name: | IncDesc:MainArea:template=WebMultilineEdit:align=center:seq=4: label=Description | |
CloseDate | for | DetailP |
Control Name: | CloseDate:MainArea:template=DatePicker:align=center:seq=5: label=Close Date | |
ImageName | for | DetailP |
Control Name: | IncidentImage:MainArea:template=WebURLPhoto:seq=6: imageCls=FramedImage:imageAlign=center | |
View Location Button | ||
Control Name: Events: | ViewMap:MainArea:align=center:seq=7 Pressed = ViewMap |
Action Diagram
This field outputs the registered LoginEmail and Password, as well as a flag showing the user registered.
Initialize
We fetch all the Incident record information to display. We also fetch the User record, since we need the Cell Number to locate the attached Incident Image. We then attempt to fetch the Incident Image. If none exists, then we hide the ImageLocation field. Finally, we hide the CloseDate field is it does not contain a valid value.
Events
ViewMap: | This calls the "ViewLocation" function for the record selected. |
These functions both use the Google Map API to convert a search string into a GeoLocation. For example, if you enter " 7000 North Mopac Expressway Austin, TX". The function will use the custom templates to give the Latitude and Longitude of that location. These functions both have the Latitude and Longitude as Input and Output.
Panel
In this panel, we are displaying the following fields:
LocationString | for | DetailP |
Control Name: | LocString:MainArea:hint=Location:fieldSet=1 | |
Full Address | for | DetailP |
Control Name: Events: | FullAddress:MainArea:template=MobileAddressLocator:fieldSet=1 Updated = AddressConverted | |
Confirm Button | ||
Control Name: Events: | Confirm:MainArea:fieldSet=2 Pressed = ConfirmLocation |
Action Diagram
Initialize
We initialize the Output to what was Input. So, if the user cancels their change, then the values will not be overwritten.
Events
ConfirmLocation: | We get the LocationString entered by the user and Set/Put the Full Address. Putting the Full Address will cause the template to calculate the Latitude/Longitude. We do not want to exit the function until the calculation has finished. |
AddressConverted: | This event is triggered once the Full Address has been calculated. So we break up the Latitude/Longitude string, set the output, then terminate. |
This function is for Mobile only. It is using the "Patterns.Mobile.GoogleMaps" pattern. The pattern will take care of nearly everything. We only need to set the Local in the Post Point Process Options. The function will display a GoogleMap centered on this location in the Full Screen Area.
This is an Android Project already setup in the Eclipse workspace: DevComm. Any instructions that are given in here are relative to this project.
Cordova API Setup
We need to install the Plugin APIs for Cordova File Transfer and Camera. Start by opening a command line interface. From your start menu, select 'Run...', then enter 'cmd' to bring up a command prompt. Change directory to the location of your Android project. The command will look something like this: "cd C:\IncidentReporter\Workspace\DeviceComm". Now run the following commands:
1) To install the Cordova Camera plugin:
cordova plugin add org.apache.cordova.camera
2) To install the Cordova File Transfer plugin:
cordova plugin add org.apache.cordova.file-transfer
In your workspace, open "DevComm/AndroidManifest.xml". Click on the AndroidManifest.xml tab. Make sure you have the following permissions set for your app:
"android.permission.WRITE_EXTERNAL_STORAGE" />
"android.permission.ACCESS_COARSE_LOCATION" />
"android.permission.ACCESS_FINE_LOCATION" />
"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
"android.permission.CAMERA" />
"android.hardware.camera" />
"android.hardware.camera.autofocus" />
"android.hardware.camera.flash" />
Installing on Android Device
To install the application on an Android device, we will need to create an APK file. But first, we need to configure the files to point to your web server.
Initial Setup
First, determine your PC's IP Address, but going to the Command Prompt and entering "ipconfig". Take note of the IPv4 Address.
Open the "incidentmobile/WebContent/www/index.html" file. Scroll to the bottom and change the "window.location" line to use your current IP address.
window.location = 'http://192.168.1.105:8080/incidentmobile/wc';
Icon and App Name
Open "DevComm/AndroidManifest.xml". Click on the Application tab. Under the Icon entry, press the Browse button. You can use the Android Resources to create any icon that you want.
To change the App name, go to "DevComm/res/values/string.xml". Click on the app_name item, and change the Value to what you would like to name the App.
Create and Deploy an APK
An Android application can be exported to an Android installation file (APK). Applications are usually signed with a key within a keystore. Please Note that these screen shots are for a different Android Project, but the instructions are still the same.
Select your DevComm project, right-click and select Android Tools --> Export Signed Application Package
Click Next.
Select Create a New Keystore. Put this file in a place that you can easily find and give it a password that you will remember. This keystore can be used for all Android Applications that you create. After you create the keystore, login to it and set the location where you want to create your APK file.
You can use Hightail, Dropbox, email, ect. to get the APK file on your device.
You now need to setup your Android Device.
By default, Android prevents you from installing APK files from unverified sources outside of the Play Store. To install this file, you will need to bypass this check. Go to the 'Settings' app, select the 'Security' option, then check the 'Unknown sources' option.
You will be prompted with a warning screen when you select the option. Remember to uncheck this option when you are finished working with your application.
Now open the APK file on your Android device.
You should now have the application on your device. Remember that the application is configured to access the web application on your PC, so it will only run while the web server is started.
Remember to reset the 'Unknown Sources' security option in Settings when you are finished.