# HG changeset patch # User Sebastian Brannstrom # Date 1278671640 -3600 # Node ID cc1be3797632a1dae3b8b59f44c9c4703f8625fd # Parent f42c9de433f22522fd64737a0b6b6b87217c5dfa First implementation of asynchronous feed parser diff -r f42c9de433f2 -r cc1be3797632 application/group/Podcast.mmp --- a/application/group/Podcast.mmp Wed Jul 07 23:27:49 2010 +0100 +++ b/application/group/Podcast.mmp Fri Jul 09 11:34:00 2010 +0100 @@ -106,5 +106,4 @@ LIBRARY ws32.lib LIBRARY hlplch.lib LIBRARY bitgdi.lib -LIBRARY apmime.lib // TDataType LIBRARY commonui.lib // CDocumentHandler diff -r f42c9de433f2 -r cc1be3797632 application/src/PodcastFeedView.cpp --- a/application/src/PodcastFeedView.cpp Wed Jul 07 23:27:49 2010 +0100 +++ b/application/src/PodcastFeedView.cpp Fri Jul 09 11:34:00 2010 +0100 @@ -230,6 +230,7 @@ void CPodcastFeedView::FeedDownloadStartedL(TFeedState /*aState*/, TUint aFeedUid) { + DP("FeedDownloadStartedL"); // Update status text iUpdatingRunning = ETrue; UpdateFeedInfoStatusL(aFeedUid, ETrue); @@ -239,6 +240,7 @@ void CPodcastFeedView::FeedDownloadFinishedL(TFeedState aState,TUint aFeedUid, TInt aError) { + DP("FeedDownloadFinishedL"); switch(aError) { case KErrCouldNotConnect: @@ -255,6 +257,7 @@ break; } UpdateFeedInfoStatusL(aFeedUid, EFalse); + UpdateToolbar(); } void CPodcastFeedView::UpdateFeedInfoStatusL(TUint aFeedUid, TBool aIsUpdating) diff -r f42c9de433f2 -r cc1be3797632 engine/inc/FeedEngine.h --- a/engine/inc/FeedEngine.h Wed Jul 07 23:27:49 2010 +0100 +++ b/engine/inc/FeedEngine.h Fri Jul 09 11:34:00 2010 +0100 @@ -47,11 +47,13 @@ const TInt KMaxLineLength = 4096; const TInt KMaxSearchString = 30; -enum TClientState { +enum TFeedEngineState { EIdle, - EUpdatingFeed, - EUpdatingImage, - ESearching + EDownloadingFeed, + EParsingFeed, + EDownloadingImage, + ESearching, + EParsingOpml }; class CFeedEngine : public CBase, public MHttpClientObserver, public MFeedParserObserver @@ -81,7 +83,7 @@ /** * Returns the current internal state of the feed engine4 */ - IMPORT_C TClientState ClientState(); + IMPORT_C TFeedEngineState ClientState(); /** * Returns the current updating client UID if clientstate is != ENotUpdateing @@ -118,7 +120,6 @@ void UpdateNextFeedL(); void NotifyOpmlParsingCompleteL(TInt aError, TUint aNumFeedsAdded); - private: void DBLoadFeedsL(); @@ -132,7 +133,7 @@ private: CHttpClient* iFeedClient; - TClientState iClientState; + TFeedEngineState iEngineState; CFeedTimer iFeedTimer; CPodcastModel& iPodcastModel; @@ -152,8 +153,8 @@ RArray iObservers; // new feeds only add one show to download list when auto downloading - TBool newFeed; - TUint showsAdded; + TBool iNewFeed; + TUint iShowsAdded; sqlite3& iDB; diff -r f42c9de433f2 -r cc1be3797632 engine/inc/FeedParser.h --- a/engine/inc/FeedParser.h Wed Jul 07 23:27:49 2010 +0100 +++ b/engine/inc/FeedParser.h Fri Jul 09 11:34:00 2010 +0100 @@ -25,6 +25,7 @@ #include // for Xml::MContentHandler #include + _LIT(KTagItem, "item"); _LIT(KTagTitle, "title"); _LIT(KTagImage, "image"); @@ -62,15 +63,21 @@ const int KBufferLength = 1024; -class CFeedParser : public CBase, public Xml::MContentHandler +class CFeedParser : public CActive, public Xml::MContentHandler { public: + static CFeedParser* NewL(MFeedParserObserver& aCallbacks, RFs& aFs); + static CFeedParser* NewLC(MFeedParserObserver& aCallbacks, RFs& aFs); + virtual ~CFeedParser(); + +private: CFeedParser(MFeedParserObserver& aCallbacks, RFs& aFs); - virtual ~CFeedParser(); + void ConstructL(); public: void ParseFeedL(const TFileName &feedFileName, CFeedInfo *item, TUint aMaxItems); - + void ParseFeedAoL(const TFileName &feedFileName, CFeedInfo *item, TUint aMaxItems); + public: // from MContentHandler void OnStartDocumentL(const Xml::RDocumentParameters& aDocParam, TInt aErrorCode); void OnEndDocumentL(TInt aErrorCode); @@ -85,6 +92,12 @@ void OnError(TInt aErrorCode); TAny* GetExtendedInterface(const TInt32 aUid); CFeedInfo& ActiveFeed(); + +private: + void DoCancel(); + void RunL(); + TInt RunError(TInt aError); + private: MFeedParserObserver& iCallbacks; TFeedState iFeedState; @@ -98,7 +111,13 @@ TUint iItemsParsed; TBool iStoppedParsing; TEncoding iEncoding; + RFs& iRfs; + Xml::CParser* iParser; + HBufC8* iXmlBuffer; + RFile iFile; + TFileName iFileName; + }; #endif diff -r f42c9de433f2 -r cc1be3797632 engine/src/FeedEngine.cpp --- a/engine/src/FeedEngine.cpp Wed Jul 07 23:27:49 2010 +0100 +++ b/engine/src/FeedEngine.cpp Fri Jul 09 11:34:00 2010 +0100 @@ -41,7 +41,7 @@ void CFeedEngine::ConstructL() { - iParser = new (ELeave) CFeedParser(*this, iPodcastModel.FsSession()); + iParser = CFeedParser::NewLC(*this, iPodcastModel.FsSession()); iFeedClient = CHttpClient::NewL(iPodcastModel, *this); iFeedTimer.ConstructL(); @@ -80,11 +80,12 @@ TRAP_IGNORE(ImportFeedsL(importFile)); } + CleanupStack::Pop(iParser); RunFeedTimer(); } CFeedEngine::CFeedEngine(CPodcastModel& aPodcastModel) - : iClientState(EIdle), + : iEngineState(EIdle), iFeedTimer(this), iPodcastModel(aPodcastModel), iDB(*aPodcastModel.DB()) @@ -107,9 +108,9 @@ /** * Returns the current internal state of the feed engine4 */ -EXPORT_C TClientState CFeedEngine::ClientState() +EXPORT_C TFeedEngineState CFeedEngine::ClientState() { - return iClientState; + return iEngineState; } @@ -144,7 +145,7 @@ EXPORT_C void CFeedEngine::UpdateAllFeedsL(TBool aAutoUpdate) { - if (iClientState != EIdle) + if (iEngineState != EIdle) { User::Leave(KErrInUse); } @@ -169,7 +170,7 @@ EXPORT_C void CFeedEngine::CancelUpdateAllFeeds() { - if(iClientState != EIdle) + if(iEngineState != EIdle) { iCancelRequested = ETrue; iFeedsUpdating.Reset(); @@ -181,7 +182,7 @@ { DP1("UpdateNextFeed. %d feeds left to update", iFeedsUpdating.Count()); - if (iClientState != EIdle) + if (iEngineState != EIdle) { User::Leave(KErrInUse); } @@ -216,7 +217,7 @@ } else { - iClientState = EIdle; + iEngineState = EIdle; for (TInt i=0;iFeedUpdateAllCompleteL(iAutoUpdatedInitiator?MFeedEngineObserver::EFeedAutoUpdate:MFeedEngineObserver::EFeedManualUpdate)); @@ -239,10 +240,10 @@ if (iActiveFeed->LastUpdated() == 0) { - newFeed = ETrue; + iNewFeed = ETrue; } - showsAdded = 0; + iShowsAdded = 0; iActiveFeed->SetLastError(KErrNone); DBUpdateFeedL(*iActiveFeed); @@ -254,7 +255,7 @@ _LIT(KFileNameFormat, "%lu.xml"); iUpdatingFeedFileName.AppendFormat(KFileNameFormat, aFeedUid); - iClientState = EUpdatingFeed; + iEngineState = EDownloadingFeed; for (TInt i=0;iGetL(aFeedInfo->ImageUrl(), filePath, ETrue)) { - iClientState = EUpdatingImage; + iEngineState = EDownloadingImage; } } @@ -527,6 +528,7 @@ TBuf title; title.Copy(item->Title()); item->SetTitleL(title); // if this leaves we are out of memory + CompleteL(NULL, KErrNone); } @@ -555,26 +557,32 @@ void CFeedEngine::CompleteL(CHttpClient* /*aClient*/, TInt aError) { - DP2("CFeedEngine::CompleteL BEGIN, iClientState=%d, aSuccessful=%d", iClientState, aError); + DP2("CFeedEngine::CompleteL BEGIN, iEngineState=%d, aSuccessful=%d", iEngineState, aError); - switch(iClientState) + switch(iEngineState) { - case EUpdatingFeed: + case EDownloadingFeed: { - iClientState = EIdle; switch (aError) { case KErrCancel: { iFeedsUpdating.Reset(); + iEngineState = EIdle; } break; case KErrCouldNotConnect: iFeedsUpdating.Reset(); + iEngineState = EIdle; break; default: { - if (!iCancelRequested) { + if (iCancelRequested) + { + iEngineState = EIdle; + } + else + { iActiveFeed->SetLastError(aError); TTime time; time.HomeTime(); @@ -585,60 +593,64 @@ // Parse the feed. We need to trap this call since it could leave and then // the whole update chain will be broken // change client state - TRAPD(parserErr, iParser->ParseFeedL(iUpdatingFeedFileName, iActiveFeed, iPodcastModel.SettingsEngine().MaxListItems())); + TRAPD(parserErr, iParser->ParseFeedAoL(iUpdatingFeedFileName, iActiveFeed, iPodcastModel.SettingsEngine().MaxListItems())); if(parserErr) { // we do not need to any special action on this error. iActiveFeed->SetLastError(parserErr); DP1("CFeedEngine::Complete()\t Failed to parse feed. Leave with error code=%d", parserErr); + iEngineState = EIdle; } - - // delete the downloaded XML file as it is no longer needed - BaflUtils::DeleteFile(iPodcastModel.FsSession(),iUpdatingFeedFileName); - - // if the feed has specified a image url. download it if we dont already have it - if((iActiveFeed->ImageUrl().Length() > 0)) + else { - if ( (iActiveFeed->ImageFileName().Length() == 0) || - (iActiveFeed->ImageFileName().Length() > 0 && - !BaflUtils::FileExists(iPodcastModel.FsSession(), - iActiveFeed->ImageFileName()) ) - ) - { - TRAPD(error, GetFeedImageL(iActiveFeed)); - if (error) - { - // we have failed in a very early stage to fetch the image. - // continue with next Feed update - iActiveFeed->SetLastError(parserErr); - iClientState = EIdle; - } - } + iEngineState = EParsingFeed; } } else { // even if it fails, this will allow us to move on - iClientState = EIdle; + iEngineState = EIdle; } } - iCancelRequested = EFalse; }break; } - DBUpdateFeedL(*iActiveFeed); NotifyFeedUpdateComplete(iActiveFeed->Uid(), aError); - - // we will wait until the image has been downloaded to start the next feed update. - if (iClientState == EIdle) + } + break; + case EParsingFeed: + { + // if the feed has specified a image url. download it if we dont already have it + if((iActiveFeed->ImageUrl().Length() > 0)) { - UpdateNextFeedL(); + if ( (iActiveFeed->ImageFileName().Length() == 0) || + (iActiveFeed->ImageFileName().Length() > 0 && + !BaflUtils::FileExists(iPodcastModel.FsSession(), + iActiveFeed->ImageFileName()) ) + ) + { + TRAPD(error, GetFeedImageL(iActiveFeed)); + if (error) + { + // we have failed in a very early stage to fetch the image. + // continue with next Feed update + iActiveFeed->SetLastError(error); + iEngineState = EIdle; + NotifyFeedUpdateComplete(iActiveFeed->Uid(), error); + } + } + else + { + iEngineState = EIdle; + NotifyFeedUpdateComplete(iActiveFeed->Uid(), aError); + } } - }break; - case EUpdatingImage: + } + break; + case EDownloadingImage: { // change client state to not updating - iClientState = EIdle; + iEngineState = EIdle; if(aError == KErrNone) { // now the image has been downloaded, so we set it again in the FeedInfo to start @@ -647,13 +659,11 @@ TRAP_IGNORE(iActiveFeed->SetImageFileNameL(*fileNameCopy, &iPodcastModel)); CleanupStack::PopAndDestroy(fileNameCopy); } - DBUpdateFeedL(*iActiveFeed); NotifyFeedUpdateComplete(iActiveFeed->Uid(), aError); - UpdateNextFeedL(); }break; case ESearching: { - iClientState = EIdle; + iEngineState = EIdle; DP2("Search complete, results in %S with error %d", &iSearchResultsFileName, aError); if(aError == KErrNone) @@ -685,7 +695,16 @@ void CFeedEngine::NotifyFeedUpdateComplete(TInt aFeedUid, TInt aError) { - DP("CFeedEngine::NotifyFeedUpdateComplete"); + DP("CFeedEngine::NotifyFeedUpdateComplete"); + + DBUpdateFeedL(*iActiveFeed); + + // we will wait until the image has been downloaded to start the next feed update. + if (iEngineState == EIdle) + { + UpdateNextFeedL(); + } + for (TInt i=0;iFeedDownloadFinishedL(MFeedEngineObserver::EFeedAutoUpdate, aFeedUid, aError)); @@ -1084,7 +1103,7 @@ { DP1("FeedEngine::SearchForFeedL BEGIN, aSearchString=%S", &aSearchString); - if (iClientState != EIdle) { + if (iEngineState != EIdle) { User::Leave(KErrInUse); } TBuf ssBuf; @@ -1107,11 +1126,11 @@ // run search if(iFeedClient->GetL(*url, iSearchResultsFileName, iPodcastModel.SettingsEngine().SpecificIAP())) { - iClientState = ESearching; + iEngineState = ESearching; } else { - iClientState = EIdle; + iEngineState = EIdle; User::Leave(KErrAbort); } diff -r f42c9de433f2 -r cc1be3797632 engine/src/FeedParser.cpp --- a/engine/src/FeedParser.cpp Wed Jul 07 23:27:49 2010 +0100 +++ b/engine/src/FeedParser.cpp Fri Jul 09 11:34:00 2010 +0100 @@ -28,24 +28,73 @@ #include "podcastutils.h" using namespace Xml; + const TInt KMaxParseBuffer = 1024; const TInt KMaxStringBuffer = 100; +const TInt KFileBufferSize = 1024; // buffer size for file reading +_LIT8( KXmlMimeType, "text/xml" ); -CFeedParser::CFeedParser(MFeedParserObserver& aCallbacks, RFs& aFs) : iCallbacks(aCallbacks), iRfs(aFs) +CFeedParser* CFeedParser::NewL(MFeedParserObserver& aCallbacks, RFs& aFs) + { + CFeedParser* self = CFeedParser::NewLC(aCallbacks, aFs); + CleanupStack::Pop(); + return self; + } + +CFeedParser* CFeedParser::NewLC(MFeedParserObserver& aCallbacks, RFs& aFs) + { + CFeedParser* self = new ( ELeave ) CFeedParser(aCallbacks, aFs); + CleanupStack::PushL( self); + self->ConstructL(); + return self; + } + +CFeedParser::CFeedParser(MFeedParserObserver& aCallbacks, RFs& aFs) : CActive(EPriorityLow), + iCallbacks(aCallbacks), iRfs(aFs) { + CActiveScheduler::Add( this); } CFeedParser::~CFeedParser() { } +void CFeedParser::ParseFeedAoL(const TFileName &feedFileName, CFeedInfo *item, TUint aMaxItems) + { + DP("CFeedParser::ParseFeedAoL BEGIN"); + // Remember to cancel any outstanding request first. + if ( IsActive()) + { + Cancel(); + } + + iActiveFeed = item; + iFeedState = EStateRoot; + iActiveShow = NULL; + iItemsParsed = 0; + iMaxItems = aMaxItems; + iStoppedParsing = EFalse; + iEncoding = ELatin1; + iFileName.Copy(feedFileName); + + User::LeaveIfError( iFile.Open( iRfs, feedFileName, EFileRead)); + + delete iXmlBuffer; + iXmlBuffer = 0; + iXmlBuffer = HBufC8::NewL( KFileBufferSize); + TPtr8 bufferPtr( iXmlBuffer->Des()); + iFile.Read( bufferPtr, KFileBufferSize, iStatus); + SetActive(); + + iParser->ParseBeginL(); + DP("CFeedParser::ParseFeedAoL END"); + } + + void CFeedParser::ParseFeedL(const TFileName &feedFileName, CFeedInfo *info, TUint aMaxItems) { - //DP1("ParseFeedL BEGIN: %S", &feedFileName); - - _LIT8(KXmlMimeType, "text/xml"); - // Contruct the parser object - CParser* parser = CParser::NewLC(KXmlMimeType, *this); + DP1("ParseFeedL BEGIN: %S", &feedFileName); + iActiveFeed = info; iFeedState = EStateRoot; iActiveShow = NULL; @@ -53,12 +102,14 @@ iMaxItems = aMaxItems; iStoppedParsing = EFalse; iEncoding = ELatin1; + iFileName.Copy(feedFileName); + CParser* parser = CParser::NewLC(KXmlMimeType, *this); ParseL(*parser, iRfs, feedFileName); CleanupStack::PopAndDestroy(parser); - //DP("ParseFeedL END"); + DP("ParseFeedL END"); } // from MContentHandler @@ -81,7 +132,7 @@ void CFeedParser::OnEndDocumentL(TInt /*aErrorCode*/) { - //DP("OnEndDocumentL()"); + DP("OnEndDocumentL()"); iCallbacks.ParsingCompleteL(iActiveFeed); } @@ -94,7 +145,7 @@ TBuf str; str.Copy(aElement.LocalName().DesC()); - //DP2("OnStartElementL START state=%d, element=%S", iFeedState, &str); + DP2("OnStartElementL START state=%d, element=%S", iFeedState, &str); iBuffer.Zero(); switch (iFeedState) { case EStateRoot: @@ -219,7 +270,7 @@ TBuf str; str.Copy(aElement.LocalName().DesC()); - //DP2("OnEndElementL START state=%d, element=%S", iFeedState, &str); + DP2("OnEndElementL START state=%d, element=%S", iFeedState, &str); switch (iFeedState) { case EStateChannelTitle: @@ -449,3 +500,52 @@ { return *iActiveFeed; } + +void CFeedParser::ConstructL() + { + iParser = CParser::NewL( KXmlMimeType, *this); + } + +void CFeedParser::DoCancel() + { + + } + +void CFeedParser::RunL() + { + if ( KErrNone == iStatus.Int()) + { + if ( iXmlBuffer->Length()== 0) // end of the file. + { + iParser->ParseEndL(); + iFile.Close(); + BaflUtils::DeleteFile(iRfs,iFileName); + + delete iXmlBuffer; + iXmlBuffer = 0; + } + else + {// Otherwise, we continue reading the next chunk of the XML file. + // Parse the next "part" of the XML document. + iParser->ParseL( *iXmlBuffer); + + // Read the next chunk of the file. + TPtr8 bufferPtr( iXmlBuffer->Des()); + iFile.Read( bufferPtr, KFileBufferSize, iStatus); + + // Don't forget to call this... :) + SetActive(); + } + } + else + { + // Do something if error happens. + } + } + +TInt CFeedParser::RunError(TInt aError) + { + + } + +