|
1 /* |
|
2 * Copyright (c) 2006-2007 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Implementation of VCommand Handler that links the VCommand |
|
15 * app to VAS |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 #include "rubydebug.h" |
|
21 #include "vcommandservices.h" |
|
22 #include "contextprovider.h" |
|
23 #include "tagnameset.h" |
|
24 #include "taggetter.h" |
|
25 #include "tagcommandconverter.h" |
|
26 #include "tagplayer.h" |
|
27 #include <nssvascoreconstant.h> |
|
28 |
|
29 #include <s32mem.h> |
|
30 #include <nssvascvasdbmgr.h> |
|
31 #include "vcommandinternalapi.h" |
|
32 |
|
33 // For CleanupResetAndDestroy |
|
34 #include <mmfcontrollerpluginresolver.h> |
|
35 |
|
36 |
|
37 /** |
|
38 * Name of the global mutex used to serialize the operations |
|
39 */ |
|
40 _LIT( KCommandHandlerLockName, "VCOMHNDLR" ); |
|
41 |
|
42 _LIT( KCommandHandlerPanic, "VCH" ); |
|
43 |
|
44 TAny* TVCommandTagPtrArrayKey::At( TInt aIndex ) const |
|
45 { |
|
46 MNssTag* tag ( NULL ); |
|
47 TInt* key (NULL); |
|
48 if ( aIndex==KIndexPtr ) |
|
49 { |
|
50 // iPtr is a pointer to the array element which is a pointer itself |
|
51 // in case of the CArrayPtr |
|
52 tag = *(MNssTag**)iPtr; |
|
53 key = &tag->RRD()->IntArray()->At( 0 ); |
|
54 } |
|
55 else |
|
56 { |
|
57 // iBase is CBufBase* of the searched array |
|
58 // CBufBase memory buffer stores pointers to MNssTag* (i.e. MNssTag**) |
|
59 // iBase->Ptr returns TPtr8 to the desired pointer |
|
60 // TPtr8.Ptr returns TUint8* of the element |
|
61 // This element itself is a pointer to CPerson (i.e. MNssTag**) |
|
62 tag = *(MNssTag**)iBase->Ptr( aIndex*sizeof( MNssTag** ) ).Ptr(); |
|
63 key = &tag->RRD()->IntArray()->At( 0 ); |
|
64 } |
|
65 return key; |
|
66 } |
|
67 |
|
68 // Construction/destruction |
|
69 CVCommandService* CVCommandService::NewL( MVCommandHandlerObserver* aObserver ) |
|
70 { |
|
71 CVCommandService* self = new (ELeave) CVCommandService( aObserver ); |
|
72 CleanupStack::PushL( self ); |
|
73 self->ConstructL(); |
|
74 CleanupStack::Pop( self ); |
|
75 return self; |
|
76 } |
|
77 |
|
78 CVCommandService::~CVCommandService() |
|
79 { |
|
80 RUBY_DEBUG0( "CVCommandService::~CVCommandService" ); |
|
81 delete iGlobalTickWatcher; |
|
82 iGlobalTickProperty.Close(); |
|
83 delete iVasDbManager; |
|
84 iCommands.ResetAndDestroy(); |
|
85 delete iTagPlayer; |
|
86 if( iLock.IsHeld() ) |
|
87 { |
|
88 RUBY_DEBUG0( "CVCommandService::~CVCommandService. Mutex is still held.\ |
|
89 Signaling it" ); |
|
90 iLock.Signal(); |
|
91 } |
|
92 iLock.Close(); |
|
93 } |
|
94 |
|
95 // From the MVCService |
|
96 |
|
97 /** |
|
98 * Synchronous. Service doesn't take the ownership, but makes an own copy |
|
99 * Duplicates can be added |
|
100 * @todo should we check for duplicates and leave with KErrAlreadyExists? |
|
101 */ |
|
102 void CVCommandService::AddCommandL( const CVCommand& aCommand ) |
|
103 { |
|
104 RUBY_DEBUG_BLOCK( "CVCommandService::AddCommandL" ); |
|
105 StartAtomicOperationLC(); |
|
106 |
|
107 RVCommandArray commands; |
|
108 CleanupClosePushL( commands ); |
|
109 commands.Append( &aCommand ); |
|
110 DoAddCommandsL( commands ); |
|
111 |
|
112 CleanupStack::PopAndDestroy( &commands ); |
|
113 CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation |
|
114 } |
|
115 |
|
116 /** |
|
117 * Synchronous. Service doesn't take the ownership, but makes an own copy |
|
118 * Duplicates can be added |
|
119 * @todo Should we check for duplicates and leave with KErrAlreadyExists? |
|
120 * @param aIgnoreErrors If ETrue, even if some commands fail to be trained, |
|
121 * handler adds all that are trainable |
|
122 */ |
|
123 void CVCommandService::AddCommandsL( const RVCommandArray& aCommands, TBool aIgnoreErrors ) |
|
124 { |
|
125 RUBY_DEBUG_BLOCK( "CVCommandService::AddCommandsL" ); |
|
126 StartAtomicOperationLC(); |
|
127 DoAddCommandsL( aCommands, aIgnoreErrors ); |
|
128 CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation |
|
129 } |
|
130 |
|
131 void CVCommandService::DoAddCommandsL( const RVCommandArray& aCommands, TBool aIgnoreErrors ) |
|
132 { |
|
133 RUBY_DEBUG_BLOCKL( "CVCommandService::DoAddCommandsL" ); |
|
134 MNssContext* context = GetVCommandContextLC(); |
|
135 CTagNameSet* trainingPack = CTagNameSet::NewLC(); |
|
136 |
|
137 for( TInt i = 0; i < aCommands.Count(); i++ ) |
|
138 { |
|
139 CArrayPtr<MNssTag>* tags = CreateTagsLC2( *aCommands[i], *context); |
|
140 // tags array contains 1 tag if UserText is not defined |
|
141 // tags array contains 2==KMaxTagsPerCommand tags if UserText is defined |
|
142 __ASSERT_ALWAYS( tags->Count() > 0, User::Leave( KErrCorrupt ) ); |
|
143 __ASSERT_ALWAYS( tags->Count() <= KMaxTagsPerCommand, User::Leave( KErrCorrupt ) ); |
|
144 |
|
145 // All the tags corresponding to the same VCommand get the same command id |
|
146 SetVCommandIdL( *tags, NewCommandIdL() ); |
|
147 trainingPack->AppendL( tags->At( 0 ), aCommands[i]->SpokenText() ); |
|
148 if( tags->Count() == KMaxTagsPerCommand ) |
|
149 { |
|
150 // UserText tag is present and this command has been converted into two tags |
|
151 trainingPack->AppendL( tags->At( 1 ), aCommands[i]->AlternativeSpokenText() ); |
|
152 } |
|
153 |
|
154 CleanupStack::Pop( tags ); // ResetAndDestroy |
|
155 CleanupStack::PopAndDestroy( tags ); // delete the CArrayPtr itself |
|
156 } // for aCommands |
|
157 |
|
158 |
|
159 CNssTrainingParameters* trainingParams = ConstructTrainingParametersLC(); |
|
160 trainingPack->TrainAndSaveL( *trainingParams, *iTagManager, aIgnoreErrors ); |
|
161 CleanupStack::PopAndDestroy( trainingParams ); |
|
162 |
|
163 CleanupStack::PopAndDestroy( trainingPack ); |
|
164 CleanupStack::PopAndDestroy( context ); |
|
165 RUBY_DEBUG0( "CVCH::DoAddCommandsL Context destroyed" ); |
|
166 InvalidateCacheL(); |
|
167 } |
|
168 |
|
169 |
|
170 /** |
|
171 * Synchronous. Removes the command from the system |
|
172 * @param aCommand Reference to the command to be removed. Existing commands are |
|
173 * compared against aCommand. All the matches are removed from VAS |
|
174 * @leave KErrNotFound No such command |
|
175 */ |
|
176 void CVCommandService::RemoveCommandL( const CVCommand& aCommand ) |
|
177 { |
|
178 RUBY_DEBUG_BLOCK( "CVCommandService::RemoveCommandL" ); |
|
179 StartAtomicOperationLC(); |
|
180 RVCommandArray array; |
|
181 CleanupClosePushL( array ); |
|
182 array.AppendL( &aCommand ); |
|
183 DoRemoveCommandsL( array ); |
|
184 CleanupStack::PopAndDestroy( &array ); |
|
185 CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation |
|
186 } |
|
187 |
|
188 /** |
|
189 * Synchronous. |
|
190 * @param aCommands Reference to the list of commands to be removed. Existing commands are |
|
191 * compared against aCommands items. All the matches are removed from VAS |
|
192 * @param aIgnoreErrors If ETrue, even if some commands fail to be removed, |
|
193 * handler will remove as many as possible |
|
194 */ |
|
195 void CVCommandService::RemoveCommandsL( const RVCommandArray& aCommands, TBool aIgnoreErrors ) |
|
196 { |
|
197 RUBY_DEBUG_BLOCK( "RemoveCommandsL" ); |
|
198 StartAtomicOperationLC(); |
|
199 DoRemoveCommandsL( aCommands, aIgnoreErrors ); |
|
200 CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation |
|
201 |
|
202 } |
|
203 |
|
204 void CVCommandService::DoRemoveCommandsL( const RVCommandArray& aCommands, |
|
205 TBool aIgnoreErrors ) |
|
206 { |
|
207 // for tracking the not found situations |
|
208 RArray<TBool> foundCommands; |
|
209 CleanupClosePushL( foundCommands ); |
|
210 for( TInt i=0; i < aCommands.Count(); i++ ) |
|
211 { |
|
212 foundCommands.AppendL( EFalse ); |
|
213 } |
|
214 |
|
215 // Iterate over all the tags in the system. Whenever a tag can be converted |
|
216 // to one of the aCommands, move it to the deletion bunch |
|
217 CTagNameSet* removeTags = CTagNameSet::NewLC(); |
|
218 MNssContext* context = GetVCommandContextLC(); |
|
219 MNssTagListArray* tags = CVCommandService::GetTagListLC2( *context ); |
|
220 for( TInt i = 0; i < tags->Count(); i++ ) |
|
221 { |
|
222 CVCommand* command = CTagCommandConverter::TagToCommandLC( *tags->At( i ) ); |
|
223 for( TInt j = 0; j < aCommands.Count(); j++ ) |
|
224 { |
|
225 if( *command == *aCommands[j] ) |
|
226 { |
|
227 RUBY_DEBUG1( "CVCH::RemoveCommandsL Tag [%d] should be deleted", i ); |
|
228 foundCommands[j] = ETrue; |
|
229 removeTags->AppendL( tags->At( i ) ); |
|
230 // ownership to removeTags, current tag to be removed from tags => index-- |
|
231 tags->Delete( i ); |
|
232 i--; |
|
233 break; |
|
234 } |
|
235 } |
|
236 CleanupStack::PopAndDestroy( command ); |
|
237 } |
|
238 |
|
239 // Handle not founds |
|
240 if( !aIgnoreErrors ) |
|
241 { |
|
242 for( TInt i=0; i < foundCommands.Count(); i++ ) |
|
243 { |
|
244 if( foundCommands[i] == EFalse ) |
|
245 { |
|
246 RUBY_ERROR1( "Command [%d] not found", i ); |
|
247 User::Leave( KErrNotFound ); |
|
248 } |
|
249 } |
|
250 } |
|
251 // Delete all the discovered tags |
|
252 removeTags->UntrainL( *iTagManager, aIgnoreErrors ); |
|
253 |
|
254 CleanupStack::PopAndDestroy( tags ); // ResetAndDestroy |
|
255 CleanupStack::PopAndDestroy( tags ); // delete |
|
256 CleanupStack::PopAndDestroy( context ); |
|
257 CleanupStack::PopAndDestroy( removeTags ); |
|
258 CleanupStack::PopAndDestroy( &foundCommands ); |
|
259 InvalidateCacheL(); |
|
260 } |
|
261 |
|
262 /** |
|
263 * Synchronous |
|
264 * @return an array of the commands in the system |
|
265 * Ownership of the array is transfered to the client |
|
266 * The returned CVCommandArray contains copies of all the |
|
267 * commands currently stored in this handler |
|
268 */ |
|
269 CVCommandArray* CVCommandService::ListCommandsL() |
|
270 { |
|
271 RUBY_DEBUG_BLOCK( "CVCommandService::ListCommandsL" ); |
|
272 StartAtomicOperationLC(); |
|
273 if( !IsCacheValidL() ) |
|
274 { |
|
275 RUBY_DEBUG0( "CVCommandService::ListCommandsL Cache is invalid. \ |
|
276 Refresh the command list" ); |
|
277 RefreshCommandListL(); |
|
278 } |
|
279 |
|
280 CVCommandArray* result = CVCommandArray::NewL( iCommands ); |
|
281 CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation |
|
282 return result; |
|
283 } |
|
284 |
|
285 // Private methods |
|
286 |
|
287 CVCommandService::CVCommandService( MVCommandHandlerObserver* aObserver ) : |
|
288 iObserver( aObserver ) |
|
289 { |
|
290 // nothing |
|
291 } |
|
292 |
|
293 void CVCommandService::ConstructL() |
|
294 { |
|
295 RUBY_DEBUG_BLOCKL( "CVCommandService::ConstructL" ); |
|
296 TInt err = iLock.OpenGlobal( KCommandHandlerLockName ); |
|
297 if( err != KErrNone ) |
|
298 { |
|
299 RUBY_DEBUG1( "CVCommandService::ConstructL failed to open mutex with [%d].\ |
|
300 Creating a new one", err ); |
|
301 err = iLock.CreateGlobal( KCommandHandlerLockName ); |
|
302 RUBY_DEBUG1( "CVCommandService::ConstructL. Created mutex. Err [%d]", err ); |
|
303 User::LeaveIfError( err ); |
|
304 } |
|
305 iTagPlayer = CTagPlayer::NewL(); |
|
306 iVasDbManager = CNssVASDBMgr::NewL(); |
|
307 iVasDbManager->InitializeL(); |
|
308 iTagManager = iVasDbManager->GetTagMgr(); |
|
309 TInt propertyErr = RProperty::Define( KSINDUID, ECommandHandlerTickKey, RProperty::EInt ); |
|
310 if( ( propertyErr != KErrNone ) && ( propertyErr != KErrAlreadyExists ) ) |
|
311 { |
|
312 RUBY_ERROR1( "Attempt to define the ECommandHandlerTickKey flag failed with [%d]", |
|
313 propertyErr ) |
|
314 User::Leave( propertyErr ); |
|
315 } |
|
316 |
|
317 RUBY_DEBUG0( "Attaching to the global tick property" ); |
|
318 User::LeaveIfError( iGlobalTickProperty.Attach( KSINDUID, ECommandHandlerTickKey ) ); |
|
319 // right after creation, local cache cannot be valid |
|
320 TInt globalTickCount; |
|
321 User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) ); |
|
322 iLocalTickCount = globalTickCount - 1; |
|
323 |
|
324 iLastSetGlobalTickCount = iLocalTickCount; |
|
325 iGlobalTickWatcher = CIntPropertyWatcher::NewL( KSINDUID, ECommandHandlerTickKey, *this ); |
|
326 } |
|
327 |
|
328 /** |
|
329 * Returns the VCommand context. Creates one on demand if none exists yet |
|
330 */ |
|
331 MNssContext* CVCommandService::GetVCommandContextLC() const |
|
332 { |
|
333 RUBY_DEBUG0( "CVCommandService::GetVCommandContextL start" ); |
|
334 CContextProvider* contextProvider = CContextProvider::NewLC( *iVasDbManager ); |
|
335 MNssContext* result = contextProvider->GetVCommandContextLC(); |
|
336 CleanupStack::Pop( result ); |
|
337 CleanupStack::PopAndDestroy( contextProvider ); |
|
338 CleanupDeletePushL( result ); |
|
339 RUBY_DEBUG0( "CVCommandService::GetVCommandContextL end" ); |
|
340 return result; |
|
341 } |
|
342 |
|
343 |
|
344 /** |
|
345 * Constructs a set of new VAS tags from a command and a context. |
|
346 * Does not save to VAS, just creates. Ownership on the created tag is |
|
347 * passed to the client |
|
348 */ |
|
349 CArrayPtr<MNssTag>* CVCommandService::CreateTagsLC2( const CVCommand& aCommand, |
|
350 const MNssContext& aContext ) |
|
351 { |
|
352 return CTagCommandConverter::CommandToTagsLC2( aCommand, aContext, *iTagManager ); |
|
353 } |
|
354 |
|
355 /** |
|
356 * Constructs training related parameters. Like language to be used |
|
357 * @leave negated TNssSpeechItemResult |
|
358 * @todo Is it ok to mix system-wide codes with the TNssSpeechItemResult codes |
|
359 */ |
|
360 CNssTrainingParameters* CVCommandService::ConstructTrainingParametersLC() const |
|
361 { |
|
362 RUBY_DEBUG0( "CVCommandService::SetLanguageParamersL start" ); |
|
363 RArray<TLanguage>* languageArray = new (ELeave) RArray<TLanguage>; |
|
364 CleanupDeletePushL ( languageArray ); // Protect [allocated on the heap] array itself |
|
365 CleanupClosePushL( *languageArray ); // Protect the array elements |
|
366 |
|
367 // Always generate a pronunciation in UI language |
|
368 User::LeaveIfError( languageArray->Append( User::Language() ) ); |
|
369 |
|
370 // Add an extra language, which can be determined by language identification |
|
371 User::LeaveIfError( languageArray->Append( ELangOther ) ); |
|
372 |
|
373 CNssTrainingParameters* trainingParams = CNssTrainingParameters::NewL(); |
|
374 // No L-functions for some time, delay pushing parameters to the cleanup stack |
|
375 |
|
376 trainingParams->SetLanguages( languageArray ); |
|
377 // No need to protect the language array anymore. |
|
378 // From now on it is managed by the parameters |
|
379 |
|
380 // For array elements pushed via CleanupClosePushL |
|
381 CleanupStack::Pop( languageArray ); |
|
382 CleanupStack::Pop( languageArray ); |
|
383 CleanupStack::PushL( trainingParams ); |
|
384 RUBY_DEBUG0( "CVCommandService::SetLanguageParamersL end" ); |
|
385 return trainingParams; |
|
386 } |
|
387 |
|
388 /** |
|
389 * Retrieves the list of tags for a given context. Synchronous. |
|
390 * Leaves two objects on the cleanup stack! |
|
391 * First PopAndDestroy will ResetAndDestroy content |
|
392 * Second one will destroy the MNsstagListArray itself |
|
393 */ |
|
394 MNssTagListArray* CVCommandService::GetTagListLC2( const MNssContext& aContext ) const |
|
395 { |
|
396 RUBY_DEBUG0( "CVCommandService::GetTagListLC start" ); |
|
397 CTagGetter* tagGetter = CTagGetter::NewLC(); |
|
398 MNssTagListArray* result = tagGetter->GetTagListLC2( *iTagManager, aContext ); |
|
399 |
|
400 CleanupStack::Pop( result ); |
|
401 CleanupStack::Pop( result ); |
|
402 CleanupStack::PopAndDestroy( tagGetter ); |
|
403 CleanupDeletePushL( result ); |
|
404 CleanupResetAndDestroy<MNssTagListArray>::PushL( *result ); |
|
405 RUBY_DEBUG0( "CVCommandService::GetTagListLC end" ); |
|
406 |
|
407 return result; |
|
408 } |
|
409 |
|
410 /** |
|
411 * Retrieves the list of tags for a given context and voice command id. |
|
412 * Synchronous. Leaves two objects on the cleanup stack! |
|
413 * First PopAndDestroy will ResetAndDestroy content |
|
414 * Second one will destroy the MNsstagListArray itself |
|
415 */ |
|
416 MNssTagListArray* CVCommandService::GetTagListLC2( const MNssContext& aContext, |
|
417 TInt aCommandId ) const |
|
418 { |
|
419 RUBY_DEBUG0( "CVCommandService::GetTagListLC start" ); |
|
420 CTagGetter* tagGetter = CTagGetter::NewLC(); |
|
421 MNssTagListArray* result = tagGetter->GetTagListLC2( *iTagManager, aContext, aCommandId ); |
|
422 |
|
423 CleanupStack::Pop( result ); |
|
424 CleanupStack::Pop( result ); |
|
425 CleanupStack::PopAndDestroy( tagGetter ); |
|
426 CleanupDeletePushL( result ); |
|
427 CleanupResetAndDestroy<MNssTagListArray>::PushL( *result ); |
|
428 RUBY_DEBUG0( "CVCommandService::GetTagListLC end" ); |
|
429 |
|
430 return result; |
|
431 } |
|
432 |
|
433 /** |
|
434 * Resets iCommands and fills them with the commands from VAS |
|
435 */ |
|
436 void CVCommandService::RefreshCommandListL() |
|
437 { |
|
438 RUBY_DEBUG_BLOCK( "CVCommandService::RefreshCommandListL" ); |
|
439 const TInt KMinimalCommandId = 0; |
|
440 iCommands.ResetAndDestroy(); |
|
441 iMaxCommandId = KMinimalCommandId - 1; |
|
442 MNssContext* context = GetVCommandContextLC(); |
|
443 MNssTagListArray* tagList = GetTagListLC2( *context ); |
|
444 RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Found [%d] tags", tagList->Count() ); |
|
445 |
|
446 // Sort array by command ids |
|
447 TVCommandTagPtrArrayKey key( ECmpTInt ); |
|
448 tagList->Sort( key ); |
|
449 |
|
450 // Array of tags related to the same VCommand |
|
451 CArrayPtrFlat<MNssTag>* commandArray = |
|
452 new ( ELeave) CArrayPtrFlat<MNssTag>( KMaxTagsPerCommand ); |
|
453 CleanupStack::PushL( commandArray ); |
|
454 // No need for CleanupResetAndDestroyPushL. |
|
455 // MNssTag objects are not copied to the commandArray |
|
456 |
|
457 TInt currentId = KMinimalCommandId - 1; |
|
458 |
|
459 for( TInt i = 0; i < tagList->Count(); i++ ) |
|
460 { |
|
461 RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Processing tag [%d]", i ); |
|
462 TInt commandId = tagList->At( i )->RRD()->IntArray()->At( 0 ); |
|
463 if( currentId != commandId ) // new or first command |
|
464 { |
|
465 if( commandArray->Count() > 0 ) |
|
466 { |
|
467 RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Converting [%d] tags to command", commandArray->Count() ); |
|
468 CStoredVCommand* command = CTagCommandConverter::TagsToCommandLC( *commandArray ); |
|
469 RUBY_DEBUG2( "CVCommandService::RefreshCommandListL Converted successfully to command id [%d], command text [%S]", command->CommandId(), &(command->SpokenText() ) ); |
|
470 iCommands.AppendL( command ); |
|
471 CleanupStack::Pop( command ); |
|
472 } |
|
473 commandArray->Reset(); |
|
474 currentId = commandId; |
|
475 } |
|
476 commandArray->AppendL( tagList->At( i ) ); |
|
477 iMaxCommandId = Max( iMaxCommandId, commandId ); |
|
478 } |
|
479 |
|
480 // Flush the last buffer |
|
481 if( commandArray->Count() > 0 ) |
|
482 { |
|
483 RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Converting [%d] tags to command", commandArray->Count() ); |
|
484 CStoredVCommand* command = CTagCommandConverter::TagsToCommandLC( *commandArray ); |
|
485 RUBY_DEBUG2( "CVCommandService::RefreshCommandListL Converted successfully to command id [%d], command text [%S]", command->CommandId(), &(command->SpokenText() ) ); |
|
486 iCommands.AppendL( command ); |
|
487 CleanupStack::Pop( command ); |
|
488 } |
|
489 CleanupStack::PopAndDestroy( commandArray ); |
|
490 CleanupStack::PopAndDestroy( tagList ); // ResetAndDestroy |
|
491 CleanupStack::PopAndDestroy( tagList ); // delete |
|
492 CleanupStack::PopAndDestroy( context ); |
|
493 MarkCacheValidL(); |
|
494 } |
|
495 |
|
496 /** |
|
497 * Marks the iCommands as an invalid reflection of the VAS content. |
|
498 * To make iCommands reflect the real VAS content a call to |
|
499 * RefreshCommandsL is needed |
|
500 */ |
|
501 void CVCommandService::InvalidateCacheL() |
|
502 { |
|
503 // Invalidating own cache means unvalidating the other instances' caches as well |
|
504 TInt globalTickCount; |
|
505 User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) ); |
|
506 |
|
507 iLocalTickCount = globalTickCount; |
|
508 iLastSetGlobalTickCount = globalTickCount + 1; |
|
509 User::LeaveIfError( iGlobalTickProperty.Set( iLastSetGlobalTickCount ) ); |
|
510 } |
|
511 |
|
512 /** |
|
513 * Marks the iCommands as the valid reflection of the VAS content. |
|
514 */ |
|
515 void CVCommandService::MarkCacheValidL() |
|
516 { |
|
517 // Since all the cache comparison/update functions are executed under the |
|
518 // StartAtomicOperation/EndAtomicOperation, we can be sure that nobody |
|
519 // touched VAS and global tick count since the last IsCacheValidL |
|
520 TInt globalTickCount; |
|
521 User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) ); |
|
522 |
|
523 iLocalTickCount = globalTickCount; |
|
524 } |
|
525 |
|
526 /** |
|
527 * Tells if the iCommands reflect the content of the VAS DB correctly |
|
528 * @return ETrue if the cache is valid, EFalse otherwise |
|
529 */ |
|
530 TBool CVCommandService::IsCacheValidL() |
|
531 { |
|
532 RUBY_DEBUG_BLOCKL( "CVCommandService::IsCacheValidL" ); |
|
533 TInt globalTickCount; |
|
534 User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) ); |
|
535 RUBY_DEBUG2( "CVCommandService::IsCacheValidL Local tick count [%d], \ |
|
536 globalTickCount [%d]", iLocalTickCount, globalTickCount ); |
|
537 return iLocalTickCount == globalTickCount; |
|
538 } |
|
539 |
|
540 /** |
|
541 * Generates new command id to be used for identifying voice tags, that |
|
542 * belong to the same VCommand |
|
543 */ |
|
544 TInt CVCommandService::NewCommandIdL() |
|
545 { |
|
546 if( !IsCacheValidL() ) |
|
547 { |
|
548 // Fills the max used command id |
|
549 RefreshCommandListL(); |
|
550 } |
|
551 return ++iMaxCommandId; |
|
552 } |
|
553 |
|
554 /** |
|
555 * Sets the given aId as a VCommand id for all the tags in the given list |
|
556 * This id is set as a first RRD int element of all the given MNssTags. |
|
557 * If the RRD int array has no elements yet, one element is added |
|
558 */ |
|
559 void CVCommandService::SetVCommandIdL( CArrayPtr<MNssTag>& aTags, TInt aId ) const |
|
560 { |
|
561 // All the tags corresponding to the same VCommand get the same command id |
|
562 |
|
563 for( TInt i = 0; i < aTags.Count(); i++ ) |
|
564 { |
|
565 if( aTags[i]->RRD()->IntArray()->Count() > 0 ) |
|
566 { |
|
567 aTags[i]->RRD()->IntArray()->At( i ) = aId; |
|
568 } |
|
569 else |
|
570 { |
|
571 aTags[i]->RRD()->IntArray()->AppendL( aId ); |
|
572 } |
|
573 } |
|
574 } |
|
575 |
|
576 /** |
|
577 * Asynchronous |
|
578 * Attempts to play back the text expected to be recognized. |
|
579 * To be playable command has to be added to CVCommandHandler AND |
|
580 * then retrieved back |
|
581 * |
|
582 * @param aHandler CVCommandHandler where the command is stored |
|
583 * @param aPlayEventHandler Entity that handles the playback callbacks |
|
584 * @see NssVasMPlayEventHandler.h |
|
585 * |
|
586 * @leave KErrBadHandle if the current command has not been retrieved |
|
587 * from CVCommandHandler (i.e. was not trained for recognition) |
|
588 * @leave KErrNotFound if this command cannot be found in aHandler |
|
589 * @leave KErrNotReady @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult |
|
590 * EVasUnexpectedRequest |
|
591 * @leave KErrInUse @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult |
|
592 * EVasInUse |
|
593 * @leave KErrArgument @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult |
|
594 * EVasInvalidParameter |
|
595 * @leave KErrGeneral @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult |
|
596 * EVasPlayFailed |
|
597 */ |
|
598 void CVCommandService::PlaySpokenTextL( const CStoredVCommand& aCommand, |
|
599 MNssPlayEventHandler& aPlayEventHandler ) |
|
600 { |
|
601 RUBY_DEBUG_BLOCK( "CVCommandService::PlaySpokenTextL" ); |
|
602 __ASSERT_ALWAYS( !iPlaybackHandler, User::Leave( KErrInUse ) ); |
|
603 StartAtomicOperationLC(); |
|
604 iPlaybackHandler = &aPlayEventHandler; |
|
605 MNssTag* playbackTag = TagByCommandIdTextL( aCommand.CommandId(), |
|
606 aCommand.SpokenText() ); |
|
607 if( !playbackTag ) |
|
608 { |
|
609 User::Leave( KErrNotFound ); |
|
610 } |
|
611 iTagPlayer->PlayTagL( playbackTag, *this ); |
|
612 CleanupStack::Pop(); // EndOfAtomicOperation |
|
613 } |
|
614 |
|
615 /** |
|
616 * Asynchronous |
|
617 * Plays back the user-specified alternative spoken text. |
|
618 * Otherwise is identical to PlaySpokenTextL |
|
619 * @see PlaySpokenTextL |
|
620 * @leave KErrNotFound if this command cannot be found in aHandler of if |
|
621 * it doesn't have a user-specified text |
|
622 */ |
|
623 void CVCommandService::PlayAlternativeSpokenTextL( const CStoredVCommand& aCommand, |
|
624 MNssPlayEventHandler& aPlayEventHandler ) |
|
625 { |
|
626 RUBY_DEBUG_BLOCK( "CVCommandService::PlayAlternativeSpokenTextL" ); |
|
627 __ASSERT_ALWAYS( !iPlaybackHandler, User::Leave( KErrInUse ) ); |
|
628 StartAtomicOperationLC(); |
|
629 iPlaybackHandler = &aPlayEventHandler; |
|
630 MNssTag* playbackTag = TagByCommandIdTextL( aCommand.CommandId(), |
|
631 aCommand.AlternativeSpokenText() ); |
|
632 if( !playbackTag ) |
|
633 { |
|
634 User::Leave( KErrNotFound ); |
|
635 } |
|
636 iTagPlayer->PlayTagL( playbackTag, *this ); |
|
637 CleanupStack::Pop(); // EndOfAtomicOperation |
|
638 } |
|
639 |
|
640 /** |
|
641 * This method is not intended to be called directly. |
|
642 * Use CVCommand::CancelPlaybackL instead |
|
643 * @see CVCommand::CancelPlaybackL |
|
644 * @see CStoredVCommand::CancelPlaybackL |
|
645 */ |
|
646 void CVCommandService::CancelPlaybackL( const CStoredVCommand& /*aCommand*/ ) |
|
647 { |
|
648 // For calling EndOfAtomicOperation if a leave occurs |
|
649 CreateAndPushEndAtomicCleanupItemL(); |
|
650 |
|
651 __ASSERT_ALWAYS( iPlaybackHandler, User::Leave( KErrNotReady ) ); |
|
652 iTagPlayer->CancelPlaybackL(); |
|
653 iPlaybackHandler = NULL; |
|
654 CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation |
|
655 } |
|
656 |
|
657 /** |
|
658 * The HandlePlayStarted method is a virtual method implemented by the |
|
659 * client and is called when play is started |
|
660 * @param aDuration - the duration of the utterance data |
|
661 */ |
|
662 void CVCommandService::HandlePlayStarted( TTimeIntervalMicroSeconds32 aDuration ) |
|
663 { |
|
664 __ASSERT_ALWAYS( iPlaybackHandler, User::Panic( KCommandHandlerPanic, KErrNotReady ) ); |
|
665 iPlaybackHandler->HandlePlayStarted( aDuration ); |
|
666 } |
|
667 |
|
668 /** |
|
669 * The HandlePlayComplete method is a virtual method implemented by the |
|
670 * client and is called when play is completed |
|
671 * @param aErrorCode EVasErrorNone if playing was successfull |
|
672 */ |
|
673 void CVCommandService::HandlePlayComplete( TNssPlayResult aErrorCode ) |
|
674 { |
|
675 __ASSERT_ALWAYS( iPlaybackHandler, User::Panic( KCommandHandlerPanic, KErrNotReady ) ); |
|
676 |
|
677 // End atomic operation before calling the observer not to block its potential |
|
678 // VCommandHandler usage from the HandlePlayComplete |
|
679 |
|
680 // Before ending atomic operation, store the current event handler to the |
|
681 // temporary variable to protect it against the potential change by the new |
|
682 // PlaySpokenTextL call |
|
683 MNssPlayEventHandler* handler = iPlaybackHandler; |
|
684 iPlaybackHandler = NULL; |
|
685 EndAtomicOperation(); |
|
686 handler->HandlePlayComplete( aErrorCode ); |
|
687 } |
|
688 |
|
689 |
|
690 /** |
|
691 * Searches vcommand tags for the tag, that corresponds to the given |
|
692 * commandId and is trained against the given text |
|
693 * @param aCommandId Command id to search for |
|
694 * @param aText |
|
695 * @return ANY of the voice tags corresponding to the given aCommandId |
|
696 * and aText. Typically there will be one such tag only. However, |
|
697 * if there are several it is not specified which one will be |
|
698 * returned |
|
699 * @return NULL if no satisfying tag is found |
|
700 */ |
|
701 MNssTag* CVCommandService::TagByCommandIdTextL( TInt aCommandId, const TDesC& aText ) |
|
702 { |
|
703 RUBY_DEBUG_BLOCKL( "CVCommandService::TagByCommandIdTextL" ); |
|
704 MNssContext* context = GetVCommandContextLC(); |
|
705 MNssTagListArray* tagList = GetTagListLC2( *context, aCommandId ); |
|
706 // A command can have up to two voice tags. We need to locate the one, |
|
707 // that has been trained against a given text |
|
708 MNssTag* tag (NULL); |
|
709 for( TInt i = 0; i < tagList->Count(); i++ ) |
|
710 { |
|
711 if( tagList->At(i)->SpeechItem()->RawText() == aText ) |
|
712 { |
|
713 tag = tagList->At(i); |
|
714 // deletes pointer only. tag will be returned to the calling party |
|
715 tagList->Delete( i ); |
|
716 break; |
|
717 } // if |
|
718 } // for |
|
719 CleanupStack::PopAndDestroy( tagList ); // ResetAndDestroy |
|
720 CleanupStack::PopAndDestroy( tagList ); // delete |
|
721 CleanupStack::PopAndDestroy( context ); |
|
722 |
|
723 return tag; |
|
724 } |
|
725 |
|
726 /** |
|
727 * Starts the section of the code, that can be simultaneously executed |
|
728 * only by a single instance of this class. Other instances have to wait |
|
729 * Same as starting the Java "synchronized" method. Puts the function |
|
730 * EndAtomicOperation to cleanup stack to make the use leave safe. Subsequent |
|
731 * call to CleanupStack::PopAndDestroy will run EndAtomicOperation. |
|
732 */ |
|
733 void CVCommandService::StartAtomicOperationLC() |
|
734 { |
|
735 RUBY_DEBUG0( "CVCommandService::StartAtomicOperation start" ); |
|
736 if ( iLock.IsHeld() ) |
|
737 { |
|
738 User::Leave( KErrLocked ); |
|
739 } |
|
740 |
|
741 iLock.Wait(); |
|
742 |
|
743 CreateAndPushEndAtomicCleanupItemL(); |
|
744 |
|
745 RUBY_DEBUG0( "CVCommandService::StartAtomicOperation end" ); |
|
746 } |
|
747 |
|
748 /** |
|
749 * Ends the section of the code, that can be simultaneously executed |
|
750 * only by a single instance of this class. Other instances have to wait |
|
751 * Same as exiting the Java "synchronized" method |
|
752 * @leave KErrNotReady if atomic operation hasn't been started |
|
753 */ |
|
754 void CVCommandService::EndAtomicOperation() |
|
755 { |
|
756 RUBY_DEBUG0( "CVCommandService::EndAtomicOperation start" ); |
|
757 __ASSERT_ALWAYS( iLock.IsHeld(), User::Panic( KCommandHandlerPanic, KErrNotReady ) ); |
|
758 iLock.Signal(); |
|
759 RUBY_DEBUG0( "CVCommandService::EndAtomicOperation end" ); |
|
760 } |
|
761 |
|
762 /** |
|
763 * Runs the EndAtomicOperation when calling CleanupStack::PopAndDestroy after |
|
764 * a call to StartAtomicOperationLC |
|
765 */ |
|
766 void CVCommandService::CleanupEndAtomicOperation( TAny* aService ) |
|
767 { |
|
768 CVCommandService* service = static_cast<CVCommandService*>(aService); |
|
769 service->EndAtomicOperation(); |
|
770 } |
|
771 |
|
772 /** |
|
773 * Creates and pushes to cleanup stack a TCleanupItem that calls |
|
774 * EndAtomicOperation when CleanupStack::PopAndDestroy is called |
|
775 */ |
|
776 void CVCommandService::CreateAndPushEndAtomicCleanupItemL() |
|
777 { |
|
778 TCleanupItem item( CleanupEndAtomicOperation, this ); |
|
779 CleanupStack::PushL( item ); |
|
780 } |
|
781 |
|
782 void CVCommandService::IntValueChanged( TInt aNewValue ) |
|
783 { |
|
784 RUBY_DEBUG2( "iLocalTickCount [%d], aNewValue [%d]", iLocalTickCount, aNewValue ); |
|
785 if( aNewValue != iLastSetGlobalTickCount ) |
|
786 { |
|
787 if( iObserver ) |
|
788 { |
|
789 iObserver->CommandSetChanged(); |
|
790 } // if iObserver |
|
791 } // if local cache is out of date |
|
792 } |
|
793 |
|
794 //End of file |