srsf/profileobserverplugin/src/vcommandprofileobserver.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:29:17 +0100
branchRCL_3
changeset 19 e36f3802f733
parent 0 bf1d17376201
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* 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 <aknlists.h>
#include <bautils.h> 

#include <ProEngFactory.h>
#include <MProEngProfileNameArray.h>

#include <vcprofileobserver.rsg>
#include <mmfcontrollerpluginresolver.h> // 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