Thursday, July 14, 2011

Blackberry app for Pace Bus of Chicago - Entry 2

I wasted the whole of yesterday in watching movies, eating and sleeping and so did not work on the app at all. Today I worked some quality time on the app and overhauled the map UI interface.

I had implemented a working prototype of map feature but there were couple of problems. First thing was the Map was drawn directly from the main PaceBus screen. That was a very bad design and I thought it would be simple for now, for development sake and hence went with it. But going through that design I was having trouble bringing the route information into the Map and passing it around the timer and HTTP connection thread to fetch the route's data. So this morning the first thing I worked was to make a Map Launch Screen containing drop down box and a button to launch Map. The drop down had the routes populated from the HTTP route fetch thread.The user is required to select the route from the drop down, click on Goto Map button to open map. This way I had the selected route in the Launch screen and was able to pass it to any module as need arises.
The next problem was to handle the timer. The idea is to have a timer set up which will go off every 15 secs (say) and everytime it triggers, the timer thread would run a fetch thread to obtain the new coordinates from the PaceBus web server and pass it to map to update the locations. The change in UI helped me in this also. Previously I had problem in deciding from where to start the timer and from where to terminate it. Now it was clear that I should start the timer when user clicks on the Goto Map button. So on button click, map is invoked and timer is run.

/**
* @see FieldChangeListener#fieldChanged
* Note: Due to the reason this is only back door, when user changes _kbUseAreaBasedProbability field the threshold will be set to default.
*/
public void fieldChanged( Field field, int context ) {
if(field == _gotoMapButtonField)
{
//Map button clicked. Get the route number selected
int routeSelectedIndex = _routeNumberChoiceField.getSelectedIndex();
strRouteNo= (String) _routeNumberChoiceField.getChoice(routeSelectedIndex);
if(strRouteNo == null || PaceConstants.SELECT_ROUTE_FROM_LIST.equals(strRouteNo)){
Dialog.inform("Select a route from the list.");
return;
}
//fetch the bus coordinates for that route
Thread fetchThread = new Thread(){
public void run() {
HTTPDemo.getInstance().fetchPage(PaceConstants.MAP_COORDINATES_PAGE+"?u="+strRouteNo, PaceConstants.EXTRACT_COORDINATES);
}};
fetchThread.start();
//In the meantime, run the spinner
UiApplication.getUiApplication().pushModalScreen(spinner);
//start a timer to timely fetch bus coordinates
timer = new Timer();
task = new TimerTask()
{
public void run()
{
//on timeout, fetch data only if Map application is running
boolean MapRunning = false;
ApplicationDescriptor descriptors[] = ApplicationManager.getApplicationManager().getVisibleApplications();
for(int i=0; i
if(descriptors[i].getName().equals("BlackBerry Maps")|| descriptors[i].getName().equals("Maps")){
Thread fetchThread = new Thread(){
public void run() {
HTTPDemo.getInstance().fetchPage(PaceConstants.MAP_COORDINATES_PAGE+"?u="+strRouteNo, PaceConstants.EXTRACT_COORDINATES);
}};
fetchThread.start();
MapRunning = true;
break;
}
}
if(!MapRunning){
//cancel timer if Map application is no more running
timer.cancel();
}
}
};
//after a delay of 5 secs, the timer will timeout every 20 secs
timer.scheduleAtFixedRate(task, 5000, 20000);
PaceBusApp.log("MapLaunchScreen(): Goto Map button event is handled. Timer started.");
}

}

The code above describes the action taken when Goto Map button is clicked. It also contains the timer initialization code. Everytime the button is clicked a fresh timer is created. One more modification done was that the timer would fetch coordinates only when there is a Map application currently running. If there is no Map application running, then the timer will cancel itself.
The MapLaunchScreen implements GlobalEventListener so it gets a notification when the coordinate fetch is complete. In the event handler, if the data received from fetch is null that means there are no buses running at this time and this is also the place where we should stop the timer (remember the timer was started the moment Map launch button was clicked) plus we should invoke a default map which doesn't have any location info. Just a generic map. Makes sense.

public void eventOccurred(long guid, int data0, int data1, Object object0,
Object object1) {

if( guid == PaceConstants.GUID_PACE_FETCH_BUS_COORDINATES && data0 == PaceConstants.EXTRACT_COORDINATES)
{
PaceBusApp.log("PaceBusApp: MapLaunchScreen - eventOccured(): GUID_PACE_FETCH_BUS_COORDINATES");
//if spinner is running, close it
if(this.spinner != null){
this.spinner.close();
this.spinner = null;
}
//save the object received
if(object0 == null){
Dialog.inform("There are no buses running this time.");
//remove timer
if(timer!=null)
timer.cancel();
//show default map
Invoke.invokeApplication(Invoke.APP_TYPE_MAPS, new MapsArguments());
return;
}
CoordinatesData = (BusCoordinatesDb)object0;
//display the coordinates on map
handleMap();
PaceBusApp.log("PaceBusApp: MapLaunchScreen - exiting eventOccured()");
}
}

The code above is the event handler which is invoked everytime the coordinates have been fetched.
handleMap is a simple function which forms the location string from the coordinates obtained and call the Map application with that argument.


public void handleMap(){
//return if we did not receive valid coordinates
if(CoordinatesData == null)
return;
Vector lt = CoordinatesData.getLatitude();
Vector lg = CoordinatesData.getLongitude();
//prepare the location string
stringBuffer = null;
stringBuffer = new StringBuffer("");
for (int i = 0; i
int g = (int) ((Double.parseDouble((String)lg.elementAt(i))) * 100000);
int t = (int) ((Double.parseDouble((String)lt.elementAt(i))) * 100000);
stringBuffer.append("");
}
stringBuffer.append("");
//launch the map application. If Map is already running, the locations will be updated
Invoke.invokeApplication(Invoke.APP_TYPE_MAPS, new MapsArguments( MapsArguments.ARG_LOCATION_DOCUMENT, stringBuffer.toString()));
}

The map code idea is borrowed from BlackBerryMapsDemo project under the sample applications. This much should suffice for now. Next time I'll try to fix some UI related bugs in the module developed so far.

KD

Stumble Upon Toolbar

No comments:

Post a Comment