Skip to content
Vreixo González Caneda edited this page Aug 16, 2014 · 5 revisions

Packages

Classes

The more important classes of this Android app are the Activities, Fragments and Asynchronous Tasks.

Activities

Following Android recent guidelines this app uses fragments, so the activities don't hold the most part of the app but are still very important for inter communication (well, SettingsActivity is really more than that, but is not possible to use a Fragment for this and maintain actual level of backwards compatibility).

My Activity

This is the core activity of the app, where resides the most part of the app's logic, MainFragment, will be attached to this activity.

Is used for intercommunication in between Fragments attached to it (is not a good practice to have direct Fragments communication) and activities (also external activities that will be requested with an intent).

To handle all of this intercommunication uses different mechanisms:

  • Implements a listener for communicate the basic UI Fragments MainFragment and DirectionListFragment.
  • In onActivityResult retrieves information form received intents and makes necessary calls.
  • Provides public functions to call MainFragment methods (this activity will always retrieve and instance of that fragment).

SettingsActivity

Holds all the functionality related to the settings menu. Some of the logic is provided by the SDK as it extends PreferenceActivity. As was said before PreferenceFragment is not used to allow maintain compatibility with Froyo (API level 8).

Contains the handlers for settings values changes. Some important functionality here is for the case when the "custom server URL" is changed, when it makes a call to an [Asynchronous task] ((http://developer.android.com/reference/android/os/AsyncTask.html)) to directly check if it's correct to avoid a non working server to be set, so also have a callback to retrieve task information.

Some complex callbacks are directly in MainFragment because it uses the listener for SharedPreferences change, that is triggered when a preference is changed. Also in some cases information is added to the intent that will receive MyActivity to call from there MainFragment if the action can't be directly related to a preference change.

Fragments

Hold the basic functionality of the app with all the work related to the UI and the app's logic.

MainFragment

This fragment corresponds with the principal view of the UI with the map, start/end text boxes and main screen UI buttons.

The main functions here are to control UI listeners and callbacks from asynchronous tasks, SettingsActivity (can be through MyAcitivty), DateTimeDialog (through MyAcitivty), Maps API V2 (connection and behavior ones) and asynchronous tasks.

Most of the information obtained from the server will be updated and displayed here and the information for the requests to external servers and triggers for them are located here. Also this is the entry point to the app and all other screens will come from here (settings with SettingsActivity and step-by-step directions with DirectionListFragment) and finally return here.

As this class stores most of the app information it take cares of save and restore everything on app restarts.

DirectionListFragment

Activates and manages the step-by-step directions.

This class needs to obtain the route information from the MainFragment and also passes back information if the "selected itinerary" is changed which is the only mutable information here. This intercommunication is through the OnFragmentListener that is implemented by MyActivity, obtaining this way all the itineraries and the index of the selected one.

This class uses some custom components to have a functional UI that are in util package:

  • ExpandableListFragment: base class for DirectionListFragment which contains UI specific features for the fragment.
  • DirectionExpandableListAdapter: utility class that manages each step of information in the itinerary list.
  • ItineraryDecrypt: utility class for break and organize itinerary information.

DateTimeDialog

Manages the dialog to choose date and time for the trip.

Uses Pickers, UI standard controls to offer the date and time in a consistent way with the rest of the system, date and time integrated in the same dialog.

The date and time are obtained from a bundle that is passed in the MainFragment when the dialog is created. Updated values are passed back to the MainFragment, calling a function of the MainFragment through a function in MyActivity (that is also attached to DateTimeDialog fragment) when OK button is pressed. As values to initialize the dialog come from MainFragment dialog can be easily initialized when is open for a second time to the values set on the first one.

Asynchronous tasks

These classes extend from [AsyncTask] (http://developer.android.com/reference/android/os/AsyncTask.html) and allow the app to process requests to external servers that will take some time without blocking the UI. Implemented in conjunction with listeners to provide a mechanism to callback with the results to the caller class when the task is finished.

As this tasks offer, in general, some UI elements that need the activity to be run, they could fail if the activity is restarted (as the old activity will be destroyed) if they try to use the activity after the restart. To avoid this, what is passed is a [weak reference] (http://developer.android.com/reference/java/lang/ref/WeakReference.html) and the activity won't be used if it was destroyed (also we avoid possible [context leaks] (http://android-developers.blogspot.com.es/2009/01/avoiding-memory-leaks.html)). Hence, the task will be destroyed if the app is restarted (for example on a device rotation) but is assured that they won't be side effects or miss-behaviors.

TripRequest

Requests a trip with all the parameters the user entered through the UI to the back-end [OpenTripPlanner RestAPI] (https://github.com/openplans/OpenTripPlanner/wiki#basic-otp-architecture) of the selected server.

This request is performed from the MainFragment and is triggered when the user clicks on the search button. As the fragment is set as the listener, when the request is done is informed of the generated trip if the task was successful.

The task handles itself a processing dialog to inform the user the request is being processed and the possible error messages.

MetadataRequest

Retrieves from the selected OTP server the available transit modes and the server bounds. For the moment only the bounds are used but in the future transit modes could be offered matching the server in use.

Metadata is only requested for custom servers, because in the [servers list Google doc] (https://docs.google.com/spreadsheet/ccc?key=0AgWy8ujaGosCdDhxTC04cUZNeHo0eGFBQTBpU2dxN0E&hl=en_US&authkey=CK-H__IP#gid=0) this is information is already present, so with the call to obtain the server list we avoid to make a second one requesting metadata.

This request is performed by the MainFragment (actively being instantiated or through the callback for changed settings) and the result is also returned to it. When the bounds are received they are saved to the database for future uses, painted in the map and set to control server limits and geocoding.

ServerChecker

Used for two different functions, may work without callback:

  • Check if a custom server is working OK: tries to plan a trip (without parameters) and checks if obtains [HTTP Success request code] (http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_Success). A callback is defined to warn the caller that is working and is safe to use this server.

  • Obtain and display server information: obtains information for an already known and stored server. For this the server is passed and his information is showed along with a message showing if a [HTTP Success request code] (http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_Success) was obtained when trying to plan a trip (as in the previous defined function of the task).

In the second case a progress information is shown to give the user some feedback that the call is working and in the second case just [toasts] (http://developer.android.com/guide/topics/ui/notifiers/toasts.html) are shown when the task is finished informing of the result.

ServerSelector

Obtains a server list and auto detects a server if this preference is checked and a valid location is passed.

The server list would be obtained from the database unless is forced to download (when the user updates server list on setting menu), if the database is empty, off course, is also downloaded.

If the location is valid, the preference for autodetect server is activated and is in the range of a server in the servers list (the custom server stays out of this), this server is automatically chosen. If not, a list with all the servers is offered to the user including an entry to insert an URL for a "custom server".

The entry for custom server in the servers list will trigger the ServerChecker and the "custom server" will only be activated if it's working, otherwise a [toast] (http://developer.android.com/guide/topics/ui/notifiers/toasts.html) will be shown displaying the error. Once the server is activated a metadata request will be triggered to have the same information that is available for a server from the list.

Once a server is selected this is passed back to the callback, in this case MainFragment, and the map is centered on the server, boundaries are drawn and a marker is set for the origin location if the user is not in the server area. If the server is a custom server this behavior is triggered in the callback for metadata request, because until that moment the necessary information won't be available.

OTPGeocoding

This task will process the address doing work of geocoding, reverse-geocoding and Point-of-Interest search. This is because [Android Geocoder] (http://developer.android.com/reference/android/location/Geocoder.html) allows for the same method to receive a location or an address and it will complete the missing part and the same happens with [MapQuest Nominatim ] (http://developer.mapquest.com/web/products/open/nominatim) and [Google Places] (https://developers.google.com/places/documentation/), in fact these last ones can do more specific POI search, answering correctly to request of "airport" for example with close airports. Android Geocoder does not behave very well with some POI search (as for example airport example) but also processes some of these kind of searches (for the previous example it will work perfectly for [airport codes] (http://en.wikipedia.org/wiki/International_Air_Transport_Association_airport_code) as "JFK").

The passed information (location coordinates or some text) is queried using first Android Geocoder if this option is activated in settings menu, to have quick results. If there are no results or the option is disactivated it queries [MapQuest Nominatim] (http://developer.mapquest.com/web/products/open/nominatim) or [Google Places] (https://developers.google.com/places/documentation/) as configured. For example interstate highways don't produce results on reverse geocoding as was discussed in [issue #33] (https://github.com/CUTR-at-USF/OpenTripPlanner-for-Android/issues/33).

To obtain [MapQuest Nominatim] (http://developer.mapquest.com/web/products/open/nominatim) and [Google Places] (https://developers.google.com/places/documentation/) results, a JSON object is request and is parsed using [org.json.] (http://www.json.org/java/index.html) classes.

When there more than one possible result a list is offered to the user, with the different fields of the results organized, using addresses.

This task is processed without showing any dialog if it's successful to be less intrusive and don't block the user when he is interacting with the UI.