diff -r cad71a31b7fc -r e36f3802f733 srsf/vcommandhandler/src/vcommandservices.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/srsf/vcommandhandler/src/vcommandservices.cpp Wed Sep 01 12:29:17 2010 +0100 @@ -0,0 +1,794 @@ +/* +* Copyright (c) 2006-2007 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Implementation of VCommand Handler that links the VCommand +* app to VAS +* +*/ + + +#include "rubydebug.h" +#include "vcommandservices.h" +#include "contextprovider.h" +#include "tagnameset.h" +#include "taggetter.h" +#include "tagcommandconverter.h" +#include "tagplayer.h" +#include + +#include +#include +#include "vcommandinternalapi.h" + +// For CleanupResetAndDestroy +#include + + +/** +* Name of the global mutex used to serialize the operations +*/ +_LIT( KCommandHandlerLockName, "VCOMHNDLR" ); + +_LIT( KCommandHandlerPanic, "VCH" ); + +TAny* TVCommandTagPtrArrayKey::At( TInt aIndex ) const + { + MNssTag* tag ( NULL ); + TInt* key (NULL); + if ( aIndex==KIndexPtr ) + { + // iPtr is a pointer to the array element which is a pointer itself + // in case of the CArrayPtr + tag = *(MNssTag**)iPtr; + key = &tag->RRD()->IntArray()->At( 0 ); + } + else + { + // iBase is CBufBase* of the searched array + // CBufBase memory buffer stores pointers to MNssTag* (i.e. MNssTag**) + // iBase->Ptr returns TPtr8 to the desired pointer + // TPtr8.Ptr returns TUint8* of the element + // This element itself is a pointer to CPerson (i.e. MNssTag**) + tag = *(MNssTag**)iBase->Ptr( aIndex*sizeof( MNssTag** ) ).Ptr(); + key = &tag->RRD()->IntArray()->At( 0 ); + } + return key; + } + +// Construction/destruction +CVCommandService* CVCommandService::NewL( MVCommandHandlerObserver* aObserver ) + { + CVCommandService* self = new (ELeave) CVCommandService( aObserver ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + return self; + } + +CVCommandService::~CVCommandService() + { + RUBY_DEBUG0( "CVCommandService::~CVCommandService" ); + delete iGlobalTickWatcher; + iGlobalTickProperty.Close(); + delete iVasDbManager; + iCommands.ResetAndDestroy(); + delete iTagPlayer; + if( iLock.IsHeld() ) + { + RUBY_DEBUG0( "CVCommandService::~CVCommandService. Mutex is still held.\ + Signaling it" ); + iLock.Signal(); + } + iLock.Close(); + } + +// From the MVCService + +/** +* Synchronous. Service doesn't take the ownership, but makes an own copy +* Duplicates can be added +* @todo should we check for duplicates and leave with KErrAlreadyExists? +*/ +void CVCommandService::AddCommandL( const CVCommand& aCommand ) + { + RUBY_DEBUG_BLOCK( "CVCommandService::AddCommandL" ); + StartAtomicOperationLC(); + + RVCommandArray commands; + CleanupClosePushL( commands ); + commands.Append( &aCommand ); + DoAddCommandsL( commands ); + + CleanupStack::PopAndDestroy( &commands ); + CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation + } + +/** +* Synchronous. Service doesn't take the ownership, but makes an own copy +* Duplicates can be added +* @todo Should we check for duplicates and leave with KErrAlreadyExists? +* @param aIgnoreErrors If ETrue, even if some commands fail to be trained, +* handler adds all that are trainable +*/ +void CVCommandService::AddCommandsL( const RVCommandArray& aCommands, TBool aIgnoreErrors ) + { + RUBY_DEBUG_BLOCK( "CVCommandService::AddCommandsL" ); + StartAtomicOperationLC(); + DoAddCommandsL( aCommands, aIgnoreErrors ); + CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation + } + +void CVCommandService::DoAddCommandsL( const RVCommandArray& aCommands, TBool aIgnoreErrors ) + { + RUBY_DEBUG_BLOCKL( "CVCommandService::DoAddCommandsL" ); + MNssContext* context = GetVCommandContextLC(); + CTagNameSet* trainingPack = CTagNameSet::NewLC(); + + for( TInt i = 0; i < aCommands.Count(); i++ ) + { + CArrayPtr* tags = CreateTagsLC2( *aCommands[i], *context); + // tags array contains 1 tag if UserText is not defined + // tags array contains 2==KMaxTagsPerCommand tags if UserText is defined + __ASSERT_ALWAYS( tags->Count() > 0, User::Leave( KErrCorrupt ) ); + __ASSERT_ALWAYS( tags->Count() <= KMaxTagsPerCommand, User::Leave( KErrCorrupt ) ); + + // All the tags corresponding to the same VCommand get the same command id + SetVCommandIdL( *tags, NewCommandIdL() ); + trainingPack->AppendL( tags->At( 0 ), aCommands[i]->SpokenText() ); + if( tags->Count() == KMaxTagsPerCommand ) + { + // UserText tag is present and this command has been converted into two tags + trainingPack->AppendL( tags->At( 1 ), aCommands[i]->AlternativeSpokenText() ); + } + + CleanupStack::Pop( tags ); // ResetAndDestroy + CleanupStack::PopAndDestroy( tags ); // delete the CArrayPtr itself + } // for aCommands + + + CNssTrainingParameters* trainingParams = ConstructTrainingParametersLC(); + trainingPack->TrainAndSaveL( *trainingParams, *iTagManager, aIgnoreErrors ); + CleanupStack::PopAndDestroy( trainingParams ); + + CleanupStack::PopAndDestroy( trainingPack ); + CleanupStack::PopAndDestroy( context ); + RUBY_DEBUG0( "CVCH::DoAddCommandsL Context destroyed" ); + InvalidateCacheL(); + } + + +/** +* Synchronous. Removes the command from the system +* @param aCommand Reference to the command to be removed. Existing commands are +* compared against aCommand. All the matches are removed from VAS +* @leave KErrNotFound No such command +*/ +void CVCommandService::RemoveCommandL( const CVCommand& aCommand ) + { + RUBY_DEBUG_BLOCK( "CVCommandService::RemoveCommandL" ); + StartAtomicOperationLC(); + RVCommandArray array; + CleanupClosePushL( array ); + array.AppendL( &aCommand ); + DoRemoveCommandsL( array ); + CleanupStack::PopAndDestroy( &array ); + CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation + } + +/** +* Synchronous. +* @param aCommands Reference to the list of commands to be removed. Existing commands are +* compared against aCommands items. All the matches are removed from VAS +* @param aIgnoreErrors If ETrue, even if some commands fail to be removed, +* handler will remove as many as possible +*/ +void CVCommandService::RemoveCommandsL( const RVCommandArray& aCommands, TBool aIgnoreErrors ) + { + RUBY_DEBUG_BLOCK( "RemoveCommandsL" ); + StartAtomicOperationLC(); + DoRemoveCommandsL( aCommands, aIgnoreErrors ); + CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation + + } + +void CVCommandService::DoRemoveCommandsL( const RVCommandArray& aCommands, + TBool aIgnoreErrors ) + { + // for tracking the not found situations + RArray foundCommands; + CleanupClosePushL( foundCommands ); + for( TInt i=0; i < aCommands.Count(); i++ ) + { + foundCommands.AppendL( EFalse ); + } + + // Iterate over all the tags in the system. Whenever a tag can be converted + // to one of the aCommands, move it to the deletion bunch + CTagNameSet* removeTags = CTagNameSet::NewLC(); + MNssContext* context = GetVCommandContextLC(); + MNssTagListArray* tags = CVCommandService::GetTagListLC2( *context ); + for( TInt i = 0; i < tags->Count(); i++ ) + { + CVCommand* command = CTagCommandConverter::TagToCommandLC( *tags->At( i ) ); + for( TInt j = 0; j < aCommands.Count(); j++ ) + { + if( *command == *aCommands[j] ) + { + RUBY_DEBUG1( "CVCH::RemoveCommandsL Tag [%d] should be deleted", i ); + foundCommands[j] = ETrue; + removeTags->AppendL( tags->At( i ) ); + // ownership to removeTags, current tag to be removed from tags => index-- + tags->Delete( i ); + i--; + break; + } + } + CleanupStack::PopAndDestroy( command ); + } + + // Handle not founds + if( !aIgnoreErrors ) + { + for( TInt i=0; i < foundCommands.Count(); i++ ) + { + if( foundCommands[i] == EFalse ) + { + RUBY_ERROR1( "Command [%d] not found", i ); + User::Leave( KErrNotFound ); + } + } + } + // Delete all the discovered tags + removeTags->UntrainL( *iTagManager, aIgnoreErrors ); + + CleanupStack::PopAndDestroy( tags ); // ResetAndDestroy + CleanupStack::PopAndDestroy( tags ); // delete + CleanupStack::PopAndDestroy( context ); + CleanupStack::PopAndDestroy( removeTags ); + CleanupStack::PopAndDestroy( &foundCommands ); + InvalidateCacheL(); + } + +/** +* Synchronous +* @return an array of the commands in the system +* Ownership of the array is transfered to the client +* The returned CVCommandArray contains copies of all the +* commands currently stored in this handler +*/ +CVCommandArray* CVCommandService::ListCommandsL() + { + RUBY_DEBUG_BLOCK( "CVCommandService::ListCommandsL" ); + StartAtomicOperationLC(); + if( !IsCacheValidL() ) + { + RUBY_DEBUG0( "CVCommandService::ListCommandsL Cache is invalid. \ + Refresh the command list" ); + RefreshCommandListL(); + } + + CVCommandArray* result = CVCommandArray::NewL( iCommands ); + CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation + return result; + } + +// Private methods + +CVCommandService::CVCommandService( MVCommandHandlerObserver* aObserver ) : + iObserver( aObserver ) + { + // nothing + } + +void CVCommandService::ConstructL() + { + RUBY_DEBUG_BLOCKL( "CVCommandService::ConstructL" ); + TInt err = iLock.OpenGlobal( KCommandHandlerLockName ); + if( err != KErrNone ) + { + RUBY_DEBUG1( "CVCommandService::ConstructL failed to open mutex with [%d].\ + Creating a new one", err ); + err = iLock.CreateGlobal( KCommandHandlerLockName ); + RUBY_DEBUG1( "CVCommandService::ConstructL. Created mutex. Err [%d]", err ); + User::LeaveIfError( err ); + } + iTagPlayer = CTagPlayer::NewL(); + iVasDbManager = CNssVASDBMgr::NewL(); + iVasDbManager->InitializeL(); + iTagManager = iVasDbManager->GetTagMgr(); + TInt propertyErr = RProperty::Define( KSINDUID, ECommandHandlerTickKey, RProperty::EInt ); + if( ( propertyErr != KErrNone ) && ( propertyErr != KErrAlreadyExists ) ) + { + RUBY_ERROR1( "Attempt to define the ECommandHandlerTickKey flag failed with [%d]", + propertyErr ) + User::Leave( propertyErr ); + } + + RUBY_DEBUG0( "Attaching to the global tick property" ); + User::LeaveIfError( iGlobalTickProperty.Attach( KSINDUID, ECommandHandlerTickKey ) ); + // right after creation, local cache cannot be valid + TInt globalTickCount; + User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) ); + iLocalTickCount = globalTickCount - 1; + + iLastSetGlobalTickCount = iLocalTickCount; + iGlobalTickWatcher = CIntPropertyWatcher::NewL( KSINDUID, ECommandHandlerTickKey, *this ); + } + +/** +* Returns the VCommand context. Creates one on demand if none exists yet +*/ +MNssContext* CVCommandService::GetVCommandContextLC() const + { + RUBY_DEBUG0( "CVCommandService::GetVCommandContextL start" ); + CContextProvider* contextProvider = CContextProvider::NewLC( *iVasDbManager ); + MNssContext* result = contextProvider->GetVCommandContextLC(); + CleanupStack::Pop( result ); + CleanupStack::PopAndDestroy( contextProvider ); + CleanupDeletePushL( result ); + RUBY_DEBUG0( "CVCommandService::GetVCommandContextL end" ); + return result; + } + + +/** +* Constructs a set of new VAS tags from a command and a context. +* Does not save to VAS, just creates. Ownership on the created tag is +* passed to the client +*/ +CArrayPtr* CVCommandService::CreateTagsLC2( const CVCommand& aCommand, + const MNssContext& aContext ) + { + return CTagCommandConverter::CommandToTagsLC2( aCommand, aContext, *iTagManager ); + } + +/** +* Constructs training related parameters. Like language to be used +* @leave negated TNssSpeechItemResult +* @todo Is it ok to mix system-wide codes with the TNssSpeechItemResult codes +*/ +CNssTrainingParameters* CVCommandService::ConstructTrainingParametersLC() const + { + RUBY_DEBUG0( "CVCommandService::SetLanguageParamersL start" ); + RArray* languageArray = new (ELeave) RArray; + CleanupDeletePushL ( languageArray ); // Protect [allocated on the heap] array itself + CleanupClosePushL( *languageArray ); // Protect the array elements + + // Always generate a pronunciation in UI language + User::LeaveIfError( languageArray->Append( User::Language() ) ); + + // Add an extra language, which can be determined by language identification + User::LeaveIfError( languageArray->Append( ELangOther ) ); + + CNssTrainingParameters* trainingParams = CNssTrainingParameters::NewL(); + // No L-functions for some time, delay pushing parameters to the cleanup stack + + trainingParams->SetLanguages( languageArray ); + // No need to protect the language array anymore. + // From now on it is managed by the parameters + + // For array elements pushed via CleanupClosePushL + CleanupStack::Pop( languageArray ); + CleanupStack::Pop( languageArray ); + CleanupStack::PushL( trainingParams ); + RUBY_DEBUG0( "CVCommandService::SetLanguageParamersL end" ); + return trainingParams; + } + +/** +* Retrieves the list of tags for a given context. Synchronous. +* Leaves two objects on the cleanup stack! +* First PopAndDestroy will ResetAndDestroy content +* Second one will destroy the MNsstagListArray itself +*/ +MNssTagListArray* CVCommandService::GetTagListLC2( const MNssContext& aContext ) const + { + RUBY_DEBUG0( "CVCommandService::GetTagListLC start" ); + CTagGetter* tagGetter = CTagGetter::NewLC(); + MNssTagListArray* result = tagGetter->GetTagListLC2( *iTagManager, aContext ); + + CleanupStack::Pop( result ); + CleanupStack::Pop( result ); + CleanupStack::PopAndDestroy( tagGetter ); + CleanupDeletePushL( result ); + CleanupResetAndDestroy::PushL( *result ); + RUBY_DEBUG0( "CVCommandService::GetTagListLC end" ); + + return result; + } + +/** +* Retrieves the list of tags for a given context and voice command id. +* Synchronous. Leaves two objects on the cleanup stack! +* First PopAndDestroy will ResetAndDestroy content +* Second one will destroy the MNsstagListArray itself +*/ +MNssTagListArray* CVCommandService::GetTagListLC2( const MNssContext& aContext, + TInt aCommandId ) const + { + RUBY_DEBUG0( "CVCommandService::GetTagListLC start" ); + CTagGetter* tagGetter = CTagGetter::NewLC(); + MNssTagListArray* result = tagGetter->GetTagListLC2( *iTagManager, aContext, aCommandId ); + + CleanupStack::Pop( result ); + CleanupStack::Pop( result ); + CleanupStack::PopAndDestroy( tagGetter ); + CleanupDeletePushL( result ); + CleanupResetAndDestroy::PushL( *result ); + RUBY_DEBUG0( "CVCommandService::GetTagListLC end" ); + + return result; + } + +/** +* Resets iCommands and fills them with the commands from VAS +*/ +void CVCommandService::RefreshCommandListL() + { + RUBY_DEBUG_BLOCK( "CVCommandService::RefreshCommandListL" ); + const TInt KMinimalCommandId = 0; + iCommands.ResetAndDestroy(); + iMaxCommandId = KMinimalCommandId - 1; + MNssContext* context = GetVCommandContextLC(); + MNssTagListArray* tagList = GetTagListLC2( *context ); + RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Found [%d] tags", tagList->Count() ); + + // Sort array by command ids + TVCommandTagPtrArrayKey key( ECmpTInt ); + tagList->Sort( key ); + + // Array of tags related to the same VCommand + CArrayPtrFlat* commandArray = + new ( ELeave) CArrayPtrFlat( KMaxTagsPerCommand ); + CleanupStack::PushL( commandArray ); + // No need for CleanupResetAndDestroyPushL. + // MNssTag objects are not copied to the commandArray + + TInt currentId = KMinimalCommandId - 1; + + for( TInt i = 0; i < tagList->Count(); i++ ) + { + RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Processing tag [%d]", i ); + TInt commandId = tagList->At( i )->RRD()->IntArray()->At( 0 ); + if( currentId != commandId ) // new or first command + { + if( commandArray->Count() > 0 ) + { + RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Converting [%d] tags to command", commandArray->Count() ); + CStoredVCommand* command = CTagCommandConverter::TagsToCommandLC( *commandArray ); + RUBY_DEBUG2( "CVCommandService::RefreshCommandListL Converted successfully to command id [%d], command text [%S]", command->CommandId(), &(command->SpokenText() ) ); + iCommands.AppendL( command ); + CleanupStack::Pop( command ); + } + commandArray->Reset(); + currentId = commandId; + } + commandArray->AppendL( tagList->At( i ) ); + iMaxCommandId = Max( iMaxCommandId, commandId ); + } + + // Flush the last buffer + if( commandArray->Count() > 0 ) + { + RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Converting [%d] tags to command", commandArray->Count() ); + CStoredVCommand* command = CTagCommandConverter::TagsToCommandLC( *commandArray ); + RUBY_DEBUG2( "CVCommandService::RefreshCommandListL Converted successfully to command id [%d], command text [%S]", command->CommandId(), &(command->SpokenText() ) ); + iCommands.AppendL( command ); + CleanupStack::Pop( command ); + } + CleanupStack::PopAndDestroy( commandArray ); + CleanupStack::PopAndDestroy( tagList ); // ResetAndDestroy + CleanupStack::PopAndDestroy( tagList ); // delete + CleanupStack::PopAndDestroy( context ); + MarkCacheValidL(); + } + +/** +* Marks the iCommands as an invalid reflection of the VAS content. +* To make iCommands reflect the real VAS content a call to +* RefreshCommandsL is needed +*/ +void CVCommandService::InvalidateCacheL() + { + // Invalidating own cache means unvalidating the other instances' caches as well + TInt globalTickCount; + User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) ); + + iLocalTickCount = globalTickCount; + iLastSetGlobalTickCount = globalTickCount + 1; + User::LeaveIfError( iGlobalTickProperty.Set( iLastSetGlobalTickCount ) ); + } + +/** +* Marks the iCommands as the valid reflection of the VAS content. +*/ +void CVCommandService::MarkCacheValidL() + { + // Since all the cache comparison/update functions are executed under the + // StartAtomicOperation/EndAtomicOperation, we can be sure that nobody + // touched VAS and global tick count since the last IsCacheValidL + TInt globalTickCount; + User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) ); + + iLocalTickCount = globalTickCount; + } + +/** +* Tells if the iCommands reflect the content of the VAS DB correctly +* @return ETrue if the cache is valid, EFalse otherwise +*/ +TBool CVCommandService::IsCacheValidL() + { + RUBY_DEBUG_BLOCKL( "CVCommandService::IsCacheValidL" ); + TInt globalTickCount; + User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) ); + RUBY_DEBUG2( "CVCommandService::IsCacheValidL Local tick count [%d], \ + globalTickCount [%d]", iLocalTickCount, globalTickCount ); + return iLocalTickCount == globalTickCount; + } + +/** +* Generates new command id to be used for identifying voice tags, that +* belong to the same VCommand +*/ +TInt CVCommandService::NewCommandIdL() + { + if( !IsCacheValidL() ) + { + // Fills the max used command id + RefreshCommandListL(); + } + return ++iMaxCommandId; + } + +/** +* Sets the given aId as a VCommand id for all the tags in the given list +* This id is set as a first RRD int element of all the given MNssTags. +* If the RRD int array has no elements yet, one element is added +*/ +void CVCommandService::SetVCommandIdL( CArrayPtr& aTags, TInt aId ) const + { + // All the tags corresponding to the same VCommand get the same command id + + for( TInt i = 0; i < aTags.Count(); i++ ) + { + if( aTags[i]->RRD()->IntArray()->Count() > 0 ) + { + aTags[i]->RRD()->IntArray()->At( i ) = aId; + } + else + { + aTags[i]->RRD()->IntArray()->AppendL( aId ); + } + } + } + +/** +* Asynchronous +* Attempts to play back the text expected to be recognized. +* To be playable command has to be added to CVCommandHandler AND +* then retrieved back +* +* @param aHandler CVCommandHandler where the command is stored +* @param aPlayEventHandler Entity that handles the playback callbacks +* @see NssVasMPlayEventHandler.h +* +* @leave KErrBadHandle if the current command has not been retrieved +* from CVCommandHandler (i.e. was not trained for recognition) +* @leave KErrNotFound if this command cannot be found in aHandler +* @leave KErrNotReady @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult +* EVasUnexpectedRequest +* @leave KErrInUse @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult +* EVasInUse +* @leave KErrArgument @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult +* EVasInvalidParameter +* @leave KErrGeneral @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult +* EVasPlayFailed +*/ +void CVCommandService::PlaySpokenTextL( const CStoredVCommand& aCommand, + MNssPlayEventHandler& aPlayEventHandler ) + { + RUBY_DEBUG_BLOCK( "CVCommandService::PlaySpokenTextL" ); + __ASSERT_ALWAYS( !iPlaybackHandler, User::Leave( KErrInUse ) ); + StartAtomicOperationLC(); + iPlaybackHandler = &aPlayEventHandler; + MNssTag* playbackTag = TagByCommandIdTextL( aCommand.CommandId(), + aCommand.SpokenText() ); + if( !playbackTag ) + { + User::Leave( KErrNotFound ); + } + iTagPlayer->PlayTagL( playbackTag, *this ); + CleanupStack::Pop(); // EndOfAtomicOperation + } + +/** +* Asynchronous +* Plays back the user-specified alternative spoken text. +* Otherwise is identical to PlaySpokenTextL +* @see PlaySpokenTextL +* @leave KErrNotFound if this command cannot be found in aHandler of if +* it doesn't have a user-specified text +*/ +void CVCommandService::PlayAlternativeSpokenTextL( const CStoredVCommand& aCommand, + MNssPlayEventHandler& aPlayEventHandler ) + { + RUBY_DEBUG_BLOCK( "CVCommandService::PlayAlternativeSpokenTextL" ); + __ASSERT_ALWAYS( !iPlaybackHandler, User::Leave( KErrInUse ) ); + StartAtomicOperationLC(); + iPlaybackHandler = &aPlayEventHandler; + MNssTag* playbackTag = TagByCommandIdTextL( aCommand.CommandId(), + aCommand.AlternativeSpokenText() ); + if( !playbackTag ) + { + User::Leave( KErrNotFound ); + } + iTagPlayer->PlayTagL( playbackTag, *this ); + CleanupStack::Pop(); // EndOfAtomicOperation + } + +/** +* This method is not intended to be called directly. +* Use CVCommand::CancelPlaybackL instead +* @see CVCommand::CancelPlaybackL +* @see CStoredVCommand::CancelPlaybackL +*/ +void CVCommandService::CancelPlaybackL( const CStoredVCommand& /*aCommand*/ ) + { + // For calling EndOfAtomicOperation if a leave occurs + CreateAndPushEndAtomicCleanupItemL(); + + __ASSERT_ALWAYS( iPlaybackHandler, User::Leave( KErrNotReady ) ); + iTagPlayer->CancelPlaybackL(); + iPlaybackHandler = NULL; + CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation + } + +/** +* The HandlePlayStarted method is a virtual method implemented by the +* client and is called when play is started +* @param aDuration - the duration of the utterance data +*/ +void CVCommandService::HandlePlayStarted( TTimeIntervalMicroSeconds32 aDuration ) + { + __ASSERT_ALWAYS( iPlaybackHandler, User::Panic( KCommandHandlerPanic, KErrNotReady ) ); + iPlaybackHandler->HandlePlayStarted( aDuration ); + } + +/** +* The HandlePlayComplete method is a virtual method implemented by the +* client and is called when play is completed +* @param aErrorCode EVasErrorNone if playing was successfull +*/ +void CVCommandService::HandlePlayComplete( TNssPlayResult aErrorCode ) + { + __ASSERT_ALWAYS( iPlaybackHandler, User::Panic( KCommandHandlerPanic, KErrNotReady ) ); + + // End atomic operation before calling the observer not to block its potential + // VCommandHandler usage from the HandlePlayComplete + + // Before ending atomic operation, store the current event handler to the + // temporary variable to protect it against the potential change by the new + // PlaySpokenTextL call + MNssPlayEventHandler* handler = iPlaybackHandler; + iPlaybackHandler = NULL; + EndAtomicOperation(); + handler->HandlePlayComplete( aErrorCode ); + } + + +/** +* Searches vcommand tags for the tag, that corresponds to the given +* commandId and is trained against the given text +* @param aCommandId Command id to search for +* @param aText +* @return ANY of the voice tags corresponding to the given aCommandId +* and aText. Typically there will be one such tag only. However, +* if there are several it is not specified which one will be +* returned +* @return NULL if no satisfying tag is found +*/ +MNssTag* CVCommandService::TagByCommandIdTextL( TInt aCommandId, const TDesC& aText ) + { + RUBY_DEBUG_BLOCKL( "CVCommandService::TagByCommandIdTextL" ); + MNssContext* context = GetVCommandContextLC(); + MNssTagListArray* tagList = GetTagListLC2( *context, aCommandId ); + // A command can have up to two voice tags. We need to locate the one, + // that has been trained against a given text + MNssTag* tag (NULL); + for( TInt i = 0; i < tagList->Count(); i++ ) + { + if( tagList->At(i)->SpeechItem()->RawText() == aText ) + { + tag = tagList->At(i); + // deletes pointer only. tag will be returned to the calling party + tagList->Delete( i ); + break; + } // if + } // for + CleanupStack::PopAndDestroy( tagList ); // ResetAndDestroy + CleanupStack::PopAndDestroy( tagList ); // delete + CleanupStack::PopAndDestroy( context ); + + return tag; + } + +/** +* Starts the section of the code, that can be simultaneously executed +* only by a single instance of this class. Other instances have to wait +* Same as starting the Java "synchronized" method. Puts the function +* EndAtomicOperation to cleanup stack to make the use leave safe. Subsequent +* call to CleanupStack::PopAndDestroy will run EndAtomicOperation. +*/ +void CVCommandService::StartAtomicOperationLC() + { + RUBY_DEBUG0( "CVCommandService::StartAtomicOperation start" ); + if ( iLock.IsHeld() ) + { + User::Leave( KErrLocked ); + } + + iLock.Wait(); + + CreateAndPushEndAtomicCleanupItemL(); + + RUBY_DEBUG0( "CVCommandService::StartAtomicOperation end" ); + } + +/** +* Ends the section of the code, that can be simultaneously executed +* only by a single instance of this class. Other instances have to wait +* Same as exiting the Java "synchronized" method +* @leave KErrNotReady if atomic operation hasn't been started +*/ +void CVCommandService::EndAtomicOperation() + { + RUBY_DEBUG0( "CVCommandService::EndAtomicOperation start" ); + __ASSERT_ALWAYS( iLock.IsHeld(), User::Panic( KCommandHandlerPanic, KErrNotReady ) ); + iLock.Signal(); + RUBY_DEBUG0( "CVCommandService::EndAtomicOperation end" ); + } + +/** +* Runs the EndAtomicOperation when calling CleanupStack::PopAndDestroy after +* a call to StartAtomicOperationLC +*/ +void CVCommandService::CleanupEndAtomicOperation( TAny* aService ) + { + CVCommandService* service = static_cast(aService); + service->EndAtomicOperation(); + } + +/** +* Creates and pushes to cleanup stack a TCleanupItem that calls +* EndAtomicOperation when CleanupStack::PopAndDestroy is called +*/ +void CVCommandService::CreateAndPushEndAtomicCleanupItemL() + { + TCleanupItem item( CleanupEndAtomicOperation, this ); + CleanupStack::PushL( item ); + } + +void CVCommandService::IntValueChanged( TInt aNewValue ) + { + RUBY_DEBUG2( "iLocalTickCount [%d], aNewValue [%d]", iLocalTickCount, aNewValue ); + if( aNewValue != iLastSetGlobalTickCount ) + { + if( iObserver ) + { + iObserver->CommandSetChanged(); + } // if iObserver + } // if local cache is out of date + } + +//End of file