This article will walk developers through the steps for developing a small S60 application that sends an HTTP query to a Web service to fetch landmarks information. The application UI is created using the Carbide UI designer. Example code is not intended to be of commercial quality and is provided for educational purposes only. A basic understanding of Carbide.c++ and S60 C++ is assumed.
Developers will need a valid Developer Certificate from the Symbian Signed programme to sign the installation file if they want to run the application on a real device.
The application UI consists of a single view, which is populated by a listbox control used for showing the landmarks data from a Web query.
Your new (still empty) UI design opens up in Carbide after creating the project.
EAknCmdExit.
Your About menu item event handler should be similar to this:
/**
* Handle the selected event.
* @param aCommand the command id invoked
* @return ETrue if the command was handled, EFalse if not
*/
TBool CMyExampleListBoxView::HandleAboutMenuItemSelectedL( TInt aCommand )
{
RunNote1L();
return ETrue;
}
Most of the application logic is handled by a separate class, containing the following public interface:
static CGPSPOIsManager* NewL();
static CGPSPOIsManager* NewLC();
virtual ~CGPSPOIsManager();
/**
* Appends data from the Web query to the data buffer
*
* @return void
* @param aData 8 bit descriptor result of HTTP query
*/
void AppendL( const TDesC8& aData );
/**
* Resets the landmark array and empties the data buffer
*
* @return void
*/
void Reset();
/**
* Handle to the array containing pointers to landmark objects
*
* @return void
*/
RArray< CPosLandmark* > Items();
/**
* Creates landmark items from the search results.
* Calls the parsing functions to parse the comma separated data
*
* @return void
*/
void CreateItemsL();
/**
* Adds the search result landmarks to the landmark database
*
* @return TInt
*/
TInt AddToDbL();
Source and header files for this class need to be added to the Carbide project manually. The main functionality is explained below, while the link to the full source code of the class is linked to in this article.
void CGPSPOIsManager::ConstructL()
{
// Open Landmarks database
iLmDb = CPosLandmarkDatabase::OpenL();
...
void CGPSPOIsManager::AppendL( const TDesC8& aData )
{
// append the received data to the data buffer
TInt newLength = iDatabuf.Length() + aData.Length();
if (iDatabuf.MaxLength() < newLength)
{
iDatabuf.ReAllocL( newLength );
}
iDatabuf.Append( aData );
}
The GPS Waypoints client API provides landmark objects formatted as:
#id, lat,lon,name,desc,location,(speed limit) ,(bearing),type,
Fields of an individual item are separated by commas and items are separated by a line break.
The sample application logic contains parsing functionality to handle the received data:
void CGPSPOIsManager::CreateItemsL()
{
// create landmarks from the received data
// parse until all items have called parseiteml
TInt ret = 0;
while ( ret != KErrNotFound )
{
ret = ParseItemL( ret );
}
}
TInt CGPSPOIsManager::ParseItemL( TInt aPosInBuf )
{
if ( aPosInBuf >= iDatabuf.Length() )
return KErrNotFound;
return ParseCSVItemL( aPosInBuf );
}
TInt CGPSPOIsManager::ParseCSVItemL( TInt aPosInBuf )
{
// GPS waypoints provides data as comma separated values...
// ...with line breaks between the result items
// work with remaining data
TPtrC8 remainder = iDatabuf.Right( iDatabuf.Length() - aPosInBuf );
// locate a line change
TInt ret = remainder.Locate( '\n' );
// no more line changes (new items) left
if ( ret == KErrNotFound )
return ret;
// single line without the line change
TPtrC8 singleline = remainder.Left( ret );
// resume parsing after the \n
TInt newindex = aPosInBuf + ret + 1;
// comment lines start with #
if ( remainder.Left( 1 ).Compare( KMyComment ) == 0 )
return newindex;
...
// pass singleline to item constructor
// item should parse and create itself
// ...add item to itemarray
//TBool eos = EFalse;
TInt linepos = 0;
iItemIndex = EMyLmId;
iLat = 0;
iLong = 0;
CPosLandmark* landmark = CPosLandmark::NewL();
while ( linepos >= 0 )
{
// if linepos == -1 => end of line
// else continue at linepos
// => increment linepos beyond the found ,
linepos = ParseCSVFieldL( linepos, singleline );
// increase itemindex after setting the value of a field in a landmark
SetLandMarkItemL( landmark );
iItemIndex++;
}
// add landmark to items
//landmark->SetPartialL( 0 );
iItems.Append( landmark );
return newindex;
}
TInt CGPSPOIsManager::ParseCSVFieldL( TInt aLinePos, TPtrC8& aLine )
{
// Get an attribute of a search result item to iToken
TPtrC8 remainder = aLine.Right( aLine.Length() - aLinePos );
TInt pos = remainder.Locate( TChar(',') );
// no more fields to read
if ( pos < 0 )
return pos;
// Copy the single token
if ( iToken.Length() < pos + 1 )
iToken.ReAllocL( pos + 1 );
iToken.Copy( remainder.Left( pos ) );
// if the last ',' was the last character in the line
TInt ret = aLinePos + pos + 1;
if ( ret >= aLine.Length() )
return -1;
else
return ret;
}
TInt CGPSPOIsManager::SetLandMarkItemL( CPosLandmark* aLandmark )
{
// only name, desc and coordinates handled in this case
switch ( iItemIndex )
{
case EMyLmName:
{
aLandmark->SetLandmarkNameL( iToken );
break;
}
case EMyLmDesc:
{
aLandmark->SetLandmarkDescriptionL( iToken );
break;
}
case EMyLmLat:
{
TLex lex( iToken );
lex.Val( iLat );
break;
}
case EMyLmLon:
{
TLex lex( iToken );
lex.Val( iLong );
TCoordinate coord( iLat, iLong );
TLocality lmlocality( coord, 10 );
aLandmark->SetPositionL( lmlocality );
break;
}
case EMyLmLocation:
{
break;
}
case EMyLmSpeed:
case EMyLmBearing:
case EMyLmType:
case EMyLmId:
default:
break;
}
return 0;
}
TInt CGPSPOIsManager::AddToDbL()
{
TInt ret = 0;
// add landmarks to the database
for ( TInt i = 0; i < iItems.Count(); i++ )
{
iLmDb->AddLandmarkL( *iItems[i] );
ret++;
}
return ret;
}
// header file
#include "CGPSPOIsManager.h"
...
CGPSPOIsManager* iModel;
// source file
CMyExampleAppUi::~CMyExampleAppUi()
{
// [[[ begin generated region: do not modify [Generated Contents]
// ]]] end generated region [Generated Contents]
delete iModel;
iModel = NULL;
}
// [[[ begin generated function: do not modify
void CMyExampleAppUi::InitializeContainersL()
{
iMyExampleListBoxView = CMyExampleListBoxView::NewL();
// this added after UI code generation
iMyExampleListBoxView->SetAppModel( iModel );
...
void CMyExampleAppUi::ConstructL()
{
iModel = CGPSPOIsManager::NewL();
...
// header file
class CGPSPOIsManager;
...
CGPSPOIsManager* iModel;
...
// source file
void CMyExampleListBoxView::SetAppModel( CGPSPOIsManager* aModel )
{
iModel = aModel;
}
The location and landmarks-related functionality requires some link libraries as well as some capabilities to be added to project definitions. This is probably a good time to tweak the settings.
With the application logic mostly in place, you can configure your HTTP-query behaviour.
Carbide UI Designer generates code that eases the pain of issuing and handling the Web queries. The code is generated to the CWebClientEngine class, which by default uses the view issuing the queries as an observer to the progress of the queries.
The HTTP query is initiated from the Search... menu item. Carbide has generated a function placeholder to the listbox class for handling that menu command.
TBool CMyExampleListBoxView::HandleSearch_closest_camerasMenuItemSelectedL( TInt aCommand )
{
// TODO: implement selected event handler
return ETrue;
}
The query format for getting traffic camera items from GPS Waypoints client API is:
http://www.gps-waypoints.net/gps/gwnapi/get_closest_waypoints.php?api_key=[YOURAPIKEY]&lat=[LATCOORDINATE]&lon=[LONCOORDINATE]&type=100006&distance=[RADIUS]&limit=[LIMIT]
You can get an API key by registering to the Web site. The sample application limits the search results to five items and uses 10,000 km as the search radius.
We used a test key for the API calls: NNVALVOGTYHXAQC.
The sample application contains the position-fetching implementation inside the listbox class. You should probably implement this functionality inside your application model and use active objects to handle the async position-fetching call.
// header file
#include <lbs.h>
...
RPositionServer iPosServer;
RPositioner iPositioner;
//source file
#include <lbspositioninfo.h>
#include "CGPSPOIsManager.h"
...
// destructor
iPositioner.Close();
iPosServer.Close();
...
// ConstructL
TInt err = iPosServer.Connect();
TPositionModuleId moduleid;
err = iPosServer.GetDefaultModuleId( moduleid );
err = iPositioner.Open( iPosServer, moduleid );
if ( err == KErrNone )
{
_LIT( KMyData, "MyExample");
iPositioner.SetRequestor(
CRequestorBase::ERequestorService,
CRequestorBase::EFormatApplication,
KMyData );
}
The sample code obtains the last-known position in the event handler of the Search... menu item.
/**
* Handle the selected event.
* @param aCommand the command id invoked
* @return ETrue if the command was handled, EFalse if not
*/
TBool CGPSPOIsSearchViewView::HandleSurroundingPOIsSelectedL( TInt aCommand )
{
// The ResetListL method has been added
// to empty the listbox
iMyExampleListBox->ResetListL();
iMyExampleListBox->ListBox()->SetCurrentItemIndex(0);
_LIT8( KSearchURL, "http://www.gps-waypoints.net/gps/gwnapi/get_closest_waypoints.php?api_key=NNVALVOGTYHXAQC&lat=%f&lon=%f&type=100006&distance=10000&limit=5" );
// get the last known position
TRequestStatus status;
TPositionInfo posinfo;
// use an AO in a real life app
iPositioner.GetLastKnownPosition( posinfo, status );
User::WaitForRequest( status );
TPosition position;
posinfo.GetPosition( position );
...
...
// create the search url
RBuf8 rbuf;
rbuf.CreateMax( KSearchURL().Length()*2 );
// format the search string to include the position info
rbuf.Format( KSearchURL, position.Latitude(), position.Longitude() );
iModel->Reset();
// generated by Carbide => encapsulates the HTTP-query
IssueHTTPGetL( &rbuf );
rbuf.Close();
return ETrue;
}
The Web Client encapsulation will notify its observer (our listbox view class) on the progress of the query.
In the UI design phase, we indicated that we are interested in handling the bodyReceived and transactionSucceeded events.
When a part of the HTTP-query result body has been received, you want to add it to the data buffer maintained by your model.
/**
* ClientBodyReceivedL()
* Called when a part of the HTTP body is received.
* @param aBodyData: Part of the body data received. (e.g. part of
* the received HTML page)
*/
void CMyExampleListBoxView::ClientBodyReceivedL(
CWebClientEngine& anEngine,
const TDesC8& aBodyData )
{
// [[[ begin generated region: do not modify [Generated Code]
HandleWebClient1BodyReceivedL( anEngine, aBodyData );
// ]]] end generated region [Generated Code]
}
/**
* Handle the bodyReceived event.
*/
void CMyExampleListBoxView::HandleWebClient1BodyReceivedL(
CWebClientEngine& /*anEngine*/,
const TDesC8& aBodyData)
{
iModel->AppendL( aBodyData );
}
When the HTTP transaction has completed, ask your model to parse the data and construct the landmark objects.
/**
* ClientTransactionSucceededL()
* Called to notify that a transaction completed successfully
* See TTransactionEvent::ESucceeded
*/
void CMyExampleListBoxView::ClientTransactionSucceededL(
CWebClientEngine& anEngine )
{
// [[[ begin generated region: do not modify [Generated Code]
HandleWebClient1TransactionSucceededL( anEngine );
// ]]] end generated region [Generated Code]
}
/**
* Handle the transactionSucceeded event.
*/
void CMyExampleListBoxView::HandleWebClient1TransactionSucceededL(
CWebClientEngine& /*anEngine*/ )
{
// show a note to indicate transaction completion
// the note was defined in the UI design phase and
// Carbide has generated wrapper code for it
RunTransactionCompleteL();
// ask the model to parse the data and create landmarks
iModel->CreateItemsL();
iMyExampleListBox->ResetListL();
RArray< CPosLandmark* > items = iModel->Items();
// populate listbox here
for ( TInt i = 0; i < items.Count(); i++ )
{
TBuf<256> buf;
TPtrC lmname;
TPtrC lmdesc;
CPosLandmark* lmark = items[ i ];
lmark->GetLandmarkName( lmname );
lmark->GetLandmarkDescription( lmdesc );
// These functions generated by Carbide
iMyExampleListBox->CreateListBoxItemL( buf, i+1,
lmname, lmdesc );
iMyExampleListBox->AddListBoxItemL( iMyExampleListBox->ListBox(), buf );
}
iMyExampleListBox->ListBox()->DrawDeferred();
}
A menu item for adding the query results to the landmarks database was defined in the UI design phase.
Modify the event handler method to add the landmarks.
/**
* Handle the selected event.
* @param aCommand the command id invoked
* @return ETrue if the command was handled, EFalse if not
*/
TBool CMyExampleListBoxView::HandleAdd_to_landmarksMenuItemSelectedL( TInt aCommand )
{
if ( iModel->Items().Count() == 0 )
RunNote2L();
else
{
iMyExampleListBox->ResetListL();
iMyExampleListBox->ListBox()->SetCurrentItemIndex(0);
iMyExampleListBox->ListBox()->DrawDeferred();
TInt ret = iModel->AddToDbL();
iModel->Reset();
}
return ETrue;
}
You should now be able to build, run, or debug the application. As a reminder, you will need a valid Developer Certificate to sign the application if you want to run it on a real device.
No related wiki articles found