diff -r cad71a31b7fc -r e36f3802f733 srsf/profileobserverplugin/src/vcommandprofileobserver.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/srsf/profileobserverplugin/src/vcommandprofileobserver.cpp Wed Sep 01 12:29:17 2010 +0100 @@ -0,0 +1,663 @@ +/* +* 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: implements profile changes handling +* +*/ + + +#include +#include + +#include +#include + +#include +#include // For CleanupResetAndDestroyPushL + +#include "vcommandprofileobserver.h" +#include "rubydebug.h" + + +// ============================ CONSTANTS =============================== + +_LIT( KResourceFile, "z:\\resource\\vcprofileobserver.rsc" ); + +// UID of the profiles application +const TUid KUidAppProfiles = { 0x100058F8 }; + +// Executable for changing profiles. +const TUid KProfileChangerUid = { 0x10281D15 }; + +// Starting id for profile command arguments +const TInt KProfileCommandArgumentBase = 200; + +// File name to read the icon from +_LIT( KIconFile, "Z:\\resource\\apps\\vcommand.mif" ); + +// Index of the profile folder icon in the default folder icon file +// @see CVCFolderIcon::NewL +const TInt KProfileFolderIconIndex = 5; + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::CVCommandProfileObserver +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CVCommandProfileObserver::CVCommandProfileObserver() + : iProfileUpdateError( EFalse ) + { + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::ConstructL() + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::ConstructL()" ); + + iService = CVCommandHandler::NewL(); + + iProfileEngine = ProEngFactory::NewEngineL(); + iNameArray = iProfileEngine->ProfileNameArrayLC(); + CleanupStack::Pop(); // we can't popup C-class with M-class argument + + LoadProfileDataL(); + SyncVCommandProfilesL(); + + // Create the profile engine notify handler only after construction of + // other parts have been completed successfully. This way, should + // construction fail, we avoid the situation where a callback is executed + // on a partially contructed object + iNotifyHandler = ProEngFactory::NewNotifyHandlerL(); + iNotifyHandler->RequestProfileNameArrayNotificationsL( *this ); + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CVCommandProfileObserver* CVCommandProfileObserver::NewL() + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::NewL()" ); + + CVCommandProfileObserver* self = new( ELeave ) CVCommandProfileObserver(); + + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + + return self; + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::~CVCommandProfileObserver +// Destructor. +// ----------------------------------------------------------------------------- +// +CVCommandProfileObserver::~CVCommandProfileObserver() + { + RUBY_DEBUG0( "CVCommandProfileObserver::~CVCommandProfileObserver()" ); + + if( iProfileEngine ) + { + iProfileEngine->Release(); + } + iProfileEngine = NULL; + + if( iNameArray ) + { + delete iNameArray; + iNameArray = NULL; + } + + DeleteCache(); + + delete iService; + + if ( iNotifyHandler ) + { + iNotifyHandler->CancelProfileNameArrayNotifications(); + } + delete iNotifyHandler; + delete iProfileFolder; + delete iFolderTitle; + delete iProfileTooltip; + + iAddedCommands.ResetAndDestroy(); + iAddedCommands.Close(); + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::SyncVCommandProfilesL +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::SyncVCommandProfilesL() + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::SyncVCommandProfilesL()" ); + + if( !iNameArray || iNameArray->MdcaCount() <= 0 ) + { + return; + } + + // Form the set of commands that should be in + RVCommandArray profileEngineCommands; + CleanupResetAndDestroyPushL( profileEngineCommands ); + for( TInt i = 0; i < iNameArray->MdcaCount(); i++ ) + { + TInt index = iNameArray->FindByName( iNameArray->MdcaPoint( i ) ); + TInt profileId = iNameArray->ProfileId( index ); + CVCommand* command = CreateProfileCommandL( iNameArray->MdcaPoint( i ), profileId ); + profileEngineCommands.AppendL( command ); + } + + // delete cache and force iCommands to be reloaded + // + DeleteCache(); + + const CVCommandArray& storedCommands = ListVCommandsL(); + CVCommandArray* deduction = storedCommands.ProduceUntrainSetByRunnablesLC( profileEngineCommands ); + CVCommandArray* addition = storedCommands.ProduceTrainSetByRunnablesLC( profileEngineCommands ); + + iService->RemoveCommandsL( deduction->PointerArray(), ETrue ); + iService->AddCommandsL( addition->PointerArray(), ETrue ); + CleanupStack::PopAndDestroy( addition ); + CleanupStack::PopAndDestroy( deduction ); + + CleanupStack::PopAndDestroy( &profileEngineCommands ); + + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::HandleProfileNameArrayModificationL +// ----------------------------------------------------------------------------- +// +// Assume that there will be only one change each time this function is called. +// +void CVCommandProfileObserver::HandleProfileNameArrayModificationL() + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::HandleProfileNameArrayModificationL()" ); + + // delete cache and force iCommands to be reloaded + // + DeleteCache(); + + // new array of profile names + MProEngProfileNameArray* nameArray = iProfileEngine->ProfileNameArrayLC(); + + // copy profile names to two descriptor arrays + // + CDesC16ArrayFlat* oldNames = new ( ELeave ) CDesCArrayFlat( iNameArray->MdcaCount() ); + CleanupStack::PushL( oldNames ); + + CDesC16ArrayFlat* newNames = new ( ELeave ) CDesCArrayFlat( nameArray->MdcaCount() ); + CleanupStack::PushL( newNames ); + + for( TInt i = 0; i < iNameArray->MdcaCount(); i++ ) + { + oldNames->AppendL( iNameArray->MdcaPoint( i ) ); + } + for( TInt j = 0; j < nameArray->MdcaCount(); j++ ) + { + newNames->AppendL( nameArray->MdcaPoint( j ) ); + } + + + // Find changes by deleting equivalent commands from both descriptor arrays. + // + for( TInt i = 0; i < newNames->MdcaCount(); i++ ) // go through the new names + { + TName searchStr = newNames->MdcaPoint( i ); + TInt index; + + oldNames->Find( searchStr, index, ECmpNormal ); + + if( index != oldNames->MdcaCount() ) // String was found + { + oldNames->Delete( index ); + newNames->Delete( i ); + i--; + } + } + + + // profile name has been edited + // + if( newNames->MdcaCount() == oldNames->MdcaCount() ) + { + + // now we should have a pair of original and changed name + // + if( newNames->MdcaCount() == 1 ) // let's edit the profile name (old name, new name) + { + TRAPD( error, EditProfileNameL( oldNames->MdcaPoint( 0 ), newNames->MdcaPoint( 0 ) ) ); + if ( error == KErrGeneral ) + { + CleanupStack::PopAndDestroy( newNames ); + CleanupStack::PopAndDestroy( oldNames ); + CleanupStack::Pop(); // nameArray ( M-class pointer ) + + delete iNameArray; + iNameArray = nameArray; + + User::Leave( error ); + } + } + } + // new profile(s) has been added + // + else if( newNames->MdcaCount() > oldNames->MdcaCount() ) + { + AddNewProfilesL( *newNames, *nameArray ); + } + // profile(s) has been deleted + // + else + { + RemoveProfilesL( *oldNames ); + } + + // update old profile names + // + CleanupStack::PopAndDestroy( newNames ); + CleanupStack::PopAndDestroy( oldNames ); + CleanupStack::Pop(); // nameArray ( M-class pointer ) + + delete iNameArray; + iNameArray = nameArray; + + if ( iProfileUpdateError ) + { + iProfileUpdateError = EFalse; + + HandleProfileNameArrayModificationL(); + } + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::HandleProfileNameArrayNotificationError +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::HandleProfileNameArrayNotificationError( TInt aError ) + { + RUBY_ERROR1( "HandleProfileNameArrayNotificationError: %d", aError ); + + if ( aError == KErrLocked ) + { + iProfileUpdateError = ETrue; + } + } + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::LoadProfileDataL +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::LoadProfileDataL() + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::LoadProfileDataL()" ); + + RFs fs; + CleanupClosePushL( fs ); + User::LeaveIfError( fs.Connect() ); + + RResourceFile resourceFile; + TResourceReader theReader; + + TFileName name; + name.Append( KResourceFile ); + + BaflUtils::NearestLanguageFile( fs, name ); + + if ( !BaflUtils::FileExists( fs, name ) ) + { + User::Leave( KErrNotFound ); + } + + resourceFile.OpenL( fs, name ); + CleanupClosePushL( resourceFile ); + + // Leaves 'res' to cleanupstack + HBufC8* res = resourceFile.AllocReadLC( VCPROFILEOBSERVERINFO ); + theReader.SetBuffer( res ); + + iProfileFolder = theReader.ReadHBufCL(); + iFolderTitle = theReader.ReadHBufCL(); + iProfileTooltip = theReader.ReadHBufCL(); + + // Cleanup res + CleanupStack::PopAndDestroy( res ); + CleanupStack::PopAndDestroy( &resourceFile ); + CleanupStack::PopAndDestroy( &fs ); + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::ListVCommandsL +// ----------------------------------------------------------------------------- +// +const CVCommandArray& CVCommandProfileObserver::ListVCommandsL() + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::ListVCommandsL()" ); + + if( !iCommands ) + { + iCommands = iService->ListCommandsL(); + } + + return *iCommands; + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::DeleteCache +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::DeleteCache() + { + RUBY_DEBUG0( "CVCommandProfileObserver::DeleteCache()" ); + + // delete cache and force iCommands to be reloaded + // + delete iCommands; + iCommands = NULL; + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::AddNewProfileL +// Adds new profile to VCommandhandler. +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::AddNewProfileL( const TDesC& aNewName, + TInt aProfileId, + TBool aCommit ) + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::AddNewProfileL()" ); + + // if profile name doesn't exists + // + if( FindProfileIndexL( aNewName ) == KErrNotFound ) + { + CVCommand* initialCommand = CreateProfileCommandL( aNewName, aProfileId ); + CleanupStack::PushL( initialCommand ); + + if ( aCommit ) + { + iService->AddCommandL( *initialCommand ); + CleanupStack::PopAndDestroy( initialCommand ); + + // delete cache and force iCommands to be reloaded + // + DeleteCache(); + } + else + { + iAddedCommands.AppendL( initialCommand ); + + CleanupStack::Pop( initialCommand ); + } + } + } + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::AddNewProfilesL +// Adds new profiles to VCommandhandler. +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::AddNewProfilesL( const CDesC16ArrayFlat& aNames, + const MProEngProfileNameArray& aProfileNames, + TBool aCommit ) + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::AddNewProfilesL()" ); + + RVCommandArray commands; + CleanupClosePushL( commands ); + CleanupResetAndDestroyPushL( commands ); + + for( TInt i = 0; i < aNames.MdcaCount(); i++ ) + { + TInt index = aProfileNames.FindByName( aNames.MdcaPoint(i) ); + TInt profileId = aProfileNames.ProfileId( index ); + + // if profile name doesn't exists + // + if( FindProfileIndexL( aNames.MdcaPoint(i) ) == KErrNotFound ) + { + CVCommand* initialCommand = CreateProfileCommandL( aNames.MdcaPoint(i), profileId ); + CleanupStack::PushL( initialCommand ); + + if ( aCommit ) + { + commands.Append( initialCommand ); + } + else + { + iAddedCommands.AppendL( initialCommand ); + } + + CleanupStack::Pop( initialCommand ); + } + } + + if ( aCommit ) + { + iService->AddCommandsL( commands ); + + // delete cache and force iCommands to be reloaded + // + DeleteCache(); + } + + CleanupStack::PopAndDestroy( &commands ); + CleanupStack::PopAndDestroy( &commands ); + } + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::CreateProfileCommandL +// Creates a VCommand for the given profile attributes +// ----------------------------------------------------------------------------- +CVCommand* CVCommandProfileObserver::CreateProfileCommandL( const TDesC& profileName, TInt aProfileId ) const + { + CVCFolderInfo* folderInfo = CVCFolderInfo::NewL( iFolderTitle->Des(), + iProfileFolder->Des(), 0, + KProfileFolderIconIndex, + KIconFile ); + CleanupStack::PushL( folderInfo ); + CVCCommandUi* commandUi = CVCCommandUi::NewL( profileName, + *folderInfo, ETrue, + iProfileTooltip->Des(), + KUidAppProfiles ); + CleanupStack::PopAndDestroy( folderInfo ); + CleanupStack::PushL( commandUi ); + + TBuf8<10> idDescriptor; + idDescriptor.Num( KProfileCommandArgumentBase + aProfileId ); + CVCRunnable* runnable = CVCRunnable::NewL( KProfileChangerUid, idDescriptor.Left( idDescriptor.Length() ) ); + CleanupStack::PushL( runnable ); + + CVCommand* command = CVCommand::NewL( profileName, + *runnable, + *commandUi ); + CleanupStack::PopAndDestroy( runnable ); + CleanupStack::PopAndDestroy( commandUi ); + return command; + } + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::RemoveProfileL +// Removes profile from VCommandhandler. +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::RemoveProfileL( const TDesC& aName ) + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::RemoveProfileL()" ); + + TInt idx = 0; + while( ( idx = FindProfileIndexL( aName ) ) != KErrNotFound ) + { + iService->RemoveCommandL( ListVCommandsL()[ idx ] ); + + // delete cache and force iCommands to be reloaded + // + DeleteCache(); + } + } + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::RemoveProfilesL +// Removes profiles from VCommandhandler. +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::RemoveProfilesL( const CDesC16ArrayFlat& aNames ) + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::RemoveProfilesL()" ); + + RVCommandArray commands; + CleanupClosePushL( commands ); + + for( TInt i = 0; i < aNames.MdcaCount(); i++ ) + { + TInt idx = 0; + while( ( idx = FindProfileIndexL( aNames.MdcaPoint(i) ) ) != KErrNotFound ) + { + commands.Append( &ListVCommandsL()[ idx ] ); + break; + } + } + + iService->RemoveCommandsL( commands ); + + // delete cache and force iCommands to be reloaded + // + DeleteCache(); + + CleanupStack::PopAndDestroy( &commands ); + } + + +// ----------------------------------------------------------------------------- +// CVCommandProfileObserver::EditProfileNameL +// Edits the profile name. +// ----------------------------------------------------------------------------- +// +void CVCommandProfileObserver::EditProfileNameL( const TDesC& aOldName, const TDesC& aNewName ) + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::EditProfileNameL()" ); + + // Get profile id + TInt idx = iNameArray->FindByName( aOldName ); + TInt profileId = iNameArray->ProfileId( idx ); + + idx = FindProfileIndexL( aOldName ); + + // this finds always user edited one, if one exists + // + if( idx == KErrNotFound ) + { + // for some reason profile doesn't exist in voice command list + // so let's add it + // + return AddNewProfileL( aNewName, profileId ); + } + + const CVCommand& oldCmd = ListVCommandsL()[ idx ]; + + CVCCommandUi* commandUi = CVCCommandUi::NewL( aNewName, + oldCmd.CommandUi().FolderInfo(), ETrue, + oldCmd.CommandUi().Tooltip(), + oldCmd.CommandUi().IconUid() ); + CleanupStack::PushL( commandUi ); + + TBuf8<10> idDescriptor; + idDescriptor.Num( profileId ); + CVCRunnable* runnable = CVCRunnable::NewL( oldCmd.Runnable() ); + CleanupStack::PushL( runnable ); + + CVCommand* command = CVCommand::NewL( aNewName, *runnable, *commandUi ); + CleanupStack::PopAndDestroy( runnable ); + CleanupStack::PopAndDestroy( commandUi ); + CleanupStack::PushL( command ); + + // Delete old command + iService->RemoveCommandL( oldCmd ); + + // delete cache and force iCommands to be reloaded + // + DeleteCache(); + + while( ( idx = FindProfileIndexL( aOldName ) ) != KErrNotFound ) + { + iService->RemoveCommandL( ListVCommandsL()[ idx ] ); + DeleteCache(); + } + + // Add new command + // + iService->AddCommandL( *command ); + CleanupStack::PopAndDestroy( command ); + + // delete cache and force iCommands to be reloaded + // + DeleteCache(); + } + + +// --------------------------------------------------------- +// CVCommandProfileObserver::FindProfileIndexL +// --------------------------------------------------------- +// +// Assumes that profiles are in a folder called GetProfileFolderName(). +// +// Finds always the user edited command if it exists. +// +TInt CVCommandProfileObserver::FindProfileIndexL( const TDesC& aName ) + { + RUBY_DEBUG_BLOCK( "CVCommandProfileObserver::FindProfileIndexL()" ); + + TInt retval = KErrNotFound; + + for( TInt i = 0; i < ListVCommandsL().Count(); i++ ) + { + if( ListVCommandsL()[ i ].CommandUi().FolderInfo().Title() == iFolderTitle->Des() && + aName == ListVCommandsL()[ i ].CommandUi().WrittenText() ) + { + retval = i; + + // user edited command + // + if( ListVCommandsL()[ i ].SpokenText() != + ListVCommandsL()[ i ].CommandUi().WrittenText() ) + { + break; + } + } + } + return retval; + } + + +// End of File +